int sum4d(int (*ar) [12] [20] [30], int rows); // ar - указатель
Здесь ar указывает на массив размером 12x20x30 со значениями типа int.
Массивы переменной длины

Вы могли уже заметить странность в функциях, имеющих дело с двумерными массивами: количество строк можно описать с помощью параметра функции, но количество столбцов встроено в функцию. Например, взгляните на такое определение:
Предположим, что были объявлены следующие массивы:
Функцию sum2d() можно применять с любым из этих массивов:
tot = sum2d(arrayl, 5); // суммирование элементов массива 5x4
tot = sum2d(array2, 100); // суммирование элементов массива 100 х 4
tot = sum2d(arrауЗ, 2); // суммирование элементов массива 2x4
Это объясняется тем, что количество строк передается в параметре rows, представляющем собой переменную. Однако если нужно просуммировать массив размером 6x5, придется использовать новую функцию, в которой параметр COLS должен быть определен как 5. Такое поведение является результатом того факта, что для указания размерности массива необходимо применять константы; таким образом, COLS нельзя заменить переменной. Если вы действительно хотите создать одну функцию, которая будет работать с двумерным массивом любого размера, это можно сделать, но придет ся приложить немало труда. (Вы должны передать массив как одномерный и заставить функцию вычислять, где начинается каждая строка.) Более того, этот прием недоста-
Массивы и указатели 407
точно гладко сочетается с подпрограммами на языке FORTRAN, которые позволяют указывать в вызове функции оба измерения. Конечно, FORTRAN можно считать древним языком программирования, но за прошедшие десятилетия эксперты в области численных методов разработали на FORTRAN много удобных библиотек вычислений. Язык С позиционируется как наследник FORTRAN, поэтому возможность преобразования библиотек FORTRAN с минимальными усилиями является весьма полезной.
Такая потребность стала главным мотивом для ввода в стандарт С99 понятия массивов переменной длины, которые позволяют использовать переменные для размерности массива. Например, можно поступить так:
int quarters = 4; int regions = 5;
double sales[regions][quarters]; // массив переменной длины
Как упоминалось ранее, с массивами переменной длины связаны некоторые ограничения. Они должны иметь автоматический класс хранения, а это означает их объявление либо в функции без применения модификаторов класса хранения static или extern (глава 12), либо в виде параметров функции. Кроме того, инициализация в объявлении невозможна. Наконец, в стандарте С11 массивы переменной длины являются необязательной возможностью языка в отличие от С99, где они были обязательными.
НА ЗАМЕТКУ! Массивы переменной длины не изменяют размер
Понятие переменный в массиве переменной длины вовсе не означает возможность изменения длины массива после его создания. Будучи созданным, массив переменной длины сохраняет тот же самый размер. В действительности понятие переменный означает, что при указании размерностей при первоначальном создании массива можно использовать переменные.
Поскольку массивы переменной длины представляют собой новое дополнение языка, их поддержка к настоящему времени пока не завершена. Давайте рассмотрим простой пример, в котором показано, как написать функцию для суммирования содержимого любого двумерного массива значений типа int. Для начала вот объявление функции с аргументом в виде двумерного массива переменной длины:
int sum2d(int rows, int cols, int ar[rows][cols]); //ar - массив переменной длины
Обратите внимание на то, что два первых параметра (rows и cols) применяются в качестве размерностей для объявления параметра типа массива ar. Поскольку в объявлении ar используются rows и cols, в списке параметров они должны быть объявлены до появления ar. Поэтому следующий прототип является ошибочным:
int sum2d(int ar[rows][cols], int rows, int cols); // некорректный порядок
В стандартах С99/С11 утверждается, что имена в прототипе можно опускать, но в этом случае размерности должны быть заменены звездочками:
int sum2d(int, int, int ar[*] [*]); //ar - массив переменной длины, имена не указаны Теперь посмотрите, как определять функцию:
int sum2d(int rows, int cols, int ar[rows][cols])
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++) for (c =0; c < cols; C++) tot += ar [r] [c];
return tot;
}
408 Глава 10
Если не считать нового заголовка функции, то единственное отличие этой функции от ее классической версии на С (листинг 10.17) состоит в том, что константа COLS была заменена переменной cols. Такое изменение стало возможным благодаря присутствию в заголовке функции массива переменной длины. Кроме того, наличие переменных, которые представляют количество строк и столбцов, позволяет применять новую функцию sum2d() с любым размером двумерного массива значений int. Данное утверждение иллюстрируется в листинге 10.18. Однако для компиляции такого кода требуется компилятор С, в котором реализована поддержка массивов переменной длины. Здесь также демонстрируется возможность использования этой функции, основанной на массиве переменной длины, либо с традиционными массивами С, либо с массивом переменной длины.
Читать дальше