Я работаю над упражнением в 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]) {...}
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}
};