# 06 Лекция Полная версия
[TOC]
## Грабли
Кроме деления на 0 есть ещё одни грабли, `Int.MIN / -1` - это [Undefined Behavior](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%91%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5). Происходит, тк диапазон значений (в дополнение до 2х) больше на 1 в отрицательных значениях.\
`-Int.MIN == Int.MIN`
**Модуль int**
```c
if (a < 0)
a = -a; // a может быть Int.MIN
```
(В стандарте знаковое переполнение не определенно! `a = -a;` - эквивалент инверт биты + 1)
Диапазон (8 бит) `-128 до 127`
```c
-128 == 1000 0000
//инверт биты
0111 1111
// + 1
1000 0000 == -128
```
```c
0 == 0000 0000
//инверт биты
1111 1111
// + 1
0000 0000 == 0
```
```c
-127 == 1000 0001
//инверт биты
0111 1110
// + 1
0111 1111 == 127
```
**Правильно**
```c
int a;
unsigned int b;
if (a < 0)
{
b = a;
b = -b; //эквивалент инверт биты и +1,
//только без Undefined Behavior
//(случался из-за переполнения)
}
else
{
b = a;
}
```
```c
-128 == 1000 0000 //в форме доп до 2х
//инверт биты
0111 1111
// + 1
1000 0000 == 128 //напрямую
```
## Ввод-вывод
Кодовая страница - обычно один байт, от 0 до 128 - ASCII, вторая половина (-128 до 0 или 128 до 255) - набор символов по текущей кодовой странице (обычно второй язык) (cp-866/cp-1256/ - русские, cp-65001 - UTF-8)
`\n` - символ перевода строки\
`\t` - символ табуляции\
начинаюстся с`\` - спецсимволы строк (`char *`) си, который используется для спец символов вроде перевода строки, чтобы написать просто символ обратного слеша - `\\`
Если писать пути в коде программы - нужно удваивать `\\`
Путь к файлу `C:\Users\My\Desktop` - выдаст ошибку при компиляции (непонятно что такое `\U`, `\M` и `\D`)
Путь к файлу на си `C:\\Users\\My\\Desktop`
либо `C:/Users/My/Desktop`
```c=
// char path[] = "C:\the_programm\no_response.exe"; //НЕправильно
// не выдаст ошибку при компиляции
// тк \t - символ табуляции, \n - символ перевода строки
char path[] = "C:\\the_programm\\no_response.exe"; // правильно
char path[] = "C:/the_programm/no_response.exe"; // или так
```
> Пути можно писать через `/`, и их НЕ нужно удваивать, это не спец. символ. (в линуксе только `/` в путях), рекомендуется так писать в `include`
> `\` спец символ везде, не только в `printf/scanf`, если поставить `\` перед концом строки - значит перевода строки не существует
> `printf("a = ", a);` - что выведется на экран?
>
> -выведется `"a = "`
Чтобы вывести значение какой-либо перемменной через `printf` есть специальный спецсимвол:
`%` - спецсимвол `printf` (есть и другие спец. символы чтобы печатать)
> если `\` спецсимвол во всех строках в си, то `%` спец символ только в семействе функций `scanf/printf`
Чтобы напечать символ `\` нужно его удвоить - `printf("\\");`\
Чтобы напечать символ `%` нужно его удвоить - `printf("%%");`
> В чем различие с `std::cin` и `std::cout`?
>
> 1) если случается что-то сложное, вывести это через плюсовые функции будет гораздо сложнее, то есть порог входа в плюсовые ниже, но если освоить сишные - будет потом гораздо проще
> 2) Плюсовые функции ооочень много жрут времени, функции вывода и так очень медлянные, а под плюсовыми скрывается ещё больший ужас
`cin`, `cout` самые медленные\
`scanf`, `printf` сильно быстрее\
`ios_base cin.tie(0); cout.tie(0);` ещё быстрее\
двоичный ввод/вывод - самое быстрое
В `printf/scanf` четко указывается тип значения/переменной которая будет печататься/считываться (через `%`), если написать переменную другого типа - она будет интерпретированна как переменная типа которого вы указали через `%`.
```c
int a = 5, b = 4, c;
c = a + b;
printf("a + b = %i", c); //выводит переменную типа int
```
> Чем `%i` и `%d` отличается от друг от друга?
>
> В `printf` - ничем, выводит `int`, в `scanf` `%i` может считать ещё значения в 8/16-ричной системе счисления, надо поставить `0` или `0x` перед числом соответственно (`%d` - считывает только десятичные числа) (`0123` - восмиричное число `83`)
Сишные функции - это функции с переменном числом аргументов, так называемые `vararg` функции.
`scanf` возвращает кол-во прочитанных переменных (прочитанных `%`).\
`printf` возвращает кол-во фактически напечатанных **символов**.\
К вопросу о том, как проверить считалось/напечаталось ли что-то на самом деле.
`scanf` пропускает пробелы при считывании (всё что считается пробельным символов в си - `' ','\n', '\t'`)
## Типы данных с плавующей точкой:
[IEEE 754-2008](https://ru.wikipedia.org/wiki/IEEE_754-2008) - как представляются числа с плавающей точкой в современном компьютере (=> в C/C++)\
[Вольный пересказ](https://www.softelectro.ru/ieee754.html)\
(стандарт IEEE-754 (не стандарт языка, стандарт железа, некоторые моменты перенял си))
Их всего 3 типа
1. `float` = (32 бита) соответствует single precision IEEE 754-2008 10^(+- 38)
2. `double` = (64 бита) соответствует double precision IEEE 754-2008 10^(+- 308)
3. `long double`: (не договорились, про совместимость между компиляторами можно не думать)
* может совпадать с `double`
* может быть (128 бит) quad precision IEEE 754-2008, есть GCC, в x86 реализованно программно (очень медленно)
---
* может быть (80 бит) extended precision (не входит в стандарт IEEE 754-2008, аппаратно реализован в x86, (быстрее любой программной реализации))
Диапазон в 10^(+- 308) НЕ означает что будут храниться 308 десятичных знаков - означает, что максимальное значение по экспоненте будет 308 и оно будет неточным (будут храниться первые цифры)
> Обычно мы не хотим использовать `long double`
`double` как минимум вдвое медленнее чем `float` (может быть сильно медленнее)
## Константы
Целые константы реализуются как `int`, то есть `10` будет константной типа `int` (можно подправить тип написав `10u` или `10llu`, тогда она будет `unsigned int` и `unsigned long long` соответсвенно)
```c
long long a = 1000000 * 1000000; //получиться черти что, вообще UB
//тк переполнение не определенно для signed
long long b = 1000000ll * 1000000ll; //приведеться в long long и правильно посчитается
```
Переполнение знакового (signed) int - [Undefined Behavior](https://https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%91%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5)
Дробные константы реализуются как `double`, то есть `10.9` будет константной типа `double` (можно подправить тип написав `10.9f` тогда она будет `float`)
Си **всю** арфметику считает в **одинаковых** типах! Нельзя к int прибавить unsigned. В случае, когда типы разные - копилятор их приводит к одному, например в случае `int + unsigned int` победит `unsigned int`
`float a = 5.7f; a += 5.6;` В этом случае `a` сначала сконвертируется в `double` сложится с константой и обратно сконвертируется в `float`.
Можно создать 16ричные и 8ричные костанты в тексте, 16ричные константы с плавающей точкой. (`0x43534P234` - 16ричное с точкой, после p экспанента)
## Ввод/Вывод
Форматы ввода семейства функций `printf/scanf`:
* `%i` - десятичные, также 16/8ричные с префиксом (0x/0) (int)
* `%u` - десятичные беззнаковые (unsigned int)
* `%x` - HEX (16рич), без префикса
Форматы вывода немного другие, в часности `%i` и `%d` с `printf` работают одинакого, выводят десятичное число, чтобы вывести число в HEX надо использовать `%x`
---
Дробные:
* float - `%f`
* double - `%lf` (сокр long float)
Однако, если мы перпутаем в `printf` - `%f` и `%lf` (скормим в `%f` double или в `%lf` float) то всё отработает хорошо, тк `printf` - `vararg` функция, а `vararg` автоматически расширяет `float` до `double`. Это только в `printf`. (типы которые короче int (например char) автоматически расширяются до int)