Чтение онлайн

на главную - закладки

Жанры

Linux программирование в примерах
Шрифт:

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

void manage_table(void) {

 static struct table *table;

 struct table *cur, *p;

42

Он получен от реальной практики работы с

gawk
Примеч. автора.

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

Если вы знаете, что вам понадобится инициализированная нулями память, следует также использовать

calloc
, поскольку возможно, что память, возвращенная
malloc
, уже заполнена нулями. Хотя вы, программист, не можете этого знать,
calloc
может это знать и избежать лишнего вызова
memset
.

Поделиться:
Популярные книги

Лидер с планеты Земля

Тимофеев Владимир
2. Потерявшийся
Фантастика:
боевая фантастика
космическая фантастика
6.00
рейтинг книги
Лидер с планеты Земля

Законы Рода. Том 10

Мельник Андрей
10. Граф Берестьев
Фантастика:
юмористическая фантастика
аниме
фэнтези
5.00
рейтинг книги
Законы Рода. Том 10

Твое сердце будет разбито. Книга 1

Джейн Анна
Любовные романы:
современные любовные романы
5.50
рейтинг книги
Твое сердце будет разбито. Книга 1

Сирота

Ланцов Михаил Алексеевич
1. Помещик
Фантастика:
альтернативная история
5.71
рейтинг книги
Сирота

Проблемы роста

Meijin Q
Проза:
современная проза
повесть
5.00
рейтинг книги
Проблемы роста

Болотник 3

Панченко Андрей Алексеевич
3. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Болотник 3

Изгой Проклятого Клана. Том 5

Пламенев Владимир
5. Изгой
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Изгой Проклятого Клана. Том 5

Двойник Короля

Скабер Артемий
1. Двойник Короля
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Двойник Короля

Авиатор: назад в СССР

Дорин Михаил
1. Авиатор
Фантастика:
попаданцы
альтернативная история
5.25
рейтинг книги
Авиатор: назад в СССР

Вернувшийся: Первые шаги. Том II

Vector
2. Вернувшийся
Фантастика:
боевая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
Вернувшийся: Первые шаги. Том II

Государь

Кулаков Алексей Иванович
3. Рюрикова кровь
Фантастика:
мистика
альтернативная история
историческое фэнтези
6.25
рейтинг книги
Государь

Сердце Дракона. нейросеть в мире боевых искусств (главы 1-650)

Клеванский Кирилл Сергеевич
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.51
рейтинг книги
Сердце Дракона. нейросеть в мире боевых искусств (главы 1-650)

Кодекс Охотника. Книга ХХХ

Винокуров Юрий
30. Кодекс Охотника
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга ХХХ

Первый среди равных. Книга IV

Бор Жорж
4. Первый среди Равных
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Первый среди равных. Книга IV