Как выделяется память для статического многомерного массива?

Все,

Это беспокоит меня уже некоторое время. В C\C++ (я думаю, java и .NET также) нам не нужно указывать индекс строки в многомерном массиве. Итак, например, я могу объявить массив целых чисел как таковой:

целочисленный Массив[][100];

Я думаю, что статические массивы в целом представлены как непрерывная память в стеке. Итак, принимая представление по столбцам, как компилятор узнает, сколько памяти нужно выделить в приведенном выше случае, поскольку в нем отсутствует одно из измерений?


person vj01    schedule 01.12.2009    source источник
comment
Я добавил теги c и c++, так как это зависит от того, как эти языки обрабатывают это.   -  person Jeffrey Aylesworth    schedule 01.12.2009


Ответы (3)


На языке С++ вы не можете просто сделать

int Array[][100]; /* ERROR: incomplete type */

потому что это будет определение объекта неполного типа, что явно недопустимо в C++. Вы можете использовать это в неопределяющей декларации

extern int Array[][100];

(или как статический член класса), но когда дело доходит до фактического определения одного и того же объекта массива, оба размера должны быть указаны явно (или получены из явного инициализатора).

В C ситуация не сильно отличается, за исключением того, что в C есть такие вещи, как пробные определения, которые позволяют вам писать

int Array[][100];

Однако предварительное определение в этом отношении очень похоже на неопределяющее объявление, поэтому оно разрешено. В конце концов вам придется определить тот же объект с явно указанным размером в той же единице перевода (некоторые компиляторы не требуют этого как нестандартное расширение). Если вы попробуете что-то подобное в непредварительном определении, вы получите ошибку

static int Array[][100]; /* ERROR: incomplete type */

Так что, если подумать, помимо предварительных определений, ситуация в C и C++ не сильно отличается: в этих языках запрещено определять объекты неполного типа, а массив неопределенного размера является неполным типом.

person AnT    schedule 01.12.2009
comment
Хотя вам не нужно указывать его явное определение для завершения типа, потому что, если явное внешнее определение не предоставлено (и все определения являются предварительными), объект неявно инициализируется в конце TU с помощью инициализатора, равного 0 : Это сделает тип массива полным, придав ему размер 1, и предоставит необходимое внешнее определение. См. 6.9.2 и его пример 2 в параграфе 5 n1256. Нестандартное расширение заключается в том, что вы можете предоставить другое определение в другом TU (таким образом, имея несколько внешних определений одного и того же объекта, вызывающих UB). - person Johannes Schaub - litb; 01.12.2009
comment
Я думал об этом (хотя и не заметил примера), но нашел формулировку в 6.9.2 неубедительной. В нем говорится, что если нет явного непредварительного определения, предоставленное компилятором определение будет эквивалентно определению с инициализатором, равным 0. Неясно, что именно это должно означать. По-видимому, предполагалось, что подразумеваемый инициализатор должен выглядеть как ... = { 0 } (в отличие от ... = 0), но по какой-то причине это не прописано. - person AnT; 01.12.2009
comment
Что касается нестандартного расширения, то, насколько я знаю, расширенные компиляторы просто обрабатывают предварительное определение как не определяющее объявление, т.е. не предварительное определение в другой единице перевода с более чем одним элементом массива будет использоваться для всех инициализаций перевода (если они доступны). - person AnT; 01.12.2009
comment
Согласен насчет странности с инициализатором равным 0 :) - person Johannes Schaub - litb; 01.12.2009
comment
Я думал о J.5.11, описывающем это общее расширение. Я думаю, что GCC помещает такие определения без инициализаторов в раздел COMMON (может быть отключен с помощью -fno-common), где во время компоновки они объединяются вместе, при этом создается объект, имеющий наибольший размер. Не уверен в этих деталях :) - person Johannes Schaub - litb; 01.12.2009

В java и .NET не думайте о «стеке» — объекты живут в куче. А в C это просто объявление — только определение действительно резервирует память! Так что это НЕ будет приемлемым определением - если вы поместите его как единственную строку в файле a.c:

$ gcc -c a.c
a.c:1: warning: array ‘Array’ assumed to have one element

так что gcc просто обрабатывает его, как если бы это был int Array[1][100];, поскольку он предупреждает вас, что он делает.

person Alex Martelli    schedule 01.12.2009
comment
@Alex Но Visual Studio позволяет объявить то, что я упоминал, хотя - person vj01; 01.12.2009
comment
Объявите это: да. Использовать: нет. Вы получите ошибку LNK2001, поскольку определения нет. - person Hans Passant; 01.12.2009
comment
Что ж, в C, как и в C++, int Array[][100] — это определение. Единственная причина, по которой он проскальзывает в C, заключается в специфических для C правилах для предварительных определений. - person AnT; 01.12.2009

Он не знает, сколько памяти выделить, с array[] он знает, что массив является указателем (например, int *array). array[][100] (поправьте меня, если я ошибаюсь) совпадает с array[100].

person empc    schedule 01.12.2009
comment
@empc Я думаю, что это неверно для статически объявленных массивов. Для статически объявленных массивов память должна выделяться во время компиляции в стеке. - person vj01; 01.12.2009
comment
@vj, нет, не обязательно в стеке - статические данные могут полностью храниться в других сегментах. @empc, int array[100]; — это всего 400 байт (в 32-битной сборке), int array[][100] — это совершенно другой зверь — ближе к int bah[]; (объявление, не определение!). - person Alex Martelli; 01.12.2009