Почему указатель на указатель несовместим с указателем на массив? - PullRequest
12 голосов
/ 28 октября 2011

ОК, у меня проблемы с пониманием указателей на указатели против указателей на массивы.Рассмотрим следующий код:

char s[] = "Hello, World";
char (*p1)[] = &s;
char **p2 = &s;
printf("%c\n", **p1); /* Works */
printf("%c\n", **p2); /* Segmentation fault */

Почему первый printf работает, а второй - нет?

Из того, что я понимаю, 's' - указатель на первый элементмассива (то есть, 'H').Поэтому объявление p2 как char ** означает, что это указатель на указатель на символ.Указание на 's' должно быть законным, поскольку 's' - указатель на символ.И, таким образом, разыменование его (т. Е. ** p2) должно дать «H».Но это не так!

Ответы [ 3 ]

11 голосов
/ 28 октября 2011

Ваше неправильное понимание заключается в том, что есть s. Это не указатель: это массив.

Теперь в большинстве контекстов s вычисляет указатель на первый элемент массива: эквивалентно &s[0], указатель на этот 'H'. Здесь важно то, что значение указателя, которое вы получаете при оценке s, является временным, эфемерным значением - так же, как &s[0].

Поскольку этот указатель не является постоянным объектом (на самом деле он не хранится в s), вы не можете указать на него указатель-указатель. Чтобы использовать указатель на указатель, у вас должен быть реальный объект-указатель, на который можно указать - например, все в порядке:

char *p = s;
char **p2 = &p;

Если вы оцениваете *p2, вы говорите компилятору загрузить то, на что указывает p2, и обращаться с ним как с указателем на символ. Это нормально, когда p2 действительно указывает на указатель на символ; но когда вы делаете char **p2 = &s;, то, на что указывает p2, вовсе не является указателем - это массив (в данном случае это блок из 13 char с).

1 голос
/ 28 октября 2011

Вот пример, который работает, плюс распечатки адресов указателей, чтобы упростить просмотр:

#include <stdio.h>
char s[] = "Hello, World";
char (*p1)[] = &s;
char *p2 = (char*)&s;

int main(void)
{
   printf("%x %x %x\n", s, p2, *p2);
   printf("%x\n", &s);    // Note that `s` and `&s` give the same value
   printf("%x\n", &s[0]);
   printf("%c\n", **p1); 
   printf("%c\n", *p2);
}
1 голос
/ 28 октября 2011

From what I understand, 's' is a pointer to the first element of the array
Нет, s это массив. Он может быть уменьшен до указателя на массив, но до этого времени это массив. Указатель на массив становится указателем на первый элемент массива. (да, это немного сбивает с толку.)

char (*p1)[] = &s; Это разрешено, это указатель на массив, назначенный адрес массива. Указывает на первый элемент s.

char **p2 = &s;
Это делает указатель на указатель и присваивает ему адрес массива. Вы назначаете ему указатель на первый элемент s (a char), когда он думает, что это указатель на указатель на один или несколько символов. Разыменование это неопределенное поведение. (segfault в вашем случае)

Доказательство того, что они разные, лежит в sizeof(char[1000]) (возвращает размер 1000 символов, а не размер указателя) и работает следующим образом:

template<int length>
void function(char (&arr)[length]) {}

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

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