В то время как внутреннее измерение многомерного массива в C легко преобразуется в указатель:
char (*p)[2][3][4]= //<pointer to an array of 2 or array of 3 of array of 4 char
(char[5/*<- this one decays to a ptr*/][2][3][4]){0};
//^array 5 of array 2 of arry 3 of array 4 of char
;
массивы и указатели очень отличаются в C, несмотря на запутанно идентичный синтаксис для индексации иразыменование (которые являются двумя сторонами одной и той же монеты, как: x[index]
- это то же самое, что *(x+index)
или index[x]
).
Я думаю, что это часть C, где, если у вас нетконтекст, идея о том, что язык отображается непосредственно на ассемблер, наиболее явно нарушается.
Сравните
char a[1][1][1][1]={{{{'a'}}}}; //1 byte
char ****b = &(char***){&(char**){&(char*){&(char){'b'}}}}; //1byte+4*ptr_sz
, и теперь код, который генерирует ****a
, против того, что генерирует ****b
:
char get_a_char(void)
{
return ****a;
}
char get_b_char(void)
{
return ****b;
}
x86-64:
get_a_char:
mov al, BYTE PTR a[rip]
ret
get_b_char:
mov rax, QWORD PTR b[rip]
mov rax, QWORD PTR [rax]
mov rax, QWORD PTR [rax]
mov rax, QWORD PTR [rax]
mov al, BYTE PTR [rax]
ret
При разыменовании многократного косвенного указателя (b
), вы получаете чек указателя.
Когда вы разыменовываете / подписываете многомерный массив, тогда ваши индексы (нули, если вы просто разыменовываете) и размеры массива используются для вычисления смещения от базы, так что вы в конечном итоге либо получите смещенный указатель (тот же самый)указатель, если вы просто разыменовываете, просто с другим типом), если вы разыграете / подпишите через только некоторые измерений, или то же самое, за которым следует выборка с этого адреса, если вы переопределили / подпишите через всеих.
В вашем случае ptr
- это int (*)[3]
- указатель на массив из 3 int, а ptr2
- int**
- указатель на указатель на int.
Когда вы делаете ptr2[1][2]
, вы добавляете 1 размер указателя, извлекаете указатель оттуда, а затем добавляете 2 размера int (целевого типа) к извлеченному указателю и извлекаете оттуда.
Это сильно отличается от того, когдавы делаете ptr[1][2]
, и в этом случае вы добавляете один размер int[3]
к базовому указателю, а затем 2 размера int и извлекаете их оттуда (всего 1 выборка).
Типы ptr
, очевидно, не могутбыть совместимым с типом ptr2
.