---
# System prepended metadata

title: 06 Лекция Полная версия

---

# 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)