Почему указатель на указатель несовместим с указателем на массив?
Хорошо, мне трудно понять указатели на указатели против указателей на массивы.
Рассмотрим следующий код:
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". Но это не так!
Ответы
Ответ 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);
}