Лишние элементы скалярного инициализатора для указателя на массив целых чисел

Я работаю над упражнением в 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 int, например

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}
};

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

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

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

то же самое, что

myFunction(int (*daytab)[13]) {...}
27
задан Palec 6 February 2015 в 13:58
поделиться

1 ответ

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

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));

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

«Эквивалентность» двух вариантов просто означает, что двумерный массив, когда он распадается на указатель на свой первый элемент, затухает в тип указателя, объявленный во втором варианте. Как только версия указателя фактически указана на массив, они эквивалентны. Но версия 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}
};
30
ответ дан 28 November 2019 в 05:39
поделиться
Другие вопросы по тегам:

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