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

на главную

Жанры

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
.

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

Live-rpg. эволюция-3

Кронос Александр
3. Эволюция. Live-RPG
Фантастика:
боевая фантастика
6.59
рейтинг книги
Live-rpg. эволюция-3

Пушкарь. Пенталогия

Корчевский Юрий Григорьевич
Фантастика:
альтернативная история
8.11
рейтинг книги
Пушкарь. Пенталогия

Хроники разрушителя миров. Книга 8

Ермоленков Алексей
8. Хроники разрушителя миров
Фантастика:
фэнтези
5.00
рейтинг книги
Хроники разрушителя миров. Книга 8

Адъютант

Демиров Леонид
2. Мания крафта
Фантастика:
фэнтези
6.43
рейтинг книги
Адъютант

Эфир. Терра 13. #2

Скабер Артемий
2. Совет Видящих
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эфир. Терра 13. #2

Кодекс Крови. Книга VI

Борзых М.
6. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга VI

Измена

Рей Полина
Любовные романы:
современные любовные романы
5.38
рейтинг книги
Измена

Я – Орк. Том 2

Лисицин Евгений
2. Я — Орк
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я – Орк. Том 2

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

Flow Ascold
2. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 2

Корпулентные достоинства, или Знатный переполох. Дилогия

Цвик Катерина Александровна
Фантастика:
юмористическая фантастика
7.53
рейтинг книги
Корпулентные достоинства, или Знатный переполох. Дилогия

Тринадцатый

NikL
1. Видящий смерть
Фантастика:
фэнтези
попаданцы
аниме
6.80
рейтинг книги
Тринадцатый

Темный Патриарх Светлого Рода

Лисицин Евгений
1. Темный Патриарх Светлого Рода
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Патриарх Светлого Рода

Раб и солдат

Greko
1. Штык и кинжал
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Раб и солдат

Волк 5: Лихие 90-е

Киров Никита
5. Волков
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Волк 5: Лихие 90-е