Подтвердить что ты не робот

Опускание размеров при инициализации многомерных массивов C/С++

Что я знаю о компиляторах C/С++, так это то, что они игнорируют внутренние фигурные скобки при инициализации многомерных массивов.

Итак, вы не можете этого сделать:

int myArray[][] = { { 2, 3 }, { 4, 5 }, { 4, 1 } };

потому что компилятор увидит его точно как

int myArray[][] = { 2, 3, 4, 5, 4, 1 };

и теперь он не знает, есть ли это 6 * 1, 3 * 2, 2 * 3, 1 * 6 или даже что-то еще (так как это может быть частичный список инициализации, необязательно полный).

Мой вопрос: почему это работает во многих компиляторах?

int myArray[][2] = { { 2 }, { 4, 5 }, { 4, 1 } };

Компилятор "интуитивно" видит это как:

int myArray[][2] = { { 2, 0 }, { 4, 5 }, { 4, 1 } };

что означает, что он не игнорирует фигурные скобки. Я пробовал это на трех разных компиляторах до сих пор, и все работало.

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

4b9b3361

Ответ 1

Попытка 2.

Ниже приводится раздел A8.7 "Язык программирования C" от ​​K & R, 2nd edition, pages 219,220

Агрегат - это структура или массив. Если совокупность содержит члены типа агрегата, правила инициализации применяются рекурсивно. Скобки могут быть исключены при инициализации следующим образом: если инициализатор для совокупного члена, который сам по себе является совокупным начинается с левой скобки, затем следующий список разделенных запятыми инициализаторы инициализируют членов суб-агрегата; это ошибочно, чтобы там было больше инициализаторов, чем членов. Если, однако инициализатор для суммирования не начинается с левой скобки, то только достаточное количество элементов из списка учитывается члены подгруппы; любые оставшиеся члены инициализировать следующий член совокупности, из которых субагрегат это часть. Например,

 int x[] = { 1, 3, 5 }; 

объявляет и инициализирует x как 1-мерный массив с членами thee, поскольку размер не указан и существует три инициализатора.

Поэтому, учитывая эту строку

int myArray[][2] = { { 2 }, { 4, 5 }, { 4, 1 } };

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

Ниже приводится раздел A8.7 "Язык программирования C" от ​​K & R, 2nd edition, page 220

float y[4][3] = {
    { 1, 3, 5 },    
    { 2, 4, 6 },
    { 3, 5, 7 }
};

is equivalent to

float y[4][3] = {
   1, 3, 5, 2, 4, 6, 3, 5, 7 
};

Note that in both cases, the fourth row of the array will be initialized 
with zero, since not enough initializers were specified.

float y[4][3] = { 
    { 1 }, { 2 }, { 3 }, { 4 } 
};

initializes the first column of y and leaves the rest 0.

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

Ответ 2

Ниже приведена статья A8.7 "Язык программирования C" от ​​K & R, 2nd edition, page 220

float y[4][3] = {
    { 1, 3, 5 },    
    { 2, 4, 6 },
    { 3, 5, 7 }
};

эквивалентно

float y[4][3] = {
   1, 3, 5, 2, 4, 6, 3, 5, 7 
};

Обратите внимание, что в обоих случаях четвертая строка массива будет инициализирована нулем, так как было указано недостаточно инициализаторов.

float y[4][3] = { 
    { 1 }, { 2 }, { 3 }, { 4 } 
};

инициализирует первый столбец y и оставляет остальное 0.

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

Ответ 3

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

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

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

22 Если инициализирован массив неизвестного размера, его размер определяется по наибольшему индексированному элементу с явным инициализатором. Массив тип завершается в конце списка инициализаторов.

И вот пример из Стандартного

int y[4][3] = {
    { 1, 3, 5 },
    { 2, 4, 6 },
    { 3, 5, 7 },
};

- это определение с полностью скобочной инициализацией: 1, 3 и 5 инициализируют первую строку y (объект массива y [0]), а именно y [0] [0], y [0] [1], y [0] [2]. Аналогично, следующие две строки инициализируют y [1] и y [2]. Инициализатор заканчивается раньше, поэтому y [3] инициализируется нулями. Точно такой же эффект мог быть достигнут

