Роббинс Арнольд
Шрифт:
3.2.1.1. Исследование подробностей на языке С
Вот объявления функций из темы справки GNU/Linux malloc(3):
#include <stdlib.h> /* ISO С */
void *calloc(size_t nmemb, size_t size);
/* Выделить и инициализировать нулями */
void *malloc(size_t size);
/* Выделить без инициализации */
void free(void *ptr);
/* Освободить память */
void *realloc(void *ptr, size_t size);
/* Изменить размер выделенной памяти */
Функции выделения памяти возвращают тип
void*
. Это бестиповый или общий указатель, все, что с ним можно делать — это привести его к другому типу и назначить типизированному указателю. Примеры впереди. Тип
size_t
является беззнаковым целым типом, который представляет размер памяти. Он используется для динамического выделения памяти, и далее в книге мы увидим множество примеров его использования. На большинстве современных систем size
_t является unsigned long
, но лучше явно использовать size_t
вместо простого целого типа unsigned
. Тип
ptrdiff_t
используется для вычисления адреса в арифметике указателей, как в случае вычисления указателя в массиве: #define MAXBUF ...
char *p;
char buf[MAXBUF];
ptrdiff_t where;
p = buf;
while (/* некоторое условие */) {
...
p += something;
...
where = p - buf; /* какой у нас индекс? */
}
Заголовочный файл
<stdlib.h>
объявляет множество стандартных библиотечных функций С и типов (таких, как size_t
), он определяет также константу препроцессора NULL
, которая представляет «нуль» или недействительный указатель. (Это нулевое значение, такое, как 0 или '((void*)0)
'. Явное использование 0 относится к стилю С++; в С, однако, NULL
является предпочтительным, мы находим его гораздо более читабельным для кода С.) 3.2.1.2. Начальное выделение памяти:
malloc
Сначала память выделяется с помощью
malloc
. Передаваемое функции значение является общим числом затребованных байтов. Возвращаемое значение является указателем на вновь выделенную область памяти или NULL
, если память выделить невозможно. В последнем случае для обозначения ошибки будет установлен errno
. (errno является специальной переменной, которую системные вызовы и библиотечные функции устанавливают для указания произошедшей ошибки. Она описывается в разделе 4.3 «Определение ошибок».) Например, предположим, что мы хотим выделить переменное число некоторых структур. Код выглядит примерно так: struct coord { /* 3D координаты */
int x, y, z;
} *coordinates;
unsigned int count; /* сколько нам нужно */
size_t amount; /* общий размер памяти */
/* ... как-нибудь определить нужное число... */
amount = count * sizeof(struct coord); /* сколько байт выделить */
coordinates = (struct coord*)malloc(amount); /* выделить память */
if (coordinates == NULL) {
/* сообщить об ошибке, восстановить или прервать */
}
/* ... использовать координаты... */
Представленные здесь шаги являются стереотипными. Порядок следующий:
1. Объявить указатель соответствующего типа для выделенной памяти.
2. Вычислить размер выделяемой памяти в байтах. Для этого нужно умножить число нужных объектов на размер каждого из них. Последний получается с помощью оператора С
sizeof
, который для этой цели и существует (наряду с другими). Таким образом, хотя размер определенной структуры среди различных компиляторов и архитектур может различаться, sizeof
всегда возвращает верное значение, а исходный код остается правильным и переносимым. При выделении массивов для строк символов или других данных типа
char
нет необходимости умножения на sizeof(char)
, поскольку последнее по определению всегда равно 1. Но в любом случае это не повредит. 3. Выделить память с помощью
malloc
, присвоив возвращаемое функцией значение переменной указателя. Хорошей практикой является приведение возвращаемого malloc
значения к типу переменной, которой это значение присваивается. В С этого не требуется (хотя компилятор может выдать предупреждение). Мы настоятельно рекомендуем всегда приводить возвращаемое значение.