# 10 Лекция Полная версия [TOC] ## Структуры ### Как отключить выравнивание Зачем - например, для работы с файлами можно описать структуру чтобы её внутреннее устройство соответствовало один в один расположению данных в файлике. Читать так файли можно не поэлементно а одной операйией чтения. (Расположение данных должно точно совпадать) (Быстрее обычного способа) В таких случаях вырвнивание можно отключить с помощью флагов компиляции (компиляторозависимо) (код может начать работать существенно медленее) Для VS (Microsoft) ```c #programa pack (push, 1) //без выравнивания // (Пушим старое выравнивание на стек) //code - описываем структуры // которое хотим чтобы плотно упаковывались #programa pack (pop) //выравнивание структур на дефолтное ``` Для Clang/GCC ```c struct A __attribute__ ((packed)) { //code }; ``` ## Про размер в си Размер какого-то обьекта - типа, переменной, массива в C/C++ можно узнать с помощью **оператора** `sizeof`, например: (в байтах) ```c int a; sizeof a == 4; sizeof(unsigned int) == 4; int arr[10]; sizeof(arr) == 40; ``` Скобки можно иногда не писать, это не функция! Полученное число - это размер **типа выражения** в скобках в **байтах**! --- К слову: ```c struct A { int x; double y; char z[4]; } a[10]; struct B { double y; int x; char z[4]; } b[10]; struct C { int x[10]; double y[10]; char z[40]; } c; sizeof(a) == 240; //байт sizeof(b) == 160; //байт sizeof(c) == 160; //байт ``` --- Тип выражения - `typeof`, есть в некоторых компиляторах, предлагают добавить в стандарт C23. По сути `typeof` уже существует, тк до того как посчитать размер в памяти нужно почитать тип выражения. --- ## Union Синтаксически - тоже самое, что и структура **Разница только в одном** - в том, как он **хранится в памяти**. ![](https://i.imgur.com/I9MWHA4.png) ![](https://i.imgur.com/MSEpdjQ.png) Это структура в которой всё элементы побайтово лежат поверх друг друга. Размер union - sizeof максимального элемента. Может использоваться для создания переменных с разными типами. (обычно вместе со структорой, которая хранит активный тип) (ещё можно прочитать битики floata, напрмер)) Все элементы начинаются с адреса 0, массив считается одним элементом, структура тоже будет считаться одним элементом, со всеми своими дырками Нужны не очень часто, но там где их нет, но они нужны - уровень извращений зашкаливает, и он будет гораздо выше чем здесь. ## Указатели В интернете есть много картинок про сравнение перехода с C на Python (например, и наоборот). Во многом легкость изучения других языков после C/С++ обуславливается тем, что здесь приходится вручную управлять памятью (=> существуют указатели). Когда говорят что на Java можно писать эффективный код - имею ввиду, что для этого программист должен обладать высшей калификацией, так как чтобы писать эффектывный код нужно понимать что язык прячет от вас и как он это длает, C, меньше С++ прячут значительно меньше чем Java => присать быстрый код на них проще (несмотря на то, что писать просто код на них сложнее) В последнее время почему-то чем быстрее удаётся накодить программу - тем лучше (лучше выпустить на неделю раньше, чем исправить пару багов), такой подход как раз и провоцирует использование простых, однако неэффективных языков, например Python, Java **Указатели** - идейно они довольно похожи на ярлыки в windows, если создать кучу ярлыков и один удалить, данные никак не поменяются. Ярлыки не содержат данные, только информацию как эти данные найти. На си тоже самое, только указатели строго типизированны, в указателе зашито то, на что он указывает. Указатель на `int` может указывать только на `int`. **Сами обьекты не содержат информации о типе обьекта**, `int` - это просто 4 байта в памяти. Информация о типе есть только в момент компиляции. По этому в C/C++ в момент компиляции указатель знает информация о типе. **Занимают в памяти столько, какая разрядность программы** (32 или 64 бита) Система обычно позволяет запускать меньшей и равной битности программы (иногда строго равной) ```c int x; // int int *p, a; // указатель на инт, и обычный инт ``` Есть религиозный момент, где ставить `*` рядом с `int` или рядом с именем переменной. Тк при создании переменной указатель относиться только к этой пеменной, а не типу, то следует её ставить рядом с переменной. `*` может относиться к типу, но это редкость (в основном при создании указателя на функцию) От того, где в C/C++ стоят пробелы - ничего не меняется! ```c p = &x; // взять адрес в памяти переменной x //и присвоить указателю p // (унарный контекст &) ``` Указатель - чтобы работать с данными, другой смысл `*`: (унарный контекс) ```c int y = *p; // обращение сквозь указатель //(разьименовка указателя), y == x //чтение сквозь указатель *p = 2; // x == 2 //запись сквозб указатель int **q=&p; // указатель на указатель **q = 3; // x == 3, *q на p, **q на x ``` Тип указателя позволяет их коректно разименовывать В си ссылок нет! (`int &a;`) (есть в си++) ## Про память Обычные переменные, аргументы функции и т.д. создаются **на стеке** (храняться на стеке)\ Выделение памяти - проиходит **на куче**. * стек - +- 2 мегабайта на процесс * куча - сколько есть операт пам Если привысить стек - ***программа упадет***, возможно молча, тк для вывода ошибки тоже нужно место на стеке. ### Глобальные переменные Опишем переменную вне функции: ```c int g = 2; int f(int a) { return a; } int main(void) { f(8); return 0; } ``` `g` будет глобальной переменной. Такие переменные глобальные: * **Одни на всю программу всегда** * **Видно из всех функций** * **Время жизни - глобальное** * **По умолчанию - видимость только в файле** + обьявить в другом файле (строго без иниц): + `int g;` - C + `extern int g;` - C++, + `::g` - обратиться в С++ (когда имя скрыто локальной переменной) * Создается до запуска main * Уничтожается после окончания main * Создается в секции данных (на куче) * Размер должен быть известен в момент компиляции Минусы глобальных переменных: * Ломают многопоточность * рекурсию * очень сложно отлаживать (могут измениться в неожиданном месте) > Глобальные переменные лучше не использовать без **Острой** необходимости > Константы **рекомендуется** размещать в глобальной области `static` по глобальной переменной **ограничивает видимость** текущим файлом. ### Static локальные переменные Выглядит как локальная: * видимость - локальная * время жизни - глобальное ```c int f(void) { static int a = 5; // иниц будет выполненно один раз первый a++; // будет выполненно Каждый раз return a; // будет выполненно Каждый раз } int main(void) { //здесь a не видно f(); // == 6 f(); // == 7 f(); // == 8 return 0; } ``` Тоже самое, что и глобальная переменная. Из минусов осталось: * Ломает многопоточность