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

Исчерпывающие элементы скалярного инициализатора для указателя на массив ints

Im работает над упражнением в K & R (например, 5-9), и я пытался преобразовать исходные программы 2D-массива

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

с помощью указателей на массив из 13 таких как

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

Но компилятор печатает предупреждение: лишние элементы в скалярном инициализаторе.

Googling не помог, и даже K & R пишет при передаче массива функции,

myFunction(int daytab[2][13]) {...}

совпадает с

myFunction(int (*daytab)[13]) {...}
4b9b3361

Ответ 1

Эти два лишь частично эквивалентны. Разница заключается в том, что:

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

объявляет двумерный массив, который включает в себя выделение пространства для массива и обеспечение того, что daytab ссылается на эту память. Однако:

static char (*daytab)[13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

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

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

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

static char arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
static char (*ptr)[3] = NULL;

ptr = arr;

... и затем используйте ptr и arr взаимозаменяемо. Или это:

static char (*ptr)[3] = NULL;

ptr = malloc(2 * sizeof(*ptr));

... получить динамически распределенный 2-мерный массив (не массив указателей на 1D-массивы, а реальный 2D-массив). Конечно, в этом случае он не инициализируется.

"Эквивалентность" двух вариаций просто означает, что 2D-массив, когда он распадается на указатель на его первый элемент, распадается на тип указателя, объявленного во втором варианте. Как только указатель на самом деле указывается на массив, эти два эквивалентны. Но версия 2D-массива устанавливает память для массива, где объявление указателя не... и указателю может быть присвоено новое значение (указанное в другом массиве), где переменная 2D-массива не может.

В C99 вы можете сделать это, хотя (если не static):

char (*daytab)[13] = (char [][13]){
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};