# 11 Лекция Полная версия
[TOC]
## Про память
### Секции
Три секции:
1) секция кода - код
2) секция данных - глобальные переменные (`static` локальные тоже)
3) секция данных на чтение - глобальные константы
---
4) Stack - локальные переменные
5) Free RAM (Heap) - куда происходит выделение памяти (обычно называют кучей)
И всё это лежит в оперативной памяти.

[ПОДРОБНЕЕ](https://stackoverflow.com/questions/53942282/where-will-the-initialized-data-segment-values-are-stored-before-run-time)
[ПОДРОБНЕЕ 2](https://www.geeksforgeeks.org/memory-layout-of-c-program)
### Выделение памяти
Для выделения памяти нам и нужны указатели:
```c=
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
int w = 1000;
//int a[1000]; - создание обычного статического массива
// так не надо при больших количествах информации
// тк локальные масивы храняться на стеке (+- 2мб на программу)
int *a = malloc(sizeof(int) * w); //создание массива с выделением памяти
if(a == NULL) // проверять рекомендуется при выделении большого размера
{
printf("Memory was not allocated!\n");
return 1;
}
a[999]; // доступ такой же
free(a); // освободить память
// функции free можно отдавать NULL
}
```
`malloc`, возвращает `void *`, принимает количество байт которые нужно *попросить* выделить систему
Религиозный вопрос про проверку. Проверять рекомендуется при выделении большого размера в памяти, тк при выделении маленького - есть огромная веротность что уже всё остальное тоже сломалось.
В си **НЕ** нужно кастовать указатель из `void *` к `int *` (*в ++ нужно, по религиозным причинам*)
Освобождать память нужно точно **таким же способом** (семейство функций), каким и выделили (на будущее). Так проиходит из-за того, что у разных функций выделения - разные вспомогательные данные, из-за этого ещё если мы попросим выделить 0 байт, то место в оперативной памяти такая штука займет больше 0.
> В системе существует много разных способов (функций) выделить память, для разных нужд:
> * просто выделение
> * выделить очень много
> * чтобы была возможность отдать другому процессу
> * и т.д.
#### Доступ
Доступ к выделенной памяти такой же как и у одномерных массивов, тк синтаксис `[]` это ни что иное, как разьменование и перемещение указателя. Обычные одномерные массивы почти тоже самое, что указатель, за исключением того, что:
1) указатель не знает рамер массива
2) массив нельзя переопределить (указатель можно заставить указывать на другое место в памяти)
3) нельзя создать указатель на массив, он будет тем же самым указателем
### Передача массива в функцию
```c
int sum(int x[3]) //или int x[] -> (int *x) автоматически будет преобразованно
{
return x[0] + x[1] + x[2];
//return (*(x + 0)) + (*(x + 1)) + (*(x + 2));
}
int main(void)
{
int a[3] = {1, 2}; //1, 2, 0
return sum(a);
}
```
Массивы в аргументах функций **никогда** не бывают массивами, они автоматически преобразуются до указателя. (альтернативная запись указателя - `int x[]`)
=> массивы никогда не **копируются** в функцию, любое изменение которые мы сдлаем с массивом внутри функции будет видно там, где этот массив создавали.
### Разница массива массивов и двумерного массива
1) **Массив массивов** (`int **m;`)
* хранится массив указатей на строки, а затем разбросанно по памяти строки
* строки могут быть разного размера
* два обращения в память (медленнее)
* строки могут поменены местами за O(1) (очень быстро)

2) **Двумерный массив** (`int m[h][w];`)
* храниться в памяти подряд
* строки единого размера
* одно обращение в память (быстрее)
* строки можно поменять местами минимум за O(w) (довольно медленно)

Разницу нужно понимать, тк обращение к этим двум обьектам выглядит идентично - `m[i][j]`
> В двумерном массиве нужный указатель вычисляется с помощью умножение, фактически, двумерный массив - это одномерный массив
### Массив массивов
Массив массивов может выделяться 3мя разными способами:
---
1) 
---
2) 
---
3) 
---