2010-07-06 13 views
13

Sono curioso di sapere qual è la differenza quando si digita un enum o una struttura. C'è qualche differenza semantica tra questi due blocchi?Quali sono le differenze tra questi due stili typedef in C?

questo:

typedef enum { first, second, third } SomeEnum; 

e questo:

enum SomeEnum { first, second, third }; 
typedef enum SomeEnum SomeEnum; 

Stessa cosa per le strutture. Ho visto entrambi in uso, ed entrambi sembrano fare la stessa cosa in C o Objective-C. C'è una vera differenza o è solo una preferenza per quale stile puoi usare?

risposta

15

La differenza è che il secondo approccio dichiara un tipo denominato enum SomeEnum e dichiara anche un nome typedef SomeEnum - un alias per quel tipo. Si può effettivamente essere combinati in equivalente one-liner

typedef enum SomeEnum { first, second, third } SomeEnum; 

che rende piuttosto evidente che l'unica differenza tra i due approcci è se c'è un nome dopo la parola chiave enum. Con il secondo approccio, puoi dichiarare l'oggetto di quel tipo di enum utilizzando SomeEnum e o enum SomeEnum e, a seconda di quale preferisci.

Il primo approccio dichiara solo il nome typedef SomeEnum per un tipo enum originariamente anonimo, il che significa che si è limitati alle dichiarazioni SomeEnum e.

Quindi, se si utilizza solo il nome typedef SomeEnum nelle dichiarazioni, non vi sarà alcuna differenza tra i due. Tuttavia, in alcuni casi potrebbe essere necessario utilizzare il nome originale completo del tipo enum SomeEnum. Nel primo approccio quel nome non è disponibile, quindi sarai sfortunato.

Per esempio, se dopo la dichiarazione di cui sopra si dichiara anche una variabile denominata SomeEnum in qualche ambito nidificato

int SomeEnum; 

il nome della variabile nasconderà il typedef-name della enum, rendendo così questa dichiarazione illegale

SomeEnum e; /* ERROR: `SomeEnum` is not a type */ 

Tuttavia, se è stato utilizzato il secondo approccio quando si dichiara il vostro enum, è possibile aggirare il problema utilizzando il nome di tipo completo

enum SomeEnum e; /* OK */ 

Questo non sarebbe possibile se si usasse il primo approccio quando si dichiara il tipo di enum.

Quando viene utilizzato con le strutture, il nome dopo il struct è un must quando si ha bisogno di un tipo di auto-riferimento (un tipo che contiene un puntatore allo stesso tipo), come

typedef struct SomeStruct { 
    struct SomeStruct *next; 
} SomeStruct; 

Infine, nella secondo approccio il nome typedef è totalmente opzionale. Si può semplicemente dichiarare

enum SomeEnum { first, second, third }; 

e basta usare enum SomeEnum ogni volta che è necessario fare riferimento a questo tipo.

7

Sì, c'è una differenza semantica. Il secondo snippet dichiara un identificatore di tag, ma il primo no. Entrambi dichiarano un identificatore ordinario.

Ciò significa che per la prima, questo codice non è valido, ma per il secondo, che è:

enum SomeEnum foo; 

Per quanto ne so, non c'è altra differenza semantica tra loro nel codice. Per le strutture e sindacati, la seconda forma, forse in combinazione con il typedef in una dichiarazione, è necessario per i tipi ricorsivi

typedef struct node { 
    struct node *parent; // refer to the tag identifier 
} node; 

L'identificatore ordinaria non è ancora visibile nella specificazione del struct, e quindi è necessario fare riferimento alla struct dall'identificatore di tag già dichiarato. Gli identificatori di tag sono indicati anteponendoli con "struct", "union" o "enum", mentre gli identificatori ordinari sono riferiti senza prefisso (quindi il nome "ordinario").

Oltre che separa gli identificatori che si riferiscono a struct, i sindacati e le enumerazioni da quelle che si riferiscono a valori, identificatori tag sono utili anche per la creazione in avanti dichiarazioni:

/* forward declaration */ 
struct foo; 

/* for pointers, forward declarations are entirely sufficient */ 
struct foo *pfoo = ...; 

/* ... and then later define its contents */ 
struct foo { 
    /* ... */ 
}; 

Typedef nomi non può essere dichiarato più volte al stesso ambito (al contrario di C++, dove possono), e hanno bisogno di fare riferimento a un tipo esistente, in modo che non possano essere utilizzati per creare dichiarazioni in avanti.

4

L'unica vera differenza è che nel secondo caso, si può usare qualcosa come:

enum SomeEnum x; 

mentre il primo unica supporta:

SomeEnum x; 

Per persone che sono state scrivendo C lungo tempo, definendo un struct senza la parola chiave struct spesso "si sente" strano ...

2

Il primo modulo crea un tipo anonimo enum e crea un alias SomeEnum.

Il secondo modulo crea sia un tipo enum SomeEnum sia un alias SomeEnum.

(in C ci sono spazi dei nomi separati per i tipi. struct Foo è diverso da enum Foo diverso da .)

Questo è più importante per struct s rispetto enum s dal momento che avresti bisogno di usare la seconda forma se il struct erano auto-referenziale. Ad esempio:

struct LinkedListNode 
{ 
    void* item; 
    struct LinkedListNode* next; 
}; 

typedef struct LinkedListNode LinkedListNode; 

Quanto sopra non sarebbe possibile con il primo modulo.

0

Per struct s c'è una vera differenza che non riguarda semplicemente la denominazione.

questo è valido C:

struct SomeEnum { struct SomeEnum *first; }; 

Questo non è:

typedef struct { SomeEnum *first; } SomeEnum; 
0

Aggiunta al commento di user207442, è possibile per un modulo di codice sorgente per dichiarare variabili di tipo "struct foo *" senza sempre con una definizione per la struct. Un tale modulo non sarà in grado di dereferenziare tali indicatori, ma potrebbe passarli da e verso altri moduli.

Ad esempio, si potrebbe avere un file di intestazione definire un tipo "USERCONSOLE" utilizzando "typedef struct _USERCONSOLE * USERCONSOLE;". Codice che # include che il file di intestazione possa avere variabili di tipo USERCONSOLE e passa tali variabili a/da moduli che sanno cosa sia veramente un _USERCONSOLE, senza che il file di intestazione debba esporre la definizione effettiva della struttura.

Problemi correlati