История вопроса
Многомерные массивы в языке C изначально задумывались для простоты работы с таблицами и матрицами. Классический двумерный массив — массив массивов, с возможностью работы с элементами табличных данных простым синтаксисом. Со временем подход эволюционировал, особенно при работе с массивами переменной длины.
Проблема
Неправильное объявление и инициализация многомерных массивов ведёт к ошибкам компиляции или логическим сбоям. При передаче многомерного массива в функцию многие разработчики теряются из-за требований спецификации — необходимо явно указывать размеры всех кроме первого измерения.
Решение
Объявление двумерного массива:
int matrix[3][4];
Полная инициализация:
int matrix[2][3] = { {1, 2, 3}, {4, 5, 6} };
Передача в функцию — все размеры, кроме первого, необходимо явно указывать:
void printMatrix(int m[][3], int rows) { for (int i = 0; i < rows; ++i) { for (int j = 0; j < 3; ++j) printf("%d ", m[i][j]); printf(" "); } }
С введением стандарта C99 можно объявлять функции принимающие массивы переменной длины:
void foo(int rows, int cols, int a[rows][cols]);
Ключевые особенности:
1. Можно ли объявить функцию, принимающую двумерный массив без указания второго размера?
Нет, C требует, чтобы все размеры, кроме первого, были известны на этапе компиляции. Это связано с арифметикой указателей при обращении к элементам.
Пример ошибки:
// Ошибка: void process(int arr[][], int rows); // Нельзя
2. Что будет, если инициализировать не все элементы многомерного массива?
Оставшиеся элементы будут автоматически заполнены нулями, если массив статический или глобальный. Для локального массива с частичной инициализацией неявно инициализированные элементы также будут нулями.
int a[2][3] = {{1}, {4}}; // a[0][1] и a[0][2], a[1][1] и a[1][2] станут 0
3. В чем разница между массивом указателей и двумерным массивом?
Двумерный массив — это единый блок памяти. Массив указателей — набор указателей на отдельные (возможно раздельно выделенные) одномерные массивы. Это важно, например, при выделении памяти под «рваные» массивы.
Попытка объявить и передать двумерный массив без указания второго размера в функцию, что приводит к ошибке компиляции. Поверхностное исправление заменой на указатель приводит к неопределённому или неверному поведению при дальнейших вычислениях.
Плюсы:
Минусы:
Разработчик явно указывает размеры всех измерений, поясняет в документации порядок хранения элементов в памяти, тем самым сокращая количество ошибок при дальнейшем сопровождении.
Плюсы:
Минусы: