İşaretçiye işaretçi neden bir diziye işaretçi ile uyuşmuyor?

Tamam, diziler için işaretçiler vs işaretçiler için işaretçiler anlama konusunda sorun yaşıyorum. Aşağıdaki kodu göz önünde bulundurun:

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

İlk printf çalışması neden, ikincisi ise değil mi?

Anladığım kadarıyla 's', dizinin ilk elemanının bir işaretçisidir ('H'). Yani p2'nin char olarak bildirilmesi ** bunun bir char işaretçisine gösterici olduğu anlamına gelir. 'S' anlamına gelmesi yasal olmalı, çünkü 's' bir char işaretçisidir. Ve böylece onu kaldıran (yani ** p2) 'H' vermelidir. Ama öyle değil!

12
@Meta: GCC 4.3.4'te değil ( demo ) veya 4.5.1 ( demo ) ...
katma yazar ildjarn, kaynak
@Meta: Ah, kodunuz geçerli C fakat geçerli değil C ++; c ++ etiketi herkesi atarken, onu kaldırdım.
katma yazar ildjarn, kaynak
Ödevlerin hiçbiri VC + + 2010'da derlenmemiştir.
katma yazar Jon, kaynak
Garip. GCC 4.4.4 üzerinde çalışıyor.
katma yazar Meta, kaynak

3 cevap

Yanlış anlamanız, s 'un ne olduğuyla ilgilidir. Bir işaretçi değildir : bu bir dizidir.

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].

Bu işaretçi kalıcı bir nesne olmadığı için (aslında s içinde saklanan bir şey değil), bir işaretçi-işaretçi noktası yapamazsınız. İşaretçi-işaretçisi kullanmak için, işaret etmek için gerçek bir işaretçi nesnesine sahip olmanız gerekir - örneğin, aşağıdakiler geçerlidir:

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
katma
@ caf, s adreslenebilir bir nesne değilse & s nasıl derleme hatası değildir ( & (a + 1) ise) ?
katma yazar Shahbaz, kaynak
@ caf, anlam vermeye başlıyor. Dizi adı bir işlev adı gibidir, & , adresini verir, ancak & olmadan & olmadan anlam ifade etmez. ayrıca adresi verir! Aynı şekilde func ve & func aynı değerde olacaktır. Bu yüzden char (* p) [] kullanılmalıdır, char (* fptr)() 'ye benzer. Doğrumuyum?
katma yazar Shahbaz, kaynak
@caf, anladım;)
katma yazar Shahbaz, kaynak
@Meta: s tarafından değerlendirilen işaretçi, a + 1 ile aynı şekilde (standart olarak değil, bir değer değil) adreslenebilir bir nesne değildir. ).
katma yazar caf, kaynak
@Shahbaz: s adreslenebilir bir nesne - bu bir dizidir - ancak s öğesinin çoğu bağlamda olarak değerlendirdiği işaretçi değeri adreslenebilir bir nesne değildir. öğesinin birinci öğeye bir gösterici olarak değerlendirdiği bağlamlardan biridir. dizi, yani & s bu dizinin adresini verir. s öğesinin hala dizilimi tanımladığı diğer bağlam, sizeof s 'dir (bu, dizinin boyutunu verir, bir işaretçi değil).
katma yazar caf, kaynak
@Shahbaz: Fonksiyonları ile durum benzer, ama aynı değil. Aradaki fark, func gibi bir fonksiyon göstericinin, & func ile aynı tür ve değere sahip olan işlevin bir göstergesini değerlendirmesidir. Diziler durumunda, s gibi bir dizi adı dizinin ilk elemanı 'na, & s ' in bir işaretçisidir. i> diziye . Şimdi dizinin ilk elemanı zorunlu olarak dizinin başında bulunur, böylece s ve & s aynı değere sahiptir (örneğin void * 'e dönüştürüldüğünde). ), ancak farklı türleri var.
katma yazar caf, kaynak
Ah tamam. Sanırım şimdi anlamaya başlıyorum. 'Geçici, geçici bir değer' olmakla ilgili kısmı açabilir misiniz? Her seferinde aynı adres olmaz mıydı?
katma yazar Meta, kaynak

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)

Farklı olduklarının kanıtı sizeof (char [1000]) (bir işaretçinin boyutu değil, 1000 karakterlik bir büyüklük) verir ve şöyle çalışır:

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

Bir dizi verildiğinde derleyici, ancak bir işaretçi değil.

1
katma

İşte çalışan örnek, artı şeyleri görmeyi kolaylaştırmak için işaretçi adreslerinin çıktıları:

#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
katma
Kodunuza bir satır ekledim, bu da bunun neden olduğunu biraz daha netleştiriyor.
katma yazar Shahbaz, kaynak