typedef struct vs struct definitions

Sono un principiante nella programmazione in C, ma mi chiedevo quale sia la differenza tra l’uso di typedef quando si definisce una struttura piuttosto che l’uso di typedef. Mi sembra che non ci sia davvero alcuna differenza, realizzano lo stesso.

struct myStruct{ int one; int two; }; 

vs.

 typedef struct{ int one; int two; }myStruct; 

L’idioma comune sta usando entrambi:

 typedef struct X { int x; } X; 

Sono definizioni diverse. Per rendere più chiara la discussione, dividerò la frase:

 struct S { int x; }; typedef struct SS; 

Nella prima riga si sta definendo l’identificatore S all’interno dello spazio dei nomi struct (non nel senso C ++). Puoi usarlo e definire variabili o argomenti di funzione del tipo appena definito definendo il tipo dell’argomento come struct S :

 void f( struct S argument ); // struct is required here 

La seconda riga aggiunge un alias di tipo nello spazio dei nomi globale e consente quindi solo di scrivere:

 void f( S argument ); // struct keyword no longer needed 

Si noti che poiché entrambi gli spazi dei nomi degli identificatori sono diversi, la definizione di S sia nelle strutture che negli spazi globali non è un errore, in quanto non sta ridefinendo lo stesso identificatore, ma piuttosto creando un identificatore diverso in una posizione diversa.

Per rendere più chiara la differenza:

 typedef struct S { int x; } T; void S() { } // correct //void T() {} // error: symbol T already defined as an alias to 'struct S' 

È ansible definire una funzione con lo stesso nome della struttura in quanto gli identificatori vengono mantenuti in spazi diversi, ma non è ansible definire una funzione con lo stesso nome di un typedef come questi identificatori collidono.

In C ++, è leggermente diverso in quanto le regole per individuare un simbolo sono cambiate in modo sottile. C ++ conserva ancora i due diversi spazi identificativi, ma a differenza di C, quando si definisce il simbolo solo all’interno dello spazio identificativo della class, non è necessario fornire la parola chiave struct / class:

  // C++ struct S { int x; }; // S defined as a class void f( S a ); // correct: struct is optional 

Quali cambiamenti sono le regole di ricerca, non dove sono definiti gli identificatori. Il compilatore cercherà la tabella dell’identificatore globale e dopo che S non è stato trovato cercherà S all’interno degli identificatori di class.

Il codice presentato prima si comporta nello stesso modo:

 typedef struct S { int x; } T; void S() {} // correct [*] //void T() {} // error: symbol T already defined as an alias to 'struct S' 

Dopo la definizione della funzione S nella seconda riga, la struct S non può essere risolta automaticamente dal compilatore e per creare un object o definire un argomento di quel tipo è necessario ricorrere nuovamente alla struct keyword:

 // previous code here... int main() { S(); struct S s; } 

struct e typedef sono due cose molto diverse.

La parola chiave struct viene utilizzata per definire o fare riferimento a un tipo di struttura. Ad esempio, questo:

 struct foo { int n; }; 

crea un nuovo tipo chiamato struct foo . Il nome foo è un tag ; è significativo solo quando è immediatamente preceduto dalla parola chiave struct , perché i tag e altri identificatori si trovano in spazi di nomi distinti. (Questo è simile a, ma molto più limitato rispetto al concetto C ++ di namespace s.)

Un typedef , nonostante il nome, non definisce un nuovo tipo; crea semplicemente un nuovo nome per un tipo esistente. Ad esempio, dato:

 typedef int my_int; 

my_int è un nuovo nome per int ; my_int e int sono esattamente dello stesso tipo. Allo stesso modo, data la definizione di struct sopra, puoi scrivere:

 typedef struct foo foo; 

Il tipo ha già un nome, struct foo . La dichiarazione typedef dà allo stesso tipo un nuovo nome, foo .

La syntax consente di combinare una struct e typedef in una singola dichiarazione:

 typedef struct bar { int n; } bar; 

