2011-10-27 26 views
12

OK, ho problemi a capire i puntatori ai puntatori rispetto ai puntatori agli array. Si consideri il seguente codice:Perché un puntatore a un puntatore non è compatibile con un puntatore a un array?

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

Perché il primo lavoro printf, mentre il secondo non lo fa?

Da quello che ho capito, 's' è un puntatore al primo elemento dell'array (cioè 'H'). Quindi dichiarare p2 come char ** significa che è un puntatore a un puntatore a un carattere. Farlo puntare a "s" dovrebbe essere legale, poiché "s" è un puntatore a un carattere. E quindi dereferenziarlo (cioè ** p2) dovrebbe dare "H". Ma non è così!

+1

Nessuna delle assegnazioni compila il VC++ 2010. – Jon

+0

Strano. Funziona perfettamente con GCC 4.4.4. – Meta

+0

@Meta: non su GCC 4.3.4 ([demo] (http://ideone.com/K6D1D)) o 4.5.1 ([demo] (http://ideone.com/gTGhY)) ... – ildjarn

risposta

11

Il tuo equivoco si trova in quello che è s. È non un puntatore: è un array.

Ora nella maggior parte dei contesti, s valuta un puntatore al primo elemento dell'array: equivalente a &s[0], un puntatore a quello 'H'. La cosa importante qui però è che quel valore del puntatore che si ottiene quando si valuta s è un valore temporaneo ed effimero - proprio come &s[0].

Poiché quel puntatore non è un oggetto permanente (non è in realtà ciò che è memorizzato in s), non è possibile creare un punto da puntatore a puntatore. Per utilizzare un puntatore a puntatore, è necessario disporre di un oggetto vero e proprio puntatore per puntare a - per esempio, il seguente è OK:

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

Se si valuta *p2, che stai dicendo al compilatore di caricare la cosa che p2 punti e trattarlo come un puntatore al char. Va bene quando p2 effettivamente punta a un puntatore-a-carattere; ma quando lo fai, char **p2 = &s;, la cosa a cui punta p2 non è un puntatore - è un array (in questo caso, è un blocco di 13 char s).

+0

Ah OK. Penso che sto iniziando a capire ora. Riesci a chiarire la parte di s come "valore temporaneo ed effimero"? Non sarebbe lo stesso indirizzo ogni volta? – Meta

+1

@Meta: intendo che il puntatore a cui 's' valuta non è un oggetto indirizzabile, nello stesso modo in cui' a + 1' non lo è (in standard, non è un lvalue). – caf

+0

@caf, se 's' non è un oggetto indirizzabile, come mai' & s' non è un errore di compilazione (ma '& (a + 1)' è)? – Shahbaz

1

From what I understand, 's' is a pointer to the first element of the array
No, s è un array. Può essere ridotto a un puntatore a un array, ma fino a quel momento, è un array. Un puntatore a un array diventa un puntatore al primo elemento dell'array. (Sì, è un po 'confuso.)

char (*p1)[] = &s; Questo è consentito, è un puntatore a un array, assegnato l'indirizzo di un array. Punta al primo elemento di s.

char **p2 = &s;
Ciò rende puntatore a un puntatore e assegna l'indirizzo dell'array. Assegnagli un puntatore al primo elemento di s (a char), quando pensa che sia un puntatore a un puntatore a uno o più caratteri. Dereferenziare questo è un comportamento indefinito. (Segfault nel tuo caso)

La prova che essi sono diversi sta nella sizeof(char[1000]) (restituisce la dimensione di 1000 caratteri, non la dimensione di un puntatore), e funzioni come questo:

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

che compilazione quando dato un array, ma non un puntatore.

1

Ecco il campione che funziona, oltre a stampe di indirizzi di puntatore per rendere le cose semplici da vedere:

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

Ho aggiunto una riga al codice che rende un po 'più chiaro il motivo per cui ciò accade. – Shahbaz

Problemi correlati