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

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

Жанры

Linux программирование в примерах

Роббинс Арнольд

Шрифт:

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
.

3.2.1.6. Подведение итогов из GNU Coding Standards

Чтобы подвести итоги, процитируем, что говорит об использовании процедур выделения памяти GNU Coding Standards:

Проверяйте каждый вызов

malloc
или
realloc
на предмет возвращенного нуля. Проверяйте
realloc
даже в том случае, если вы уменьшаете размер блока; в системе, которая округляет размеры блока до степени двойки,
realloc
может получить другой блок, если вы запрашиваете меньше памяти.

В Unix

realloc
может разрушить блок памяти, если она возвращает ноль. GNU
realloc
не содержит подобной ошибки: если она завершается неудачей, исходный блок остается без изменений. Считайте, что ошибка устранена. Если вы хотите запустить свою программу на Unix и хотите избежать потерь в этом случае, вы можете использовать GNU
malloc
.

Вы должны считать, что

free
изменяет содержимое освобожденного блока. Все, что вы хотите получить из блока, вы должны получать до вызова
free
.

В этих трех коротких абзацах Ричард Столмен (Richard Stallman) выразил суть важных принципов управления динамической памятью с помощью

malloc
. Именно использование динамической памяти и принцип «никаких произвольных ограничений» делают программы GNU такими устойчивыми и более работоспособными по сравнению с их Unix-двойниками.

Мы хотим подчеркнуть, что стандарт С требует, чтобы

realloc
не разрушал оригинальный блок памяти, если она возвращает
NULL
.

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

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Измена. Отбор для предателя

Лаврова Алиса
1. Отбор для предателя
Фантастика:
фэнтези
5.00
рейтинг книги
Измена. Отбор для предателя

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

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

Шаг в бездну

Муравьёв Константин Николаевич
3. Перешагнуть пропасть
Фантастика:
фэнтези
космическая фантастика
7.89
рейтинг книги
Шаг в бездну

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Хроники странного королевства. Вторжение. (Дилогия)

Панкеева Оксана Петровна
110. В одном томе
Фантастика:
фэнтези
9.38
рейтинг книги
Хроники странного королевства. Вторжение. (Дилогия)

Часовой ключ

Щерба Наталья Васильевна
1. Часодеи
Фантастика:
фэнтези
9.36
рейтинг книги
Часовой ключ

Инвестиго, из медика в маги

Рэд Илья
1. Инвестиго
Фантастика:
фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Инвестиго, из медика в маги

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Очешуеть! Я - жена дракона?!

Амеличева Елена
Фантастика:
юмористическая фантастика
5.43
рейтинг книги
Очешуеть! Я - жена дракона?!

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9