Questo è un idioma comune. Ora puoi fare riferimento a questo tipo di struttura come struct bar o semplicemente come bar .

Si noti che il nome typedef non diventa visibile fino alla fine della dichiarazione. Se la struttura contiene un puntatore a se stesso, si usa la versione struct per riferirsi ad essa:

 typedef struct node { int data; struct node *next; /* can't use just "node *next" here */ } node; 

Alcuni programmatori useranno identificatori distinti per il tag struct e per il nome typedef. Secondo me, non c’è una buona ragione per quello; usare lo stesso nome è perfettamente legale e rende più chiaro che sono dello stesso tipo. Se è necessario utilizzare identificatori diversi, utilizzare almeno una convenzione coerente:

 typedef struct node_s { /* ... */ } node; 

(Personalmente, preferisco omettere il typedef e fare riferimento al tipo come struct bar typedef salva un po ‘di battitura, ma nasconde il fatto che è un tipo di struttura.Se vuoi che il tipo sia opaco, questo può essere un buon Se il codice client si riferirà al membro n per nome, allora non è opaco, è visibilmente una struttura e secondo me ha senso riferirsi ad esso come una struttura, ma molti programmatori intelligenti non sono d’accordo con me su questo punto, preparati a leggere e comprendere il codice scritto in entrambi i casi).

(C ++ ha regole diverse.Data una dichiarazione di struct blah , puoi riferirti al tipo come blah , anche senza typedef.Utilizzare un typedef potrebbe rendere il tuo codice C un po ‘più simile al C ++ se pensi che sia un buon cosa.)

Un’altra differenza non evidenziata è che dare alla struct un nome (ad esempio struct myStruct) consente anche di fornire dichiarazioni anticipate della struttura. Quindi in qualche altro file, potresti scrivere:

 struct myStruct; void doit(struct myStruct *ptr); 

senza dover avere accesso alla definizione. Quello che raccomando è che combini i tuoi due esempi:

 typedef struct myStruct{ int one; int two; } myStruct; 

Questo ti dà la comodità del nome typedef più conciso, ma ti consente comunque di usare il nome completo della struttura se necessario.

In C (non C ++), devi dichiarare le variabili struct come:

 struct myStruct myVariable; 

Per poter utilizzare myStruct myVariable; invece, puoi typedef la struct:

 typedef struct myStruct someStruct; someStruct myVariable; 

È ansible combinare la definizione di struct e typedef in una singola istruzione che dichiara una struct anonima e lo typedef .

 typedef struct { ... } myStruct; 

Se usi struct senza typedef , dovrai sempre scrivere

 struct mystruct myvar; 

È illegale scrivere

 mystruct myvar; 

Se usi typedef non hai più bisogno del prefisso struct .

In C, le parole chiave specificatore di tipo di strutture, unioni ed enumerazioni sono obbligatorie, ovvero devi sempre prefisso il nome del tipo (il suo tag ) con struct , union o enum quando si fa riferimento al tipo.

Puoi eliminare le parole chiave usando un typedef , che è una forma di informazione che si nasconde poiché il tipo effettivo di un object non sarà più visibile quando lo dichiarerai.

Si raccomanda quindi (si veda ad esempio la guida allo stile di codifica del kernel di Linux , Capitolo 5) per farlo solo quando si vuole effettivamente hide queste informazioni e non solo per salvare alcune sequenze di tasti.

Un esempio di quando si dovrebbe usare un typedef sarebbe un tipo opaco che viene sempre usato solo con le funzioni / macro di accesso corrispondenti.

La differenza arriva quando usi la struct .

Il primo modo che devi fare:

 struct myStruct aName; 

Il secondo modo ti consente di rimuovere la struct della parola chiave.

 myStruct aName; 

Non è ansible utilizzare la dichiarazione typedef struct con la typedef struct .

La struct stessa è di tipo anonimo, quindi non hai un nome effettivo da inoltrare dichiarare.

 typedef struct{ int one; int two; } myStruct; 

