По какой причине компилятор C требует, чтобы количество столбцов в 2d-массиве было определено?

с учетом следующей сигнатуры функции:

void readFileData(FILE* fp, double inputMatrix[][], int parameters[])

это не компилируется.

и исправлено:

void readFileData(FILE* fp, double inputMatrix[][NUM], int parameters[])

мой вопрос: почему компилятор требует, чтобы число столбцов было определено при обработке 2D-массива в C? Есть ли способ передать двумерный массив в функцию с неизвестными размерами?

спасибо

9
задан Asher Saban 20 August 2010 в 14:17
поделиться

6 ответов

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] , не так ясно, хотя для реализации было бы чрезвычайно сложно сделать разница.

2
ответ дан 4 December 2019 в 07:46
поделиться

Нет, нет. На самом деле ситуация довольно проста: на самом деле функция получает всего один линейный блок памяти. Сообщив ему количество столбцов, он узнает, как преобразовать что-то вроде block [x] [y] в линейный адрес в блоке (т.е. ему нужно сделать что-то вроде address = row * column_count + столбец ).

1
ответ дан 4 December 2019 в 07:46
поделиться

Встроенные многоуровневые массивы в 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 ]. Эту формулу перевода можно легко обобщить для массивов с любым количеством измерений. В нем будут задействованы все размеры многомерного массива, кроме первого. Вот почему каждый раз, когда вы объявляете массив, вам необходимо указывать все размеры, кроме первого.

18
ответ дан 4 December 2019 в 07:46
поделиться

Это потому, что в памяти это просто непрерывная область, одномерный массив, если хотите. И чтобы получить реальное смещение inputMatrix [x] [y], компилятор должен вычислить (x * elementsPerColumn) + y . Поэтому ему нужно знать elementsPerColumn, а это, в свою очередь, означает, что вам нужно сообщить об этом.

1
ответ дан 4 December 2019 в 07:46
поделиться

Другие люди объясняли почему, но способ передать 2D-массив с неизвестными размерами - это передать указатель. В любом случае компилятор понижает уровень параметров массива до указателей. Просто убедитесь, что в документации по API четко указано, чего вы ожидаете.

1
ответ дан 4 December 2019 в 07:46
поделиться

Поскольку массив в C - это чисто память без какой-либо метаинформации о размерах, компилятор должен знать, как применять индекс строки и столбца при адресации элемента вашей матрицы.

inputMatrix [i] [j] внутренне преобразован во что-то эквивалентное * (inputMatrix + i * NUM + j)

, и здесь вы видите, что требуется NUM .

5
ответ дан 4 December 2019 в 07:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: