Чаму гэта паказальнік на паказальнік, несумяшчальны з паказальнікам на масіў?

Добра, у мяне ўзнікаюць праблемы з разуменнем паказальнікаў на паказальнікі супраць паказальнікаў на масівы. Разгледзім наступны код:

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, як сімвал ** азначае, што ён з'яўляецца паказальнікам на паказальнік на знак. Выраб яна паказвала на «s» павінна быць законным, бо «s» з'яўляецца паказальнікам на знак. І, такім чынам, разнаймення яго (г.зн. ** p2) павінна даць «H». Але гэта не так!

12
@Meta: Ці не на GCC 4.3.4 ( дэма ) або 4.5.1 ( дэма ) ...
дададзена аўтар ildjarn, крыніца
@Meta: Ах, ваш код сапраўдны C, але не дзейнічае C ++; клавішы C ++ Тэг кідае ўсё, таму я выдаліў яго.
дададзена аўтар ildjarn, крыніца
Ні адно з заданняў ня кампіляваць на VC ++ 2010.
дададзена аўтар Jon, крыніца
Дзіўна. Ён выдатна працуе на GCC 4.4.4.
дададзена аўтар Meta, крыніца

3 адказы

Ваша няправільна зразумець ляжыць у чым S гэта. Гэта <�ет> не паказальнік: гэта масіў.

Now in most contexts, s evaluates to a pointer to the first element of the array: equivalent to &s[0], a pointer to that 'H'. The important thing here though is that that pointer value you get when evaluating s is a temporary, ephemeral value - just like &s[0].

Таму што паказальнік не з'яўляецца сталым аб'ектам (гэта на самай справе не тое, што захоўваецца ў s ), вы не можаце зрабіць кропку паказальнік на паказальнік на яго. Каб выкарыстоўваць паказальнік на паказальнік, вы павінны мець рэальны аб'ект паказальнік, каб паказаць на - напрыклад, наступныя ў парадку:

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

If you evaluate *p2, you're telling the compiler to load the thing that p2 points to and treat it as a pointer-to-char. That's fine when p2 does actually point at a pointer-to-char; but when you do char **p2 = &s;, the thing that p2 points to isn't a pointer at all - it's an array (in this case, it's a block of 13 chars).

11
дададзена
@caf, калі s не з'яўляецца адрасаванай аб'ектам, як прыйсці & в е ня памылка кампіляцыі (але & (а + 1) ёсць) ?
дададзена аўтар Shahbaz, крыніца
@caf, яна пачынае мець сэнс. Імя масіва, як імя функцыі, і з ён дае свой адрас, але так як без і гэта не мае сэнсу, без і яго таксама дае адрас! Сапраўды гэтак жа FUNC і і Функ будзе мець тое ж значэнне. Па гэтай жа прычыне сімвал (* р) [] варта выкарыстоўваць, падобна паўкокс (* fptr)() . Я правільна?
дададзена аўтар Shahbaz, крыніца
@caf, ён атрымаў;)
дададзена аўтар Shahbaz, крыніца
@Meta: Я маю на ўвазе, што паказальнік, што s прымае значэнне не з'яўляецца адрасаванай аб'ектам, такім жа чынам, што а + 1 ня (у standardese, гэта не імянныя ).
дададзена аўтар caf, крыніца
@Shahbaz: s <�я> з'яўляецца </я> адрасаваных аб'ект - гэта масіў - але значэнне паказальніка, што s прымае значэнне <�я> у большасці кантэкстаў </я> не з'яўляецца адрасаванай аб'ект. Бывае так, што & s гэта адзін з кантэкстаў, дзе s гэта <�я> не </я> вылічвацца паказальнік на першы элемент - ён застаецца целеуказатель для масіў, таму & s дае адрас гэтага масіва. Іншы кантэкст, дзе s яшчэ пазначае сам масіў з'яўляецца SizeOf s (так што гэта дае памер масіў , а не паказальніка).
дададзена аўтар caf, крыніца
@Shahbaz: Гэта вельмі падобна на сітуацыю з функцыямі, але не ідэнтычныя. Розніца заключаецца ў тым, што функцыя, як абазначэнне Func прымае значэнне паказальніка на функцыю, якая мае той жа тып і значэнне, як & Func . У выпадку масіваў, імя масіва, як S прымае значэнне паказальніка на <�я> першы элемент з масіва, у той час як & в е з'яўляецца паказальнікам < я> у масіў . Цяпер першы элемент масіва абавязкова знаходзіцца ў пачатку масіва, так што s і & в е маюць аднолькавае значэнне (напрыклад. Пры пераўтварэнні ў несапраўдным * ), але яны маюць розныя <�я> тыпу .
дададзена аўтар caf, крыніца
Ах ОК. Я думаю, што я пачынаю разумець, у цяперашні час. Вы можаце проста прыбраць частка аб s быць 'часовым, эфемернае значэнне? Не было б той жа адрас кожны раз?
дададзена аўтар Meta, крыніца

From what I understand, 's' is a pointer to the first element of the array
No, s is an array. It can be reduced to a pointer to an array, but until such time, it is an array. A pointer to an array becomes a pointer to the first element of the array. (yeah, it's kinda confusing.)

char (*p1)[] = &s; This is allowed, it's a pointer to an array, assigned the address of an array. It points to the first element of s.

char **p2 = &s;
That makes a pointer to a pointer and assigns it the address of the array. You assign it a pointer to the first element of s (a char), when it thinks it's a pointer to a pointer to one or more chars. Dereferencing this is undefined behavior. (segfault in your case)

Доказ таго, што яны адрозніваюцца ад іншых заключаецца ў SizeOf (Char [1000]) (вяртае памер 1000 знакаў, а не памер паказальніка), а таксама такія функцыі, як гэта:

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

які будзе збіраць, калі дадзены масіў, але не паказальнік.

1
дададзена

Вось прыклад, які працуе, а таксама раздрукоўкі паказальнікаў адрасоў, каб зрабіць рэчы простымі, каб убачыць:

#include 
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
дададзена
Я дадаў адзін радок у код, што робіць яго трохі ясней, чаму гэта адбываецца.
дададзена аўтар Shahbaz, крыніца