Linux программирование в примерах | страница 59



делает рекурсивный вызов >manage_table(). Переменная >table снова может быть изменена совершенно незаметно! После возвращения из >other_routine() значение cur может снова стать недействительным.

Можно подумать (что мы вначале и сделали), что единственным решением является знать это и добавить после вызова функции переназначение >cur с соответствующим комментарием. Однако, Брайан Керниган (Brian Kernighan) любезно нас поправил. Если мы используем индексирование, проблема поддержки указателя даже не возникает:

>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(), поскольку в этом случае никогда не возникает вопросов по поводу вновь выделенной памяти.