Linux программирование в примерах
table =(struct table*)malloc(count * sizeof(struct table));.../* заполнить таблицу */...table[i].i = j; /* Обновить член i-го элемента */...if (/* некоторое условие */) {/* нужно увеличить таблицу */count += count/2;p =(struct table*)realloc(table, count * sizeof(struct table));table = p;}table[i].i = j; /* ПРОБЛЕМА 1 устраняется */other_routine();/* Рекурсивный вызов, модифицирует таблицу */table[i].j = k; /* ПРОБЛЕМА 2 также устраняется */Использование индексирования не решает проблему, если вы используете глобальную копию первоначального указателя на выделенные данные; в этом случае, вам все равно нужно побеспокоиться об обновлении своих глобальных структур после вызова
.realloc()ЗАМЕЧАНИЕ. Как и в случае с
, когда вы увеличиваете размер памяти, вновь выделенная послеmalloc()память не инициализируется нулями. Вы сами при необходимости должны очистить память с помощьюrealloc(), посколькуmemset()лишь выделяет новую память и больше ничего не делает.realloc()3.2.1.5. Выделение с инициализацией нулями:
calloc()Функция
является простой оболочкой вокругcalloc(). Главным ее преимуществом является то, что она обнуляет динамически выделенную память. Она также вычисляет за вас размер памяти, принимая в качестве параметра число элементов и размер каждого элемента:malloc()coordinates = (struct coord*)calloc(count, sizeof(struct coord));По крайней мере идейно, код
довольно простой. Вот одна из возможных реализаций:calloc()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
может разрушить блок памяти, если она возвращает ноль. GNUreallocне содержит подобной ошибки: если она завершается неудачей, исходный блок остается без изменений. Считайте, что ошибка устранена. Если вы хотите запустить свою программу на Unix и хотите избежать потерь в этом случае, вы можете использовать GNUrealloc.mallocВы должны считать, что
изменяет содержимое освобожденного блока. Все, что вы хотите получить из блока, вы должны получать до вызоваfree.freeВ этих трех коротких абзацах Ричард Столмен (Richard Stallman) выразил суть важных принципов управления динамической памятью с помощью
. Именно использование динамической памяти и принцип «никаких произвольных ограничений» делают программы GNU такими устойчивыми и более работоспособными по сравнению с их Unix-двойниками.malloc()Мы хотим подчеркнуть, что стандарт С требует, чтобы
не разрушал оригинальный блок памяти, если она возвращаетrealloc().NULL3.2.1.7. Использование персональных программ распределения
Набор функций с
является набором общего назначения по выделению памяти. Он должен быть способен обработать запросы на произвольно большие или маленькие размеры памяти и осуществлять все необходимые учетные действия при освобождении различных участков выделенной памяти. Если ваша программа выделяет значительную динамическую память, вы можете обнаружить, что она тратит большую часть своего времени в функцияхmalloc().malloc()Вы можете написать персональную программу распределения — набор функций или макросов, которые выделяют большие участки памяти с помощью
, а затем дробят их на маленькие кусочки по одному за раз. Эта методика особенно полезна, если вы выделяете множество отдельных экземпляров одной и той же сравнительно небольшой структуры.malloc()Например, GNU awk (gawk) использует эту методику. Выдержка из файла
в дистрибутивеawk.h(слегка отредактировано, чтобы уместилось на странице):gawk#define getnode(n) if (nextfree) n = nextfree, \nextfree = nextfree->nextp; else n = more_nodes()#define freenode(n) ((n)->flags = 0, (n)->exec_count = 0,\(n)->nextp = nextfree, nextfree = (n))Переменная
указывает на связанный список структур NODE. Макросnextfreeубирает из списка первую структуру, если она там есть. В противном случае она вызываетgetnode(), чтобы выделить новый список свободных структурmore_nodes(). МакросNODEосвобождает структуруfreenode(), помещая его в начало списка.NODE