2015-12-15 21 views
8

Penso che nascondere la definizione della struttura renda il codice più sicuro mentre si applica con l'aiuto del compilatore che nessun membro della struttura può accedere direttamente. Lo svantaggio è che gli utenti non possono dichiarare le variabili del tipo di struttura sullo stack perché la dimensione della struttura non è nota, mentre a volte è preferibile evitare di utilizzare malloc(). Questo può essere (con un parziale successo) risolto con alloca(3) che è presente in tutte le principali implementazioni di libc, sebbene questa funzione non sia conforme a POSIX. Considerati questi piccoli vantaggi e svantaggi, un tale design può essere considerato generalmente buono?È buona norma nascondere la definizione della struttura in C?

In lib.h:

struct foo; 
extern size_t foo_size; 
int foo_get_bar (struct foo *); 

In lib.c:

struct foo { 
    int bar; 
}; 

size_t foo_size = sizeof foo; 

int foo_get_bar (struct foo *foo) 
{ 
    return foo->bar; 
} 

In example.c:

#include "lib.h" 

int bar(void) { 
    struct foo *foo = alloca (foo_size); 
    foo_init (foo); 
    return foo_get_bar (foo); 
} 

UPD: Aggiornamento della questione, affermando esplicitamente che l'idea di usare alloca() deve essere in grado di dichiarare la struttura sullo stack nascondendo la sua definizione.

+4

La scelta più comune è usare qualcosa come 'foo_create' e' foo_destroy', il che significa che non esporre * nessun * dettaglio della tua struttura, e che può fare cose più avanzate come la memorizzazione internamente di 'malloc''d puntatori . Esistono poche e preziose situazioni in cui * in realtà * si desidera utilizzare 'alloca', a parte forse i sistemi incorporati in cui' malloc' e gli amici sono super-limitati. –

+2

Se la struttura è opaca, sarebbe un cattivo progetto avere codice client che ha bisogno di allocare o dichiarare qualsiasi variabile di quel tipo come mostrato nell'esempio. Tutte le istanze della struttura dovrebbero provenire dalla libreria stessa. – kaylum

+3

'VLA []' consentito? (C99)? Dichiarare un array di caratteri di 'foo_size' (usando' alignas') può funzionare. Eppure, in gerneral, sono d'accordo con @kaylum – chux

risposta

3

Sì, è buona norma nascondere i dati.

Alternativo a alloca(foo_size); è quello di dichiarare un array di caratteri allineati ed eseguire la conversione del puntatore.

La conversione del puntatore non è tuttavia completamente portatile.

L'array di caratteri deve essere un VLA se la dimensione deve essere una variabile e non una costante di tempo di compilazione.

extern size_t size; 

struct sfoo; 

#include <stddef.h> 

int main(void) { 
    unsigned char _Alignas (max_align_t) cptr[size]; 
    // or unsigned char _Alignas (_Complex long double) cptr[size]; // some widest type 
    struct sfoo *sfooptr = (struct sfoo *) cptr; 

Se VLA non sono desiderati/disponibili, dichiarare il formato come una costante come #define foo_N 100 che sarà certamente almeno quanto necessario.

+0

Questa idea è interessante, ma difficilmente qualifica come buona pratica. – chqrlie

+0

@chqrlie Se si dettagliano i problemi di non buona pratica, forse possiamo affrontarli. "difficilmente si qualifica come buona pratica". è simile a un OP che dice "il codice non funziona". – chux

+0

ciò che è * buona pratica * è probabilmente basato sull'opinione pubblica. Se il prezzo da pagare per nascondere l'implementazione è difficile da leggere questo aggeggio VLA, personalmente ritengo che non valga la pena. Quello che stai proponendo è un trucco per consentire l'allocazione dello stack, hai intenzione di nasconderlo con una macro? Potrebbe essere considerato * buona pratica *? – chqrlie

2

La funzione bar richiama il comportamento non definito: la struttura puntata da foo non è inizializzata.

Se si desidera nascondere i dettagli della struttura, fornire uno foo_create() che ne assegni uno e inizializzi e foo_finalize che rilasci qualsiasi risorsa e lo liberi.

Ciò che si propone potrebbe essere fatto per funzionare, ma è soggetto a errori e non è una soluzione generale.

+0

Risolto il problema. Come ho detto se viene usato 'foo_create()', allora è impossibile allocare memoria su stack che a volte è desiderabile. –

+0

@AlexanderSolovets In pratica, questo è solitamente indesiderabile. Consentire agli oggetti di essere assegnati e abbattuti in pila non è grandioso; significa che non puoi eseguire alcuna pulizia sull'istanza. – duskwuff

+0

Se si desidera mantenere l'uso di 'alloca', è possibile avere una funzione' foo_init' per inizializzarla. Come chux menzionato nei commenti avresti anche bisogno dell'allineamento della struttura, quindi è molto più facile per l'utente chiamare 'foo_create' e non preoccuparti di nulla. – Kevin

Problemi correlati