int y[4][3] = {
    1, 3, 5, 2, 4, 6, 3, 5, 7
};

Инициализатор для y [0] не начинается с левой скобки, поэтому используются три элемента из списка. Аналогично, следующие три последовательно выполняются для y [1] и y [2].

Ответ 4

ANSCI C-89 (3.5.7) говорит:

float y[4][3] = {
    { 1, 3, 5 },
    { 2, 4, 6 },
    { 3, 5, 7 },
};

- это определение с полностью заключенной в скобки инициализацией: 1, 3 и 5 инициализируют первую строку объекта массива y[0] (а именно y[0][0], y[0][1] и y[0][2]). Аналогично, следующие две строки инициализируют y[1] и y[2]. Инициализатор заканчивается раньше, поэтому y[3] инициализируется нулями. Точно такой же эффект мог быть достигнут

float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };

Инициализатор для y[0] не начинается с левой скобки, поэтому используются три элемента из списка. Аналогично, следующие три последовательно выполняются для y[1] и y[2]. Кроме того,

float z[4][3] = {
    { 1 }, { 2 }, { 3 }, { 4 }
};

инициализирует первый столбец z, как указано, и инициализирует остальные нулями.

Ответ 5

Я думаю, эта глава важна:

8.5.1 Агрегаты

(...)

При инициализации многомерного массива инициализатор-предложения инициализируют элементы с последним (самым правым) индексом массива, изменяющимся самым быстрым (8.3.4). [Пример:

int x[2][2] = { 3, 1, 4, 2 };

инициализирует x[0][0] до 3, x[0][1] до 1, x[1][0] до 4 и от x[1][1] до 2. С другой стороны,

float y[4][3] = {
    { 1 }, { 2 }, { 3 }, { 4 }
};

инициализирует первый столбец y (рассматривается как двумерный массив) и оставляет остальное ноль. -end пример]

В объявлении формы

T x = { a };

фигурные скобки можно отбросить в списке инициализаторов следующим образом .105 Если список инициализатора начинается с левой скобки, то последующий список разделов, разделенных запятыми, инициализирует элементы субагрегата; ошибочно, чтобы было больше предложений инициализатора, чем членов. Если, однако, список инициализаторов для субагрегата не начинается с левой скобки, тогда для инициализации членов субагрегата берутся только достаточные предложения инициализатора из списка; любые оставшиеся условия инициализации оставляются для инициализации следующего члена агрегата, членом которого является текущий субагрегат. [Пример:

float y[4][3] = {
    { 1, 3, 5 },
    { 2, 4, 6 },
    { 3, 5, 7 },
};

является полностью фиксированной инициализацией: 1, 3 и 5 инициализируют первую строку массива y[0], а именно y[0][0], y[0][1] и y[0][2]. Аналогично, следующие две строки инициализируют y[1] и y[2]. Инициализатор заканчивается раньше, и поэтому элементы y[3] инициализируются так, как если бы они были явно инициализированы выражением формы float(), то есть инициализированы с помощью 0.0. В следующем примере скобки в списке инициализаторов устранены; однако список инициализаторов имеет тот же эффект, что и полностью скопированный список инициализаторов вышеприведенного примера,

float y[4][3] = {
    1, 3, 5, 2, 4, 6, 3, 5, 7
};

Инициализатор для y начинается с левой скобки, но для y [0] нет, поэтому используются три элемента из списка. Аналогично, следующие три последовательно выполняются для y[1] и y[2]. -end пример]

Ответ 6

В спецификации языка указано, что "значения полей, которые не заданы, равны нулю" в инициализации массива:

Раздел 8.5: параграф 7 версии n3337 версии С++ 11:

Если в списке меньше предложений инициализатора, чем членов в совокупности, то каждый член явно не инициализирован должны быть инициализированы из пустого списка инициализаторов (8.5.4). [ Пример:

struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };

инициализирует ss.a с 1, ss.b с "asdf" и ss.c со значением выражение формы int(), то есть 0. - конец примера]