Una dichiarazione anticipata come questa non funzionerà:

 struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types 

Il typedef , come è con altri costrutti, è usato per dare un tipo di dati ad un nuovo nome. In questo caso è fatto principalmente per rendere il codice più pulito:

 struct myStruct blah; 

vs.

 myStruct blah; 

Il seguente codice crea una struttura anonima con l’alias myStruct :

 typedef struct{ int one; int two; } myStruct; 

Non è ansible fare riferimento senza l’alias perché non si specifica un identificatore per la struttura.

Vedo che alcuni chiarimenti sono in ordine su questo. C e C ++ non definiscono i tipi in modo diverso. In origine il C ++ non era altro che un set aggiuntivo di include sopra C.

Il problema che praticamente tutti gli sviluppatori C / C ++ hanno oggi è: a) le università non stanno più insegnando i fondamenti e b) le persone non capiscono la differenza tra una definizione e una dichiarazione.

L’unica ragione per cui tali dichiarazioni e definizioni esistono è che il linker può calcolare gli offset degli indirizzi nei campi della struttura. Questo è il motivo per cui la maggior parte delle persone scappa con il codice che è in realtà scritto in modo errato – perché il compilatore è in grado di determinare l’indirizzamento. Il problema sorge quando qualcuno cerca di fare qualcosa in anticipo, come una coda, o una lista collegata, o piggying-backing di una struttura O / S.

Una dichiarazione inizia con ‘struct’, una definizione inizia con ‘typedef’.

Inoltre, una struttura ha un’etichetta di dichiarazione in avanti e un’etichetta definita. La maggior parte delle persone non lo sa e usa l’etichetta di dichiarazione diretta come etichetta di definizione.

Sbagliato:

 struct myStruct { int field_1; ... }; 

Hanno appena usato la dichiarazione diretta per etichettare la struttura– quindi ora il compilatore ne è a conoscenza– ma non è un tipo definito reale. Il compilatore può calcolare l’indirizzamento– ma questo non è il modo in cui è stato concepito per essere usato, per ragioni che mostrerò momentaneamente.

Le persone che usano questa forma di dichiarazione, devono sempre mettere ‘struct’ in praticamente ogni riferimento ad esso – perché non è un nuovo tipo ufficiale.

Invece, qualsiasi struttura che non fa riferimento a se stessa, dovrebbe essere dichiarata e definita solo in questo modo:

 typedef struct { field_1; ... }myStruct; 

Ora è un tipo effettivo e, se utilizzato, è ansible utilizzare come “myStruct” senza doverlo anteporre alla parola “struct”.

Se desideri una variabile puntatore a quella struttura, quindi includi un’etichetta secondaria:

 typedef struct { field_1; ... }myStruct,*myStructP; 

Ora hai una variabile puntatore a quella struttura, personalizzata per essa.

DICHIARAZIONE DI AVANTI–

Ora, ecco le cose fantasiose, come funziona la dichiarazione anticipata. Se si desidera creare un tipo che si riferisce a se stesso, come un elenco collegato o un elemento di coda, è necessario utilizzare una dichiarazione di inoltro. Il compilatore non considera la struttura definita fino a quando non arriva al punto e virgola alla fine, quindi è solo dichiarato prima di quel punto.

 typedef struct myStructElement { myStructElement* nextSE; field_1; ... }myStruct; 

Ora, il compilatore sa che sebbene non sappia ancora quale sia l’intero tipo, può comunque fare riferimento a esso usando il riferimento forward.

Si prega di dichiarare e typedef correttamente le vostre strutture. C’è in realtà una ragione.

Con l’ultimo esempio si omette la parola chiave struct quando si utilizza la struttura. Quindi, ovunque nel tuo codice, puoi scrivere:

 myStruct a; 

invece di

 struct myStruct a; 

Ciò consente di risparmiare un po ‘di digitazione e potrebbe essere più leggibile, ma è una questione di gusti