Модель памяти в языке Си
Модель памяти в языке Си — система хранения объектов в языке Си[1].
Способ хранения объекта в языке Си определяет его время жизни — часть времени выполнения программы, во время которого объект существует или для него зарезервировано место. Объект имеет постоянный адрес и сохраняет своё последнее значение. Запрещается обращаться к объекту, который перестал существовать, при этом, если при работе с объектом использовался указатель, его значение остаётся неопределённым.
Существует три способа хранения объектов[1]: автоматический, статический и динамический.
Свойство | Автоматический | Статический | Динамический |
---|---|---|---|
Объявление | Объект без связывания и без static | Имеет внутреннее или внешнее связывание или объявлен с квалификатором static | Выделен с помощью malloc |
Время существования | Блок, в котором объявлен объект | Всё время выполнения программы | От вызова malloc до вызова free |
Инициализация | Отсутствует в случае отсутствия явной инициализации | Происходит один раз до запуска программы | Частично в случае calloc |
Размер | Фиксированный, неизменяемый | Фиксированный, неизменяемый | Любой, изменяемый |
Типичное размещение | Стек или регистры процессора | Отдельный сегмент памяти | Куча |
Статический объект можно инициализировать явно, либо использовать умалчиваемую инициализацию.
При использовании функции calloc
все объекты имеют нулевое значение кроме чисел с плавающей запятой и указателей[2].
Выражения, не являющиеся lvalue, связанные с обращением к массиву, являющегося членом структуры (struct
) или объединения (union
) имеют время существования, ограниченное оценкой такого выражения[1].
Си-строки, которыми инициализируются указатели char*
, имеют статический тип хранения и не должны изменяться[3].
Динамическая память
Ни один объект не может находиться в динамической памяти без явного указания программиста. Для работы с динамической памятью существуют функции malloc
, calloc
, realloc
и free
. Поскольку функции, выделяющие память, принимают размер в переменной типа size_t
, максимальный объём выделяемой памяти ограничен SIZE_T_MAX
[1].
Функции malloc
и calloc
выделяют память, которая после использования должна быть освобождена с помощью вызова free
. После освобождения значение указателя остаётся неопределённым. Функция realloc
возвращает указатель на изменённый блок памяти, если запрос не может быть удовлетворён, размер блока памяти не изменяется[1].
#include <stdlib.h>
void foo (void **ptr, size_t size)
{
*ptr = realloc (*ptr, size+128); /* утечка памяти, если realloc вернёт NULL */
if (!*ptr)
{
...
}
}
При работе с динамической памятью возможны утечки памяти и ошибки двойного освобождения блока.
Пример
#include <stdlib.h>
#include <string.h>
static int x; /* 0 по умолчанию, существует всё время выполнения */
static int y=45; /* 45, существует всё время выполнения */
int cnt(void)
{
static int i=0;/*статический тип, инициализируется нулём только при запуске
программы, а не каждый вызов функции */
int j=-1;/*автоматический тип, инициализируется каждый раз
при вызове функции -1*/
i++;/* увеличивается на 1 в статической области памяти каждый запуск функции*/
j++;/* увеличивается на 1 локальная переменная */
return (i+j);/*при первом вызове с запуска программы функция вернёт 1,
при втором вызове 2, ... */
}
int main (void)
{
char arr[50] = "This is object of automatic storage duration";
/* имеет автоматический тип,существует до выхода из main,
начальные 45 элементов массива инициализированы элементами
строки с закрывающим нулём, остальные не определены */
char *line = "Simple line"; /*автоматический тип, существует до выхода
из main, line инициализирован указателем на константу */
int y; /* значение не определено, существует до выхода из main*/
int z=10; /* значение определено, существует до выхода из main*/
char *ptr; /* значение указателя не определено */
ptr = malloc (50); /* значение по указателю не определено,
объект по указателю существует до вызова free */
strcpy (ptr, arr);
free (ptr);
return 0;
}