Я тестировал некоторые коды, чтобы узнать, как 2d-массив реализован в c. Затем я столкнулся со следующей проблемой.
Код:
int main(){
int a[4][4];
printf("a: %p, *a: %p, **a: 0x%x\n",a,*a,**a);
}
Я скомпилировал это с помощью 32-битной Ubuntu gcc.
В результате:
a: 0xbf9d6fdc, *a: 0xbf9d6fdc, **a: 0x0
Я ожидал другого значения для a
и *a
, но они одинаковы.
почему a
и *a
одинаковы в этом случае?
Разве a
не int**
тип?
Тогда какова роль *
оператора в *a
?
Обратите внимание, что технически вы должны привести аргументы, соответствующие форматеру %p
, к void*
. В большинстве архитектур передача любого типа указателя будет иметь тот же эффект, но это не гарантируется.
Выполнение **a
может вызвать неопределенное поведение, поскольку оно оценивается как a[0][0]
, которое не было инициализировано/установлено ранее.
@alk Педантично, адрес a
занят, и его нельзя объявить с помощью квалификатора register
. В основных системах без экзотических представлений ловушек это означает, что переменная принимает неопределенное значение, но это не UB. stackoverflow.com/a/40674888/584518
Проверьте типы!!
Учитывая определение как int a[4][4];
a
имеет тип int [4][4]
- массив массива из 4 целых чисел. Это нет то же, что и int **
.a[n]
имеет тип int [4]
- массив из 4 целых чисел. Это нет то же, что и int *
a[n][m]
относится к типу int
. - целое число.Теперь, учитывая тот факт, что адрес массива также является адресом первого элемента в массиве, значения одинаковы, но они различаются по типам.
Чтобы визуально проверить
int a[4][4]
+-------+--------+-------+-----------+
| | | | |
|a[0][0]| a[0][1]| a[0][2] a[0][3] |
| | | | |
+------------------------------------+
| | | | |
|a[1][0]| | | |
| | | | |
+------------------------------------+
| | | | |
|a[2][0]| | | |
| | | | |
+------------------------------------+
| | | | |
| | | | |
| | | | |
| | | | |
+-------+--------+-------+-----------+
Затем, цитируя C11
, §6.3.2.1
Except when it is the operand of the
sizeof
operator, the_Alignof
operator, or the unary&
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [...]
Таким образом, при передаче аргумента типа массива в качестве аргумента функции он распадается на указатель на первый элемент массива.
Итак, давайте посмотрим.
a
, распадается на &(a[0])
- адрес первого элемента типа int (*)[4]
.*a
, который совпадает с a[0]
, распадается на int *
, указатель на первый элемент.**a
что то же самое, что *(*a)
== *(a[0])
== a[0][0]
- это значение int
по этому индексу.Теперь еще раз внимательно посмотрите на изображение выше — видите ли вы, что первый элемент int [0]
и int [0][0]
в основном находится в одном и том же адрес? Другими словами, начальный адрес тот же.
Это причина вывода, который вы видите.
Я предположил a
как тип указателя, поскольку []
оператор *
работает одинаково для int**
и int arr[][]
в большинстве случаев. Спасибо за разрешение моего недоразумения.
@DongRyeong Я добавил некоторые уточнения, надеюсь, это поможет.
a
неint**
тип?» Ни в коем случае нетint **
в Любые этого. Где бы вы ни читали/выводили/вам говорили это, они ошибались.