516 глава 12
Динамически выделяемая память появляется при вызове malloc() или родственной ей функции и освобождается при вызове free(). Постоянство памяти управляется программистом, а не каким-то набором жестких правил, поэтому блок памяти может быть создан в одной функции и освобожден в другой. По этой причине область памяти, применяемая для динамического распределения памяти, может стать фрагментированной, т.е. неиспользованные участки будут идти вперемешку с активными блоками памяти. Кроме того, использование динамической памяти имеет тенденцию быть более медленным процессом, чем работа со стековой памятью.
Обычно программа применяет разные области памяти для статических объектов, автоматических объектов и динамически выделенных объектов. Сказанное демонстрируется в листинге 12.15.
Листинг 12.15. Программа where.с

Ниже показан вывод, полученный в одной из систем:
static_store: 30 по адресу 00378000
auto_store: 40 по адресу 0049FB8C
*pi: 35 по адресу 008Е9ВА0
Строковый литерал по адресу 00375858
Автоматический массив char по адресу 0049FB74 Динамическая строка по адресу 008E9BD0 Строка в кавычках по адресу 00375908
Как видите, статические данные, включая строковые литералы, занимают одну область, автоматические данные — вторую область, а динамически выделенные данные — третью область (часто называемую кучей или свободным хранилищем).
Классы хранения, связывание и управление памятью 517
Квалификаторы типов ANSI С
Вам уже известно, что переменная характеризуется типом и классом хранения. В стандарте С90 были добавлены еще два свойства: постоянство и изменчивость. Эти свойства объявляются с помощью ключевых слов const и volatile, которые создают квалифицированные типы. В стандарте С99 появился третий квалификатор, restrict, предназначенный для содействия компилятору в оптимизации. Наконец, в СИ добавлен четвертый квалификатор, _Atomic. Стандарт С11 предоставляет дополнительную библиотеку, управляемую stdatomic.h, для поддержки параллельного программиро- ванпя, и Atomic является частью этой необязательной поддержки.
Стандарт С99 наделяет квалификаторы типов новым свойством — теперь они идем- потеитиы. Хотя это звучит подобно жесткому требованию, в действительности это означает лишь то, что один и гот же квалификатор можно указывать в объявлении более одного раза, и избыточные квалификаторы игнорируются:
const const const int n = 6; //то же самое, что и const int n = 6;
Это делает приемлемой, например, следующую последовательность:
typedef const int zip;
const zip q = 8;
Квалификатор типа const
Вы уже встречались со случаями использования const в главах 4 и 10. В качестве напоминания: ключевое слово const в объявлении создает переменную, значение которой не может быть изменено посредством присваивания либо инкрементирования/ декрементирования. В случае компилятора, совместимого со стандартом ANSI, код
const int nochange; /* указывает, что m является константой */
nochange =12; /* не разрешено */
приводит к выдаче сообщения об ошибке. Тем не менее, инициализировать переменную const можно. Таким образом, следующий код допустим:
const int nochange = 12; /* все в порядке */
Предыдущее объявление делает nochange переменной только для чтения. После того, как она инициализирована, изменять ее нельзя.
Ключевое слово const можно применять, например, для создания массива данных, которые в программе не могут изменяться:
const int days 1 [12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Использование const с объявлениями указателей и параметров
Применять ключевое слово const при объявлении простой переменной и массива довольно просто. Указатели в этом смысле сложнее, т.к. необходимо проводить различие между объявлением самого указателя как const и превращением в const значения, на которое он указывает. Объявление
const float * pf; /* pf указывает на константное значение float */
создает указатель pf, ссылающийся на значение, которое должно оставаться постоянным. Значение самого pf можно изменять. Например, его можно установить для указания на другое значение const. В противоположность этому, объявление
float * const pt; /* pt является указателем const */ говорит о том, что значение самого указателя pt не может быть модифицировано.
518 глава 12
Он должен всегда ссылаться на один и то же адрес, однако значение, на которое он указывает, может изменяться. Наконец, объявление
const float * const ptr;
означает, что ptr должен всегда указывать па одну и ту же ячейку, а значение, хранящееся в этой ячейке, не должно изменяться.
Читать дальше