', еще более тонкие. В частности, предположим, что
other_routine
делает рекурсивный вызов
manage_table
. Переменная
table
снова может быть изменена совершенно незаметно! После возвращения из
other_routine
значение cur может снова стать недействительным.
Можно подумать (что мы вначале и сделали), что единственным решением является знать это и добавить после вызова функции переназначение
cur
с соответствующим комментарием. Однако, Брайан Керниган (Brian Kernighan) любезно нас поправил. Если мы используем индексирование, проблема поддержки указателя даже не возникает:
table[i].j = k; /* ПРОБЛЕМА 2 также устраняется */
Использование индексирования не решает проблему, если вы используете глобальную копию первоначального указателя на выделенные данные; в этом случае, вам все равно нужно побеспокоиться об обновлении своих глобальных структур после вызова
realloc
.
ЗАМЕЧАНИЕ. Как и в случае с
malloc
, когда вы увеличиваете размер памяти, вновь выделенная после
realloc
память не инициализируется нулями. Вы сами при необходимости должны очистить память с помощью
memset
, поскольку
realloc
лишь выделяет новую память и больше ничего не делает.
3.2.1.5. Выделение с инициализацией нулями:
calloc
Функция
calloc
является простой оболочкой вокруг
malloc
. Главным ее преимуществом является то, что она обнуляет динамически выделенную память. Она также вычисляет за вас размер памяти, принимая в качестве параметра число элементов и размер каждого элемента:
довольно простой. Вот одна из возможных реализаций:
void *calloc(size_t nmemb, size_t size) {
void *p;
size_t total;
total = nmemb * size; /* Вычислить размер */
p = malloc(total); /* Выделить память */
if (p != NULL) /* Если это сработало - */
memset(p, '\0', total); /* Заполнить ее нулями */
return p; /* Возвращаемое значение NULL или указатель */
}
Многие опытные программисты предпочитают использовать
calloc
, поскольку в этом случае никогда не возникает вопросов по поводу вновь выделенной памяти.
Если вы знаете, что вам понадобится инициализированная нулями память, следует также использовать
calloc
, поскольку возможно, что память, возвращенная
malloc
, уже заполнена нулями. Хотя вы, программист,
не можете этого знать,
calloc
может это знать и избежать лишнего вызова
memset
.
3.2.1.6. Подведение итогов из GNU Coding Standards
Чтобы подвести итоги, процитируем, что говорит об использовании процедур выделения памяти GNU Coding Standards:
Проверяйте каждый вызов
malloc
или
realloc
на предмет возвращенного нуля. Проверяйте
realloc
даже в том случае, если вы уменьшаете размер блока; в системе, которая округляет размеры блока до степени двойки,
realloc
может получить другой блок, если вы запрашиваете меньше памяти.
В Unix
realloc
может разрушить блок памяти, если она возвращает ноль. GNU
realloc
не содержит подобной ошибки: если она завершается неудачей, исходный блок остается без изменений. Считайте, что ошибка устранена. Если вы хотите запустить свою программу на Unix и хотите избежать потерь в этом случае, вы можете использовать GNU
malloc
.
Вы должны считать, что
free
изменяет содержимое освобожденного блока. Все, что вы хотите получить из блока, вы должны получать до вызова
free
.
В этих трех коротких абзацах Ричард Столмен (Richard Stallman) выразил суть важных принципов управления динамической памятью с помощью
malloc
. Именно использование динамической памяти и принцип «никаких произвольных ограничений» делают программы GNU такими устойчивыми и более работоспособными по сравнению с их Unix-двойниками.
Мы хотим подчеркнуть, что стандарт С требует, чтобы
realloc
не разрушал оригинальный блок памяти, если она возвращает
NULL
.
3.2.1.7. Использование персональных программ распределения
Набор функций с
malloc
является набором общего назначения по выделению памяти. Он должен быть способен обработать запросы на произвольно большие или маленькие размеры памяти и осуществлять все необходимые учетные действия при освобождении различных участков выделенной памяти. Если ваша программа выделяет значительную динамическую память, вы можете обнаружить, что она тратит большую часть своего времени в функциях
malloc
.
Вы можете написать персональную программу распределения — набор функций или макросов, которые выделяют большие участки памяти с помощью
malloc
, а затем дробят их на маленькие кусочки по одному за раз. Эта методика особенно полезна, если вы выделяете множество отдельных экземпляров одной и той же сравнительно небольшой структуры.
Например, GNU awk (gawk) использует эту методику. Выдержка из файла
awk.h
в дистрибутиве
gawk
(слегка отредактировано, чтобы уместилось на странице):