Указатель на целочисленный массив против двойного указателя на целое число - PullRequest
3 голосов
/ 12 июня 2019

Я бы подумал, что целочисленный массив имеет тип указатель на целое число, так что это будет означать, что указатель на целочисленный массив имеет тип двойной указатель на целое число.Но результаты, которые я получаю, говорят об обратном.У меня есть подозрение, что массив целочисленных типов не указатель типа на целое число.

Вот мой пример:

int main(){
  int p[3]={1,2,3};
  int (*ptr)[3] = &p;
  int **ptr2 = &p;

  printf("%d\n",(*ptr)[0]);
  printf("%d\n",**ptr2);
  return 0;
}

p имеет целочисленный массив элемента типа 3,

& p имеет указатель типа на массив из 3 элементов.

ptr имеет указатель типа на целочисленный массив из 3 элементов.

ptr2 имеет двойной указатель на целое число

ТакМой вопрос: если целочисленный массив является указателем на целое число, почему ptr2 не работает должным образом?Отличается ли двойной указатель типа на целое число от указателя типа на целочисленный массив?Любая помощь, проясняющая это для меня, будет оценена.Спасибо!

Ответы [ 3 ]

3 голосов
/ 12 июня 2019

В то время как внутреннее измерение многомерного массива в 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.

2 голосов
/ 12 июня 2019

Я бы подумал, что целочисленный массив имеет тип указатель на целое число,

Это не так. Массивы распадаются на указатели во многих общих обстоятельствах, но они не совпадают.

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

Нет, это не так.

почему ptr2 не работает должным образом?

ptr2 - указатель, содержащий адрес массива p. Разыменование этого с помощью *ptr2 даст первый элемент в p. Снова разыменовывая это, мы использовали бы первый элемент в p в качестве адреса и дали бы значение по этому адресу.

Вот почему вы должны прочитать предупреждения от вашего компилятора. Даже без флагов -Wall и -Wextra (которые вы всегда должны использовать) этот код выдает это предупреждение:

k.c:6:16: warning: initialization of ‘int **’ from incompatible pointer type ‘int (*)[3]’ [-Wincompatible-pointer-types]
   int **ptr2 = &p;
                ^

Там есть текст. int ** не совместим с int(*)[3]

Всегда читайте предупреждения компилятора.

0 голосов
/ 12 июня 2019

Как вы обнаружили, вы можете взять адрес массива. тип этого, как правило, не очень полезный «указатель на конкретный тип массива», а не общий указатель на указатель на содержимое.

Если необходимо, вы можете создать указатель, эквивалентный массиву, а затем взять адрес этого:

int p[] = {1,2,3};
int *q=p;
int **r=q;

Но нет способа сократить этот шаг, и вы зависите от времени жизни этого промежуточного указателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...