Теперь у вас есть три способа создания массива.
• Объявить массив, используя константные выражения для размерностей, и применять для доступа к элементам имя массива. Такой массив может быть создан с использованием либо статической, либо автоматической памяти.
• Объявить массив переменной длины, применяя переменные выражения для размерностей, и использовать для доступа к элементам имя массива. (Вспомните, что эта возможность предусмотрена стандартом С99.) Такой вариант доступен только для автоматической памяти.
• Объявить указатель, вызвать malloc(), присвоить возвращаемое значение указателю и применять для доступа к элементам указатель. Этот указатель может быть либо статическим, либо автоматическим.
Второй и третий методы можно использовать для выполнения того, что не получится сделать с обычным объявленным массивом — создать динамический массив, память под который выделяется во время выполнения программы и тогда же есть возможность выбрать его размер. Предположим, например, что n — целочисленная переменная. До выхода стандарта С99 нельзя было поступать так:
double item[n]; /* до С99: не разрешено, если n является переменной */
Однако можно было записывать следующим образом даже в случае компилятора, выпущенного до выхода С99:
ptd = (double *) malloctn * sizeof(double)); /* нормально */
Этот прием работает и, как вы вскоре убедитесь, он обладает несколько большей гибкостью, чем массив переменной длины.
Обычно вы должны компенсировать каждый случай вызова malloc() вызовом free(). Функция free() принимает в качестве аргумента адрес, возвращенный ранее
Классы хранения, связывание и управление памятью 511
функцией malloc(), и освобождает память, которая была выделена. Таким образом, продолжительность существования выделенной памяти рассчитывается с момента, когда была вызвана функция malloc() для выделения памяти, и до момента, когда вызывается функция free() с целью освобождения памяти для ее повторного использования. Функции malloc() и free() можно рассматривать как инструменты для управления пулом памяти. Каждый вызов malloc() выделяет память для применения программой, а каждый вызов free() восстанавливает память в пуле, так что она может повторно использоваться. Аргументом free() должен быть указатель на блок памяти, выделенный malloc(); функцию free() нельзя применять для освобождения памяти, выделенной другими средствами, такими как объявление массива. Функции malloc() и free() имеют прототипы в заголовочном файле stdlib.h.
За счет использования malloc() программа может решать, массив какого размера требуется, и создавать его во время выполнения. Эта возможность демонстрируется в листинге 12.14. В нем указателю ptd присваивается адрес блока памяти, после чего ptd применяется, как если бы это было имя массива. Если выделить нужную память не удалось, для прекращения работы программы вызывается функция exit(), прототип которой содержится в stdlib.h. Значение EXIT_FAILURE определено в этом же заголовочном файле. Стандарт предоставляет два возвращаемых значения, которые гарантированно распознают все операционные системы: EXIT_SUCCESS (эквивалентно значению 0) для указания на нормальное завершение программы и EXIT FAILURE для указания на аварийное завершение. Некоторые операционные системы, включая Unix, Linux и Windows, могут принимать дополнительные целочисленные значения, обозначающие конкретные формы отказа.
Листинг 12.14. Программа dyn arr.с

512 глава 12

Ниже показаны результаты пробного запуска. Мы ввели шесть чисел, но программа обработала только пять из них, поскольку размер массива был ограничен до 5.
Введите максимальное количество элементов типа double.
5
Введите значения (q для выхода) :
20 30 35 25 4 0 80
Введено 5 элементов:
20.00 30.00 35.00 25.00 40.00
Программа завершена.
Давайте рассмотрим код. Программа получает нужный размер массива с помощью следующих строк:
if (scanf ("%d", &max) != 1)
{
puts("Количество введено некорректно -- программа завершена."); exit(EXIT_FAILURE);
}
Показанная ниже строка кода выделяет в памяти пространство, достаточное для хранения запрошенного количества элементов, и затем присваивает адрес этого блока указателю ptd:
ptd = (double *) malloclmax * sizeof (double));
Приведение к (double * ) не обязательно в С, но требуется в C++, поэтому использование приведения типа упрощает перенос программы из С в C++.
Читать дальше