2013-06-03 15 views
5

Si dice che la matrice di lunghezza zero sia per la struttura di lunghezza variabile, che posso capire. Ma ciò che mi sorprende è il motivo per cui non usiamo semplicemente un puntatore, ma possiamo dereferenziare e allocare una struttura di dimensioni diverse allo stesso modo.Perché utilizziamo array a lunghezza zero anziché puntatori?

EDIT - Aggiunto esempio dai commenti

Assumendo:

struct p 
{ 
    char ch; 
    int *arr; 
}; 

possiamo usare questa:

struct p *p = malloc(sizeof(*p) + (sizeof(int) * n)); 

p->arr = (struct p*)(p + 1); 

Per ottenere un pezzo di memoria contiguo. Tuttavia, mi è sembrato di dimenticare lo spazio p->arr che occupa e sembra essere una cosa disparata dal metodo dell'array di dimensione zero.

+2

Si prega di fornire un esempio. – Arafangion

+1

Precisazioni relative alle strutture. Ci sono tre casi, 'lastMemberOfArray []' in C90, 'lastMemberOfArray []' in C99 e 'lastMemberOfArray [0]' in GNU goo non standard. Nel caso C90, si tratta di un attacco sporco basato su un comportamento indefinito. È possibile che si verifichino problemi se alla fine della struttura sono presenti byte di riempimento della struttura. C99 ha risolto questo problema e ha creato un tipo chiamato _flessibile membro dell'array_, che funziona allo stesso modo ma con un comportamento ben definito. Infine, GNU non standard consente array di dimensioni zero per lo stesso scopo. Compila come standard con '-std = c99 -pedantic-errors' e' [0] 'non verrà compilato. – Lundin

+1

@ Il primo esempio di Lundin avrebbe dovuto essere "' lastMemberOfArray [1] 'in C90". Errore di battitura semplice, aggiungendolo solo a beneficio di chi non ricorda. –

risposta

5

Queste sono varie forme del cosiddetto "struct hack", discusso nella domanda 2.6 dello comp.lang.c FAQ.

La definizione di una matrice di dimensione 0 è in realtà illegale in C, ed è stata almeno dallo standard ANSI del 1989. Alcuni compilatori lo consentono come un'estensione, ma basarsi su ciò porta a un codice non portatile.

Un modo più portabile per implementare questo è quello di utilizzare un array di lunghezza 1, ad esempio:

struct foo { 
    size_t len; 
    char str[1]; 
}; 

Si può allocare più sizeof (struct foo) byte, utilizzando len per tenere traccia della dimensione allocata, e poi accedere a str[N] per ottenere l'ennesimo elemento dell'array. Poiché i compilatori C in genere non eseguono il controllo dei limiti di array, questo in genere "funziona". Ma, in senso stretto, il comportamento non è definito.

La norma ISO 1999 ha aggiunto una funzione denominata "membri di matrice flessibili", destinato a sostituire questo uso:

struct foo { 
    size_t len; 
    char str[]; 
}; 

si può trattare con questi nello stesso modo come il più vecchio trucco struct, ma il comportamento è ben definito. Ma devi fare tutta la contabilità da solo; sizeof (struct foo) ancora non include la dimensione della matrice, per esempio.

È possibile, ovviamente, utilizzare un puntatore invece:

struct bar { 
    size_t len; 
    char *ptr; 
}; 

e questo è un buon approccio alla perfezione, ma ha una semantica diversa. Il vantaggio principale di "struct hack", o di membri di array flessibili, è che l'array è allocato in modo contiguo con il resto della struttura, e si può copiare l'array insieme alla struttura usando memcpy (purché l'obiettivo sia stato adeguatamente assegnato). Con un puntatore, l'array viene assegnato separatamente, il che può essere o non essere esattamente quello che vuoi.

8

Il puntatore non è realmente necessario, quindi costa spazio senza alcun beneficio. Inoltre, potrebbe implicare un altro livello di riferimento indiretto, che non è davvero necessario.

confrontare questi dichiarazioni di esempio, per una matrice integer dinamico:

typedef struct { 
    size_t length; 
    int data[0]; 
} IntArray1; 

e:

typedef struct { 
    size_t length; 
    int *data; 
} IntArray2; 

Fondamentalmente, il puntatore esprime "il primo elemento della matrice è a questo indirizzo, che può essere tutto "che è più generico di quanto sia tipicamente necessario. Il modello desiderato è "il primo elemento dell'array è proprio qui, ma non so quanto sia grande l'array".

Naturalmente, il secondo modulo consente di far crescere l'array senza rischiare che l'indirizzo di "base" (l'indirizzo della struttura IntArray2) cambi, che può essere davvero accurato. Non è possibile farlo con IntArray1, poiché è necessario allocare la struttura di base e gli elementi di dati interi insieme. Trade-off, trade-off ...

+0

Un array non può avere dimensione zero C11 6.7.6.2 – Lundin

12

Se si utilizza un puntatore, la struttura non sarà più di lunghezza variabile: avrà una lunghezza fissa, ma i suoi dati saranno memorizzati in una posizione diversa.

L'idea dietro array a lunghezza zero * consiste nel memorizzare i dati dell'array "in linea" con il resto dei dati nella struttura, in modo che i dati dell'array seguano i dati della struttura in memoria. Puntatore a una regione di memoria allocata separatamente non ti consente di farlo.


* Tali matrici sono noti anche come matrici flessibili; in C99 li si dichiara come element_type flexArray[] anziché element_type flexArray[0], ovvero si scende a zero.

1

Questo perché con un puntatore è necessaria un'assegnazione e un'assegnazione separate.

struct WithPointer 
{ 
    int someOtherField; 
    ... 
    int* array; 
}; 

struct WithArray 
{ 
    int someOtherField; 
    ... 
    int array[1]; 
}; 

per ottenere un 'oggetto' di WithPointer che devi fare:

struct WithPointer* withPointer = malloc(sizeof(struct WithPointer)); 
withPointer.array = malloc(ARRAY_SIZE * sizeof(int)); 

per ottenere un 'oggetto' di WithArray:

struct WithArray* withArray = malloc(sizeof(struct WithArray) + 
              (ARRAY_SIZE - 1) * sizeof(int)); 

Questo è tutto.

In alcuni casi è anche molto utile, o addirittura necessario, avere la matrice in memoria consecutiva; per esempio in pacchetti di protocollo di rete.

+2

Un array non può avere dimensione zero C11 6.7.6.2 – Lundin

+0

@ Lundin Hai ragione e hai corretto il codice qui sopra. Grazie. –

Problemi correlati