Сначала память выделяется с помощью 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()
значения к типу переменной, которой это значение присваивается. В С этого не требуется (хотя компилятор может выдать предупреждение). Мы настоятельно рекомендуем всегда приводить возвращаемое значение.
Обратите внимание, что на C++ присвоение знамения указателя одного типа указателю другого типа требует приведения типов, какой бы ни был контекст. Для управления динамической памятью программы C++ должны использовать new
и delete
, а не malloc()
и free()
, чтобы избежать проблем с типами.
4. Проверить возвращенное значение. malloc()
возвращает NULL
. Если вы используете значение без проверки, ваша программа может быть немедленно завершена из-за
Если вы проверите возвращенное значение, вы можете по крайней мере выдать диагностическое сообщение и корректно завершить программу. Или можете попытаться использовать какой-нибудь другой способ восстановления.
Выделив блок памяти и установив в coordinates
указатель на него, мы можем затем интерпретировать coordinates
как массив, хотя он в действительности указатель:
int cur_x, cur_y, cur_z;
size_t an_index;
an_index = something;
cur_x = coordinates[an_index].x;
cur_y = coordinates[an_index].y;
cur_z = coordinates[an_index].z;
Компилятор создает корректный код для индексирования через указатель при получении доступа к членам структуры coordinates[an_index]
.