Динамическая работа с памятью
Конечно, многие алгоритмы можно описать с помощью уже имеющихся в нашем арсенале средств... Но если мы говорим про реальные сервисы, то они редко имеют такое обидное ограничение, как количество элементов, которыми мы можем пользоваться.
До этого мы выделяли жестко фиксированные объёмы памяти. Определяли их прямо на моменте компиляции:
int a;
char b[10];
struct Point2d = { -1, 1 };
– в каждом из перечисленных случаем компилятор знает, сколько нужно выделить
памяти под наши переменные. Более того, зная, что область видимости переменной
– блок {}
, компилятор также знает, когда память можно освободить.
Он может делать это автоматически.
Модель памяти Си
Модель памяти – по сути набор правил: как создавать переменные, где они доступны, сколько под них выделять и когда удалять.
Рассмотрим 3 типа переменных, три подхода к работе с памятью:
Автоматический тип | Статический тип | Динамический тип | |
---|---|---|---|
Объявление | Объект без связывания и static | Имеет связывание или объявлен как static | Выделен с помощью *alloc |
Время жизни | Блок, в котором объявлена переменная | Время работы программы | От *alloc до free |
Инициализация | Отсутствует | Один раз до запуска программы | Частично в случае calloc |
Размер | Фиксированный, неизменяемый | Фиксированный, неизменяемый | Любой, изменяемый |
Те переменные, которые мы использовали до этого – очевидно, имеют автоматический тип памяти.
Теперь рассмотрим пример статического типа данных. Эти переменные создаются при старте программы и живут до конца работы программы. Это их главное отличие:
#include <stdio.h>
void foo() {
int a = 10;
static int sa = 10;
a += 5;
sa += 5;
printf("a = %d, sa = %d\n", a, sa);
}
int main() {
int i;
for (i = 0; i < 10; ++i) {
foo();
}
}
– можно заметить, что sa
инициализирована 1 раз, после чего уже использовалась
повторно:
a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60