«Итак, каково будет значение имени массива 2D int?»
«Я действительно понимаю, что массив не является указателем. В моем вопросе я имею в виду, что когдаимя массива используется в выражении, компилятор сгенерирует указатель константы. "
Вы должны быть осторожны здесь.Как продолжение вашего комментария под вашим вопросом, есть нюансы в том, как применяются правила преобразования массивов / указателей, которые влияют на type
, который получается в результате преобразования.Это будет определять, можно ли и как использовать имя массива в выражении.
"... компилятор сгенерирует указатель константы."
Компилятор не генерирует указатель-константу, компилятор следует C11 Стандарт - 6.3.2.1 Другие операнды - L-значения, массивы и указатели функций (p3) .Когда имя массива используется в выражении, выражение оценивается по адресу, который является результатом преобразования массива в указатель (с учетом 4-исключений, указанных в пункте 3).
Правило, касающеесяПреобразование массив / указатель не зависит от количества измерений, правило применяется одинаково независимо. Однако , type
указателя, который получается в результате преобразования , действительно зависит от количества измерений массива.Это очень важно, и это будет диктовать, действительно ли вы используете имя массива.
Один из способов помочь закрепить то, что происходит в процессе конверсии, - сделать это шаг за шагом.Начните с 1D массива и продолжайте свой путь вверх.
6.3.2.1 - Преобразование 1D массива в указатель при доступе
Если у вас есть простой массив, например
int array[10];
При доступе к массиву преобразуется в указатель на первый элемент, например, адрес элемента , &array[0]
.(это просто указатель на int
или с формальным типом int *
)
6.3.2.1 - Преобразование двухмерного массива в указатель при доступе
Для двумерного массива правило применяется точно так же, например,
int array[10][10];
Здесь array
, двумерный массив, по сути, представляет собой массив из 10 - int[10]
массивов (массив 1Dмассивы).При доступе array[10][10]
преобразуется в указатель на первый массив 10-int
точно таким же образом, &array[0][0]
(что приводит к указателю на массив int[10]
или с формальным типомint (*)[10]
) Это не указатель на указатель (например, int**
), это, в частности, указатель на массив int[10]
.
( note важное различие между int *[10]
(массив из 10 указателей , который при обращении станет указателем на указатель)) и int (*)[10]
(указатель на массив из 10 int
))
Ответ
"Итак ... значение имени двумерного массива int при использовании в выражении" - это адрес первого 1D массива целых чисел, которые составляют двумерный массив формального типаint (*)[N]
(где N
- количество элементов в строке).
Нюанс в применении стандарта
Тип является критическим для правильногоиспользование имени массива.Для двумерного массива результирующий адрес является указателем на массив .Что приведет к разыменованию этого указателя?( Ответ: и массив ) Что происходит, когда вы получаете доступ к этому массиву через разыменованный указатель?(подсказка: преобразование в правилах доступа применяется снова).Вы должны знать, какой будет тип указателя, полученный в результате преобразования, чтобы правильно использовать имя массива в выражении.
Пример может помочь
Или нет, но работа с типами указателей, полученными в результате доступа к массиву и преобразования указателей, может помочь в этом разобраться. Ниже пример объявляет простой 4x3
2D-массив int
.Затем он объявляет указатель (p
) надлежащего типа, чтобы разрешить использование имени массива в выражении, присваивающем адрес массива указателю.Указатель, инициализированный именем массива, затем используется для дальнейшей инициализации целочисленного указателя (ip
) на первый элемент в первом массиве.
В этом примере выводится адрес для каждого элемента, а затем с помощьюуказатель p
выводит адрес начала каждого массива строк, который составляет двумерный массив.Наконец, код входит в цикл проверки, сравнивая адреса каждого элемента по (1) индексу массива, (2) адресу, удерживаемому указателем p
с использованием смещения, и (3) адресу, удерживаемому ip
.Целью является использование каждого из различных указателей, полученных в результате выражения, присваивающего имя массива для ссылки на каждый элемент и обеспечивающего совпадение адресов, содержащихся в каждом указателе.
#include <stdio.h>
int main (void) {
int array[ ][3] = { {1, 2, 3}, /* 2D array values */
{3, 4, 5},
{5, 6, 7},
{7, 8, 9} },
(*p)[3] = array, /* pointer to array */
*ip = *p; /* integer poiner */
size_t size = sizeof array,
nele = size / sizeof **array,
nrow = size / sizeof *array,
ncol = sizeof *array / sizeof **array;
printf ("2D array statistics:\n\n"
" size: %zu (bytes)\n nele: %zu (ints)\n"
" nrow: %zu\n ncol: %zu\n",
size, nele, nrow, ncol);
puts ("\naddress of each array element:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++)
printf (" %p", (void*)&array[i][j]);
putchar ('\n');
}
puts ("\naddress of each 1D array:\n");
for (size_t i = 0; i < nrow; i++)
printf (" %p\n", (void*)p[i]);
puts ("\nvalidating each array element address by index & pointer:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++) {
if (ip != &array[i][j] || ip != *p + j) {
fprintf (stderr, "address validation failed for "
"array[%zu][%zu]\n(%p != %p || %p != %p)\n",
i, j, (void*)ip, (void*)&array[i][j],
(void*)ip, (void*)(p + j));
return 1;
}
ip++;
}
p++;
}
puts (" done!");
return 0;
}
Пример использования / Вывод
$ ./bin/array_2d_access
2D array statistics:
size: 48 (bytes)
nele: 12 (ints)
nrow: 4
ncol: 3
address of each array element:
0x7ffe7c9a9780 0x7ffe7c9a9784 0x7ffe7c9a9788
0x7ffe7c9a978c 0x7ffe7c9a9790 0x7ffe7c9a9794
0x7ffe7c9a9798 0x7ffe7c9a979c 0x7ffe7c9a97a0
0x7ffe7c9a97a4 0x7ffe7c9a97a8 0x7ffe7c9a97ac
address of each 1D array:
0x7ffe7c9a9780
0x7ffe7c9a978c
0x7ffe7c9a9798
0x7ffe7c9a97a4
validating each array element address by index & pointer:
done!
Дайте мне знать, помогло ли это и есть ли у вас какие-либо дополнительные вопросы.