2011-12-31 3 views
16

ho la seguente strutturaCome inizializzare una struttura con membro di matrice flessibile

typedef struct _person { 
    int age; 
    char sex; 
    char name[]; 
}person; 

Ho fatto qualche ricerca su Internet di base (ma senza successo) su come creare un'istanza e inizializzare una struttura con un membro di matrice flessibile senza usare malloc().

Ad esempio: per strutture normali come

struct a { 
    int age; 
    int sex; 
}; 

Possiamo creare un'istanza di struct a e inizializzarlo come

struct a p1 = {10, 'm'}; 

Ma per strutture con matrice flessibile in esso (come _person come menzionato sopra) come possiamo creare un'istanza e inizializzarla come lo facciamo per il normale structures?

È possibile? In tal caso, come si passa la dimensione dell'array durante l'inizializzazione e il valore effettivo da inizializzare?

(o)

E 'vero che l'unico modo per creare una struttura con matrice flessibile sta usando malloc() come indicato nella specifica C99 - 6.7.2.1 Structure and union specifiers - point #17?!

+1

impossibile, struct deve avere dimensioni di compilazione. – Anycorn

+6

@Anycorn: le strutture con membri di array flessibili hanno una dimensione temporale di compilazione. –

+3

GCC ha un'estensione che ti permette di fare qualcosa come 'struct {size_t len; dati int []; } x = {4, {1, 2, 3, 4}}; 'e funzionerà, ma non è portatile. Puoi sempre cercare nella versione della piattaforma di 'alloca' una soluzione probabilmente più portabile, ma dovresti assicurarti che tutti si comportino allo stesso modo e abbiano le stesse stranezze di implementazione. –

risposta

8

No, gli array flessibili devono sempre essere assegnati manualmente. Ma è possibile utilizzare calloc per inizializzare la parte flessibile e un valore letterale composto per inizializzare la parte fissa. Mi piacerebbe che in un avvolgo inline funzione di allocazione in questo modo:

typedef struct person { 
    unsigned age; 
    char sex; 
    size_t size; 
    char name[]; 
} person; 

inline 
person* alloc_person(int a, char s, size_t n) { 
    person * ret = calloc(sizeof(person) + n, 1); 
    if (ret) memcpy(ret, 
        &(person const){ .age = a, .sex = s, .size = n}, 
        sizeof(person)); 
    return ret; 
} 

Si osservi che questo lascia il controllo se l'assegnazione è riuscita al chiamante.

Se non è necessario un campo size come ho incluso qui, una macro sarebbe addirittura sufficiente. Solo che non sarebbe possibile verificare la restituzione di calloc prima di eseguire lo memcpy. Sotto tutti i sistemi che ho programmato finora, questo si annullerà in modo relativamente gradevole. Generalmente penso che sia return of malloc is of minor importance, ma le opinioni variano ampiamente su quell'argomento.

Questo potrebbe forse (in questo caso particolare) danno maggiori opportunità di l'ottimizzatore di integrare il codice nei dintorni:

#define ALLOC_PERSON(A, S, N)         \ 
((person*)memcpy(calloc(sizeof(person) + (N), 1),    \ 
       &(person const){ .age = (A), .sex = (S) },  \ 
       sizeof(person))) 

Edit: Il caso che questo potrebbe essere migliore di funzione è quando A e S sono costanti di tempo di compilazione.In tal caso il composto letterale, dal momento che è qualificato const, potrebbe essere allocato staticamente e la sua inizializzazione potrebbe essere eseguita in fase di compilazione. Inoltre, se nel codice venissero visualizzate più allocazioni con gli stessi valori, al compilatore sarebbe permesso di realizzare solo una singola copia di quel composto letterale.

+1

Fare una copia deselezionata del risultato di 'calloc()' è pericoloso; se l'allocazione fallisce, si ottiene un core dump (o un altro comportamento indefinito che è improbabile che sia quello che volevi). –

+0

@JonathanLeffler, a destra, verrà modificato. Lo integrerò nella funzione. Per il marcro farò semplicemente riferimento al mio rant generico sul controllo del ritorno di 'malloc'. –

+0

La tua soluzione di funzione inline è eccezionale. La macro non ha alcun vantaggio su di essa, IMHO. È illeggibile, non controlla il valore di ritorno calloc e non funzionerà meglio. Le macro generalmente non funzionano meglio delle funzioni inline (a volte peggio - si consideri di passare strlen() a una macro, che la valuta due volte). – ugoren

2

Un tipo di struttura con un membro di matrice flessibile può essere trattato come se il membro di matrice flessibile fosse omesso, quindi è possibile inizializzare la struttura in questo modo.

person p = { 10, 'x' }; 

Tuttavia, non ci sono membri della matrice flessibile assegnato e qualsiasi tentativo di accedere a un membro della matrice flessibile o formare un puntatore a una oltre la sua estremità è valido. L'unico modo per creare un'istanza di una struttura con un membro di matrice flessibile che abbia effettivamente elementi in questo array è allocare dinamicamente memoria per esso, ad esempio con malloc.

+2

C'è un'estensione GCC che ti permette di specificare il membro della matrice flessibile usando la stessa sintassi, se è quello che ti interessa. –

4

Ci sono alcuni trucchi che è possibile utilizzare. Dipende dalla tua particolare applicazione.

Se si desidera inizializzare una singola variabile, è possibile definire una struttura di dimensioni corrette:

struct { 
     int age; 
     char sex; 
     char name[sizeof("THE_NAME")]; 
    } your_variable = { 55, 'M', "THE_NAME" }; 

Il problema è che si deve utilizzare il puntatore casting per interpretare la variabile come "persona" (ad esempio, . "* (persona *) (& your_variable)" Ma è possibile utilizzare un sindacato che contiene per evitare questo:.

union { 
struct { ..., char name[sizeof("THE_NAME")]; } x; 
person p; 
} your_var = { 55, 'M', "THE_NAME" }; 

così, your_var.p è di tipo "persona" si può anche usare una macro per definisci il tuo inizializzatore, così puoi scrivere la stringa o n una volta:

#define INIVAR(x_, age_, sex_ ,s_) \ 
    union {\ 
    struct { ..., char name[sizeof(s_)]; } x;\ 
    person p;\ 
    } x_ = { (age_), (sex_), (s_) } 

INIVAR(your_var, 55, 'M', "THE NAME"); 

Un altro problema è che questo trucco non è adatto per creare un array di "persona". Il problema con gli array è che tutti gli elementi devono avere la stessa dimensione. In questo caso è più sicuro utilizzare const char * anziché char[]. Oppure utilizzare l'allocazione dinamica;)