2010-02-01 17 views
7

Ho chiesto un question earlier alla definizione di una struttura utilizzando malloc. Questa è stata la risposta mi è stata data da parte della maggioranza:Definizione di una struttura in C con Malloc

struct retValue* st = malloc(sizeof(*st)); 

stavo mostrando a un amico il mio codice, e c'è venuto a un ostacolo. Qualcuno potrebbe spiegare perché questo codice funziona? Dal mio punto di vista, * st non è stato definito quando lo si sposta, quindi potrebbe esserci qualche tipo di immondizia. Dovrebbe essere malloc(sizeof(struct retValue))

Grazie per qualsiasi aiuto

+0

Hai risposto alla tua domanda. sizeof (struct retValue) è corretto –

+1

Spiacente, la domanda non era "È corretto, o cosa è corretto?" era "Perché funziona?" – Blackbinary

+2

Potrebbe aiutare a capire se si modifica la terminologia. Non stai * definendo * una struttura usando malloc, stai * assegnando * una struttura usando malloc. Stai definendo 'st', che è un puntatore (non una struct). Quel puntatore è già definito e disponibile per l'uso nell'espressione initialiser (sul RHS del segno di uguale), semplicemente non ha un valore, quindi la maggior parte degli usi non sarebbe valida. Questo va bene, però, perché sizeof non usa il valore. –

risposta

19

Sizeof analizza il tipo dell'espressione ad esso assegnata, non valuta l'espressione. Pertanto, è necessario solo accertarsi che le variabili utilizzate nell'espressione siano dichiarate in modo che il compilatore possa dedurne il tipo.

Nell'esempio, st è già dichiarato come pointer-to-struct-retValue. Di conseguenza il compilatore è in grado di dedurre il tipo dell'espressione "* st".

Anche se non sembra che sia già stato dichiarato nel codice, il compilatore si è già preso cura di esso. Tutte le dichiarazioni nel tuo codice vengono spostate all'inizio del blocco in cui si verificano dal compilatore. Supponiamo di scrivere

Un modo per illustrare la conoscenza che è disponibile al compilatore è osservare l'output intermedio che genera. Si consideri questo codice esempio ...

struct retValue {long int a, long int b}; 
... 
printf("Hello World!\n"); 
struct retValue* st = malloc(sizeof(*st)); 

Uso gcc come esempio e teh codice in alto nel principale() funzione test.c, esaminiamo produzione intermedia eseguendo ...

gcc -fdump-tree-cfg test.c 

il compilatore genera il file test.c.022t.cfg - vedere le cose e vedrete

[ ... removed internal stuff ...] 
;; Function main (main) 

Merging blocks 2 and 3 
main (argc, argv) 
{ 
    struct retValue * st; 
    int D.3097; 
    void * D.3096; 

    # BLOCK 2 
    # PRED: ENTRY (fallthru) 
    __builtin_puts (&"Hello World!"[0]); 
    D.3096 = malloc (16); 
    st = (struct retValue *) D.3096; 
    D.3097 = 0; 
    return D.3097; 
    # SUCC: EXIT 

} 

Nota come la dichiarazione è stata spostata all'inizio del blocco e l'argomento su malloc è già stato sostituito con il valore effettivo che indica la dimensione del tipo a cui è stata valutata l'espressione. Come sottolineato nei commenti, il fatto che la dichiarazione sia stata spostata in cima al blocco è un dettaglio di implementazione del compilatore. Tuttavia, il fatto che il compilatore sia in grado di farlo e anche di inserire la dimensione corretta nel malloc mostra che il compilatore è stato in grado di dedurre le informazioni necessarie dall'input.

Personalmente preferisco dare il nome del tipo effettivo come parametro a sizeof, ma probabilmente è una questione di stile di codifica in cui direi che la coerenza supera la preferenza personale.

+1

La sua domanda riguardava '' St' facendo "cose ​​cattive". '* st' non è valido se' st' non punta a dati validi. Ma poiché 'sizeof' non valuta il suo argomento, va bene usare' * st' come un operando su 'sizeof', anche se' st' è 'NULL' per esempio. –

+0

In realtà, la domanda era "come/perché funziona nonostante il fatto che st non possa essere dereferenziato al momento che malloc abbia bisogno di sapere quanto spazio allocare" ... – VoidPointer

+1

Inserire una dichiarazione all'inizio del blocco nel parsed la versione del codice è un dettaglio di implementazione. Il compilatore non è tenuto a farlo, anche se ciò accade, e il codice del questionario funziona indipendentemente dal fatto che il compilatore lo faccia o meno. Il seguente codice non viene compilato, a indicare che la dichiarazione non è "realmente" spostata: 'sizeof (* st); char * st = 0; '. –

19

Il sizeof operatore in realtà non valutare la sua operando - sembra proprio il tipo. Questo viene fatto in fase di compilazione piuttosto che in fase di esecuzione. Quindi può essere tranquillamente eseguito prima che la variabile sia stata assegnata.

+0

Penso di aver capito. Quindi il tuo 'sizeof' dice a * st e dice 'Oh questo è un puntatore!' e quindi assegna abbastanza memoria per un puntatore. A lui non importa cosa st sia in realtà in mano. Destra? – Blackbinary

+5

No, è il contrario. '* st' significa" la cosa che st indica ", quindi il compilatore restituisce la dimensione della struttura, non la dimensione del puntatore. –

+3

@Blackbinary: Close: 'st' è un puntatore, ma' * st' è una struttura. Quindi guarda '* st' e dice" Oh questa è una 'struct retValue'!" e quindi alloca memoria sufficiente per una struttura retValue. I contenuti effettivi di '* st' non contano. – interjay

1

Ciò che conta è la dichiarazione/definizione del tipo di struttura e non la definizione di un oggetto di tale classe. Nel momento in cui raggiungi lo malloc, una dichiarazione/definizione sarà stata rilevata dal compilatore, altrimenti avresti riscontrato un errore del compilatore.

Il fatto che sizeof non valuti i suoi operandi è un problema secondario.

A nit minore: ricordiamo che abbiamo bisogno parentesi quando forniamo i nomi dei tipi di sizeof come in:

sizeof(struct retValue); 

e non in caso di oggetti, semplicemente facciamo:

sizeof *st; 

Vedere lo standard:

6.5 .3 operatori unari sintassi

unary-expression: 
[...] 
sizeof unary-expression 
sizeof (type-name) 
0

In C, sizeof è un operatore, e non valuta la sua tesi. Ciò può portare a effetti "interessanti", che qualcuno di nuovo in C non necessariamente anticipa. L'ho menzionato in modo più dettagliato in my answer nella domanda "Strangest language feature".