2012-02-17 26 views
13

Come esperto non C/C++ ho sempre considerato parentesi quadre e matrici di puntatori come uguali.Qual è la differenza tra array di parentesi quadre e array di puntatori?

cioè:

char *my_array_star; 
char my_array_square[]; 

Ma ho notato che quando l'uso in una struttura/classe tuttavia non si comportano allo stesso:

typedef struct { 
    char whatever; 
    char *my_array_star; 
} my_struct_star; 

typedef struct { 
    char whatever; 
    char my_array_square[]; 
} my_struct_square; 

La riga sotto visualizza 16, whatever prende 1 byte, my_array_pointer richiede 8 byte. causa l'imbottitura la dimensione totale struttura è 16.

printf("my_struct_star: %li\n",sizeof(my_struct_star)); 

La riga sotto visualizza 1, whatever richiede 1 byte, my_array_pointer non viene preso in considerazione.

printf("my_struct_square: %li\n",sizeof(my_struct_square)); 

Giocando intorno ho notato che le parentesi quadre sono usate come spazio in più nella struttura

my_struct_square *i=malloc(2); 

i->whatever='A'; 
i->my_array_square[0]='B'; 

il colpo riga mostra A:

printf("i[0]=%c\n",((char*)i)[0]); 

la linea colpo display B:

printf("i[1]=%c\n",((char*)i)[1]); 
Così ho

non posso dire più che le parentesi quadre sono uguali ai puntatori. Ma mi piacerebbe capire la ragione di questo comportamento. Ho paura di perdere un concetto chiave di quelle lingue.

+0

In C non è possibile dichiarare 'qualcosa []' e si deve definire la dimensione dell'array. Scegli tra i due. – Eregrith

+1

@Eregrith: tu * puoi * dichiarare tale array alla fine di una struct, ed è per questo che compila. –

+0

Continuerò a usare il puntatore per gli array creati dinamicamente e non mi preoccupo. Non mi piace quell'altra sintassi, mi sembra offensivo. – evanmcdonnal

risposta

23

Gli array e i puntatori non si comportano allo stesso modo perché sono non lo stesso, sembra proprio così.

Le matrici sono un gruppo di elementi contigui mentre un puntatore è ... beh ... un puntatore a un articolo singolo .

Questo singolo articolo venga indicata potrebbe essere il primo di una matrice in modo che sia possibile accedere anche gli altri, ma il puntatore stesso non sa e non si preoccupa che.

La ragione per cui gli array e i puntatori sembrano spesso identici è che, in molti casi, un array decadrà in un puntatore al primo elemento di quell'array.

Uno dei posti in cui questo si verifica è nelle chiamate di funzione. Quando si passa una matrice a una funzione, questa viene decaduta in un puntatore. Ecco perché cose come la dimensione di un array non passano esplicitamente alla funzione. Con questo voglio dire:

#include <stdio.h> 

static void fn (char plugh[]) { 
    printf ("size = %d\n", sizeof(plugh)); // will give char* size (4 for me). 
} 

int main (void) { 
    char xyzzy[10]; 
    printf ("size = %d\n", sizeof(xyzzy)); // will give 10. 
    fn (xyzzy); 

    return 0; 
} 

L'altra cosa troverete è che, mentre è possibile plugh++ e plugh-- ai vostri cuori contenuto (a patto che non lo fai dereference al di fuori della matrice), si puo' Lo faccio con l'array xyzzy.

Nelle tue due strutture, c'è una grande differenza. Nella versione puntatore, è presente un puntatore a dimensione fissa all'interno della struttura, che punta a un articolo all'esterno dello della struttura.

Ecco perché occupa uno spazio - il puntatore di 8 byte è allineato a un limite di 8 byte come segue:

+----------------+ 
| 1 char variable| 
+----------------+ 
| 7 char padding | 
+----------------+ 
| 8 char pointer | 
+----------------+ 

Con la matrice "illimitata", avete all'interno della struttura e puoi renderlo grande quanto vuoi - devi solo allocare abbastanza memoria quando crei la variabile. Per default (cioè, secondo l'sizeof), la dimensione è zero:

+----------------+ 
| 1 char variable| 
+----------------+ 
| 0 char array | 
+----------------+ 

tuttavia è possibile allocare più spazio, ad esempio:

typedef struct { 
    char whatever; 
    char my_array_square[]; 
} my_struct_square; 

my_struct_square twisty = malloc (sizeof (my_struct_square) + 10); 

si dà una variabile twisty che ha un carattere whatever e una serie di dieci caratteri chiamati my_array_square.

Questi array non limitati possono apparire solo alla fine di una struttura e possono esserne solo uno (altrimenti il ​​compilatore non avrebbe idea di dove questa sezione di lunghezza variabile sia stata iniziata e terminata) e sono specificamente per consentire array di dimensioni arbitrarie in la fine delle strutture.

+0

Grazie per questa risposta di alta qualità, complimenti! – Raphael

+0

+1 spiegazione eccellente. Non sono sicuro che gli array di membri non registrati siano addirittura ammessi negli standard. Di solito ricevo un avviso sull'utilizzo di un'estensione non standard se faccio questo. – Kevin

+0

"Array decadenza a semplici puntatori quando passati a una funzione": questo è il motivo per cui preferisco sempre utilizzare la sintassi del puntatore negli argomenti della funzione 'void fn (char * foobar)' sulla sintassi dell'array 'void fn (char foobar [])' – Flimm

5

Il my_array_square membro è quello che viene chiamato un membro di matrice "flessibile". Tali matrici senza una dimensione specificata possono apparire solo alla fine di una struttura e non contribuiscono alle sue dimensioni. L'intento è di allocare manualmente il resto dello spazio per tutti gli elementi di cui hai bisogno. Altrimenti, la dimensione dell'array viene determinata in fase di compilazione.

Il modello di utilizzo di un tale struct potrebbe essere il seguente:

my_struct_square *s = malloc(sizeof(my_struct_square) + 5 * sizeof(char)); 
... 
s->my_array_square[4]; // the last element of the array 

In tutti gli altri casi, la dimensione di un array deve essere noto al momento della compilazione. Anche il tipo di matrice va insieme alle sue dimensioni, ad esempio int a[20] è di tipo int[20], non solo int[].

Inoltre, capire la differenza tra vettori e puntatori è cruciale. @paxdiablo lo ha trattato abbastanza bene.

+0

Grazie per questa grande risposta, perfetta! – Raphael

+0

Si noti che i membri di array flessibili sono consentiti solo in linguaggio C versione C99 o successiva. Se il codice come questo viene tentato in C90 ("ANSI C"), allora credo che si possa finire con degli arresti anomali quando si scrivono i dati sui possibili byte di riempimento che possono essere memorizzati alla fine della struttura, se i dati scritti sono una "trappola" rappresentazione". Per favore correggimi se sbaglio. – Lundin

+0

@Lunding: Immagino che non dovrebbe essere compilato affatto con un compilatore C90. –

Problemi correlati