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



возвращает >NULL, вы теряете указатель на первоначальную память. Эту память больше нельзя использовать. Что еще важнее, эту память невозможно освободить! Это создает утечку памяти, которую нужно избежать.

Для версии >realloc() в стандартном С есть некоторые особые случаи: когда аргумент >ptr равен >NULL, >realloc() действует подобно >malloc() и выделяет свежий блок памяти. Когда аргумент >size равен 0, >realloc() действует подобно >free() и освобождает память, на которую указывает >ptr. Поскольку (а) это может сбивать с толку и (б) более старые системы не реализуют эту возможность, мы рекомендуем использовать >malloc(), когда вы имеете в виду >malloc(), и >free(), когда вы имеете в виду >free().

Вот другой довольно тонкий момент[42]. Рассмотрим процедуру, которая содержит статический указатель на динамически выделяемые данные, которые время от времени должны расти. Процедура может содержать также автоматические (т.е. локальные) указатели на эти данные. (Для краткости, мы опустим проверки ошибок. В коде продукта не делайте этого.) Например:

>void manage_table(void) {

> static struct table *table;

> struct table *cur, *p;

> int i;

> size_t count;

> ...

> table =

>  (struct table*)malloc(count * sizeof(struct table));

> /* заполнить таблицу */

> cur = &table[i]; /* указатель на 1-й элемент */

> ...

> cur->i = j; /* использование указателя */

> ...

> if (/* некоторое условие */) {

>  /* нужно увеличить таблицу */

>  count += count/2;

>  p =

>  (struct table*)realloc(table, count * sizeof(struct table));

>  table = p;

> }

> cur->i = j; /* ПРОБЛЕМА 1: обновление элемента таблицы */

> other_routine(); /* ПРОБЛЕМА 2: см. текст */

> cur->j = k; /* ПРОБЛЕМА 2: см. текст */

> ...

>}

Это выглядит просто; >manage_table() размешает данные, использует их, изменяет размер и т.д. Но есть кое-какие проблемы, которые не выходят за рамки страницы (или экрана), когда вы смотрите на этот код.

В строке, помеченной '>ПРОБЛЕМА 1', указатель cur используется для обновления элемента таблицы. Однако, >cur был инициализирован начальным значением >table. Если некоторое условие верно и >realloc() вернула другой блок памяти, >cur теперь указывает на первоначальный, освобожденный участок памяти! Каждый раз, когда >table меняется, нужно обновить также все указатели на этот участок памяти. Здесь после вызова >realloc() и переназначения >table недостает строки '>cur = &table[i];'.

Две строки, помеченные '>ПРОБЛЕМА 2', еще более тонкие. В частности, предположим, что >other_routine()