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

на главную

Жанры

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
.

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

Возвышение Меркурия. Книга 17

Кронос Александр
17. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 17

Сильнейший ученик. Том 2

Ткачев Андрей Юрьевич
2. Пробуждение крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сильнейший ученик. Том 2

Теневой путь. Шаг в тень

Мазуров Дмитрий
1. Теневой путь
Фантастика:
фэнтези
6.71
рейтинг книги
Теневой путь. Шаг в тень

Ну, здравствуй, перестройка!

Иванов Дмитрий
4. Девяностые
Фантастика:
попаданцы
альтернативная история
6.83
рейтинг книги
Ну, здравствуй, перестройка!

Вечный. Книга III

Рокотов Алексей
3. Вечный
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга III

Безымянный раб

Зыков Виталий Валерьевич
1. Дорога домой
Фантастика:
фэнтези
9.31
рейтинг книги
Безымянный раб

Измена. Возвращение любви!

Леманн Анастасия
3. Измены
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Измена. Возвращение любви!

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

Винокуров Юрий
23. Кодекс Охотника
Фантастика:
боевая фантастика
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга XXIII

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

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

Лорд Системы 14

Токсик Саша
14. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Лорд Системы 14

Последний Паладин. Том 2

Саваровский Роман
2. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 2

Последний реанорец. Том III

Павлов Вел
2. Высшая Речь
Фантастика:
фэнтези
попаданцы
5.25
рейтинг книги
Последний реанорец. Том III

Темный Лекарь 5

Токсик Саша
5. Темный Лекарь
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Темный Лекарь 5

Газлайтер. Том 6

Володин Григорий
6. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 6