Matrice di lunghezza zero

Sto lavorando sul refactoring del vecchio codice e ho trovato poche strutture contenenti array di lunghezza zero (sotto). Avvertimenti depressi dal pragma, ovviamente, ma non sono riuscito a creare con “nuove” strutture contenenti tali strutture (errore 2233). Array ‘byData’ usato come puntatore, ma perché non usare il puntatore, invece? o array di lunghezza 1? E, naturalmente, non sono stati aggiunti commenti per farmi godere il processo … Qualche causa per usare questa cosa? Qualche consiglio nel refactoring di quelli?

struct someData { int nData; BYTE byData[0]; } 

NB: è C ++, Windows XP, VS 2003

Sì, questo è un C-Hack.
Per creare un array di qualsiasi lunghezza:

 struct someData* mallocSomeData(int size) { struct someData* result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE)); if (result) { result->nData = size; } return result; } 

Ora hai un object di alcuni dati con una matrice di una lunghezza specificata.

Sfortunatamente ci sono diversi motivi per cui dichiarare una matrice di lunghezza zero alla fine di una struttura. In sostanza ti dà la possibilità di avere una struttura a lunghezza variabile restituita da un’API.

Raymond Chen ha fatto un eccellente post sul blog sull’argomento. Ti suggerisco di dare un’occhiata a questo post perché probabilmente contiene la risposta che desideri.

Nota nel suo post, si occupa di matrici di dimensioni 1 invece di 0. Questo è il caso perché gli array di lunghezza zero sono un’entrata più recente negli standard. Il suo post dovrebbe ancora essere applicato al tuo problema.

http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx

MODIFICARE

Nota: anche se il post di Raymond dice che gli array di lunghezza 0 sono legali in C99, in realtà non sono ancora legali in C99. Invece di un array di lunghezza 0, dovresti utilizzare un array di lunghezza 1

Questo è un vecchio trucco per consentire un array di dimensioni flessibili.

Nello standard C99 questo non è necessario in quanto supporta la syntax arr [].

La tua intenzione su “perché non utilizzare un array di dimensioni 1” è azzeccata.

Il codice sta facendo “C struct hack” sbagliato, perché le dichiarazioni di array di lunghezza zero sono una violazione del vincolo. Ciò significa che un compilatore può respingere il tuo hack immediatamente dopo la compilazione con un messaggio diagnostico che interrompe la traduzione.

Se vogliamo perpetrare un hack, dobbiamo farlo passare oltre il compilatore.

Il modo giusto per fare “C struct hack” (che è compatibile con i dialetti C risalenti al ANSI C del 1989, e probabilmente molto prima) è usare una matrice perfettamente valida di dimensione 1:

 struct someData { int nData; unsigned char byData[1]; } 

Inoltre, invece di sizeof struct someData , la dimensione della parte precedente di byData viene calcasting utilizzando:

 offsetof(struct someData, byData); 

Per allocare una struct someData con spazio per 42 byte in byData , utilizzeremmo:

 struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42); 

Si noti che questo offsetof calcolo è in realtà il calcolo corretto anche nel caso in cui la dimensione dell’array sia zero. Vedete, la dimensione dell’intera struttura può includere il riempimento. Ad esempio, se abbiamo qualcosa di simile:

 struct hack { unsigned long ul; char c; char foo[0]; /* assuming our compiler accepts this nonsense */ }; 

La dimensione della struct hack è molto probabilmente imbottita per l’allineamento a causa del membro di ul . Se unsigned long una larghezza di quattro byte, allora possibilmente sizeof (struct hack) è 8, mentre offsetof(struct hack, foo) è quasi certamente 5. Il metodo offsetof è il modo per ottenere la dimensione esatta della parte precedente della struct poco prima della matrice.

In tal modo sarebbe il modo di ridefinire il codice: renderlo conforms al classico, altamente portatile, struct hack.

Perché non usare un puntatore? Perché un puntatore occupa spazio extra e deve essere inizializzato.

Ci sono altri buoni motivi per non usare un puntatore, e cioè che un puntatore richiede uno spazio di indirizzamento per essere significativo. La struct hack è esternalizzabile: vale a dire, ci sono situazioni in cui tale layout è conforms alla memoria esterna come aree di file, pacchetti o memoria condivisa, in cui non si vogliono puntatori perché non sono significativi.

Diversi anni fa, ho usato la struct hack in un messaggio di memoria condivisa che passava l’interfaccia tra il kernel e lo spazio utente. Non volevo dei puntatori lì, perché sarebbero stati significativi solo nello spazio degli indirizzi originale del processo che generava un messaggio. La parte del kernel del software aveva una vista della memoria usando la propria mapping a un indirizzo diverso, e quindi tutto era basato su calcoli di offset.

Vale la pena indicare IMO il modo migliore per eseguire il calcolo delle dimensioni, che viene utilizzato nell’articolo Raymond Chen collegato in precedenza.

 struct foo { size_t count; int data[1]; } size_t foo_size_from_count(size_t count) { return offsetof(foo, data[count]); } 

L’offset della prima voce alla fine dell’allocazione desiderata è anche la dimensione dell’allocazione desiderata. IMO è un modo estremamente elegante per eseguire il calcolo delle dimensioni. Non importa quale sia il tipo di elemento della matrice di dimensioni variabili. L’offsetof (o FIELD_OFFSET o UFIELD_OFFSET in Windows) è sempre scritto allo stesso modo. Nessuna espressione sizeof () per rovinare accidentalmente.