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