с учетом следующей сигнатуры функции:
void readFileData(FILE* fp, double inputMatrix[][], int parameters[])
это не компилируется.
и исправлено:
void readFileData(FILE* fp, double inputMatrix[][NUM], int parameters[])
мой вопрос: почему компилятор требует, чтобы число столбцов было определено при обработке 2D-массива в C? Есть ли способ передать двумерный массив в функцию с неизвестными размерами?
спасибо
C не имеет какой-либо конкретной поддержки многомерных массивов. Двумерный массив, такой как double inputMatrix [N] [M]
, представляет собой просто массив длины N
, элементы которого представляют собой массивы длины M
двойников.
Есть обстоятельства, при которых вы можете не указывать количество элементов в типе массива. Это приводит к неполному типу - типу, требования к хранилищу которого неизвестны. Таким образом, вы можете объявить двойной вектор []
, который представляет собой массив неопределенного размера двойников. Однако вы не можете помещать объекты неполных типов в массив, потому что компилятор должен знать размер элемента при доступе к элементам.
Например, вы можете написать double inputMatrix [] [M]
, который объявляет массив неопределенной длины, элементы которого являются массивами длины M
двойников. Затем компилятор знает, что адрес inputMatrix [i]
составляет i * sizeof (double [M])
байтов за пределами адреса inputMatrix [0]
( и поэтому адрес inputMatrix [i] [j]
равен i * sizeof (double [M]) + j * sizeof (double)
bytes). Обратите внимание, что ему необходимо знать значение M
; вот почему вы не можете опускать M
в объявлении inputMatrix
.
Теоретическим следствием расположения массивов является то, что inputMatrix [i] [j]
обозначает тот же адрес, что и inputMatrix + M * i + j
.¹
Практическое следствие такой схемы состоит в том, что для эффективного кода вы должны упорядочивать массивы таким образом, чтобы наиболее часто меняющееся измерение было последним. Например, если у вас есть пара вложенных циклов, лучше использовать кеш с for (i = 0; i
Ссылки C89: §3.5.4.2 (типы массивов), §3.3.2.1 (выражения индекса массива)
Ссылки C99: §6.7.5.2 (типы массивов), §6.5.2.1-3 (выражения индекса массива).
¹ Доказательство того, что это выражение корректно определено, оставляем читателю в качестве упражнения. Является ли inputMatrix [0] [M]
допустимым способом доступа к inputMatrix [1] [0]
, не так ясно, хотя для реализации было бы чрезвычайно сложно сделать разница.
Нет, нет. На самом деле ситуация довольно проста: на самом деле функция получает всего один линейный блок памяти. Сообщив ему количество столбцов, он узнает, как преобразовать что-то вроде block [x] [y]
в линейный адрес в блоке (т.е. ему нужно сделать что-то вроде address = row * column_count + столбец
).
Встроенные многоуровневые массивы в C (и в C ++) реализованы с использованием подхода «индекс-трансляция». Это означает, что двумерный (трехмерный, четырехмерный и т. Д.) Массив размещается в памяти как обычный одномерный массив достаточного размера, а доступ к элементам такого массива осуществляется путем пересчета многомерных индексов на соответствующий одномерный индекс. Например, если вы на самом деле определяете 2D-массив размером M x N
double inputMatrix[M][N]
, под капотом компилятор создает массив размером M * N
double inputMatrix_[M * N];
. Каждый раз, когда вы обращаетесь к элементу вашего array
inputMatrix[i][j]
компилятор преобразует его в
inputMatrix_[i * N + j]
Как видите, для выполнения преобразования компилятор должен знать N
, но на самом деле не нужно знать M
]. Эту формулу перевода можно легко обобщить для массивов с любым количеством измерений. В нем будут задействованы все размеры многомерного массива, кроме первого. Вот почему каждый раз, когда вы объявляете массив, вам необходимо указывать все размеры, кроме первого.
Это потому, что в памяти это просто непрерывная область, одномерный массив, если хотите. И чтобы получить реальное смещение inputMatrix [x] [y], компилятор должен вычислить (x * elementsPerColumn) + y
. Поэтому ему нужно знать elementsPerColumn, а это, в свою очередь, означает, что вам нужно сообщить об этом.
Другие люди объясняли почему, но способ передать 2D-массив с неизвестными размерами - это передать указатель. В любом случае компилятор понижает уровень параметров массива до указателей. Просто убедитесь, что в документации по API четко указано, чего вы ожидаете.
Поскольку массив в C - это чисто память без какой-либо метаинформации о размерах, компилятор должен знать, как применять индекс строки и столбца при адресации элемента вашей матрицы.
inputMatrix [i] [j]
внутренне преобразован во что-то эквивалентное * (inputMatrix + i * NUM + j)
, и здесь вы видите, что требуется NUM
.