Подтвердить что ты не робот

Почему указатель на указатель несовместим с указателем на массив?

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

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 ** означает, что оно является указателем на указатель на char. Приведение его к "s" должно быть законным, поскольку "s" является указателем на char. И, таким образом, разыменование его (т.е. ** p2) должно давать "H". Но это не так!

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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), когда он считает его указателем на указатель на один или несколько символов. Вызов разыменования - это поведение undefined. (segfault в вашем случае)

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

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

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

Ответ 3

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

#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);
}