2014-06-15 28 views
54

Ho il seguente codice (di lavoro) in una base di codice esistente, utilizzato in include file condiviso tra C e C++, compilare il MSVC (2010) e Windows DDK:Qual è il significato di `struct X typedef` vs.` typedef struct X`?

struct X { 
    USHORT x; 
} typedef X, *PX; 

E:

enum MY_ENUM { 
    enum_item_1, 
    enum_item_2 
} typedef MY_ENUM; 

per quanto ne so, corretta definizione dovrebbe essere così:

typedef struct { 
    USHORT x; 
} X, *PX; 

c'è qualche scopo per avere il modulo qui sotto? Mi sto perdendo qualcosa?

risposta

56

Il fatto che sia typedef <type> <alias> e <type> typedef <alias> valgono semplicemente deriva dalla definizione della grammatica della lingua.

typedef è classificato come classe di archiviazione specfifier (come static, auto), e il tipo stesso è noto come il tipo specificatore. Dalle definizioni di sintassi nella sezione 6.7 dello standard, vedrete che questi sono liberi di essere scambiati:

declaration: 
    declaration-specifiers init-declarator-list ; 

declaration-specifiers: 
    storage-class-specifier declaration-specifiers 
    type-specifier declaration-specifiers 
    type-qualifier declaration-specifiers 
    function-specifier declaration-specifiers 

init-declarator-list: 
    init-declarator 
    init-declarator-list , init-declarator 

init-declarator: 
    declarator 
    declarator = initializer 

(nota, naturalmente, che questo vale anche per le strutture e per i non-struct, il che significa che double typedef trouble; è valido anche.)

6

Entrambi hanno lo stesso significato. Entrambe queste due forme sono validi:

typedef <existing_type> <new_type> 
<existing_type> typedef <new_type> 

È possibile typedef struct sopra in entrambi i modi:

struct X { 
    USHORT x; 
}typedef X, *PX;  // <existing_type> typedef <new_type> 

o

typedef struct { 
    USHORT x; 
} X, *PX;   // typedef <existing_type> <new_type> 
+2

non dovrebbe sempre essere "typedef "? È un'estensione speciale per le strutture? – Itaypk

+0

@Itaypk Non solo le strutture, come puoi vedere dal tuo esempio di 'enum'. –

+0

@Itaypk; No. Non è un'estensione. Vedi la modifica. – haccks

5

In realtà è possibile inserire tutti gli identificatori di dichiarazione nell'ordine desiderato! Le posizioni di qualsiasi puntatore * e il dichiaratore effettivo (la variabile o il nuovo tipo nome) contano, ma tutto quello typedefintunsignedconststatic ecc. Roba può essere in qualsiasi ordine.

Se si guarda la grammatica ufficiale di C, si dice semplicemente:

declaration: 
    declaration-specifiers init-declarator-list ; 

Il declaration-specifiers sono tutti gli identificatori di classe di memorizzazione (typedef, extern, ecc), specificatori di tipo (il tipo effettivo, come int o struct X), tipo qualificatori (const e volatile) e alcuni altri meno comuni. Il loro ordine non è importante La seconda parte è il init-declarator-list, ed è il nome della variabile o del nuovo tipo (nel caso di typedef), qualsiasi carattere *, l'inizializzazione della variabile (int x = 3) e altro. L'ordine delle cose nella parte dichiaratore è importante, ma non l'ordine negli specificatori di dichiarazione.

18

Come altri hanno detto, typedef è un identificatore di classe di archiviazione e come con altri identificatori di classe di archiviazione è anche possibile inserire lo specificatore tra il tipo e il dichiaratore.

Anche se questo è valido ed è anche una forma che dovrebbe essere evitato come C contrassegnato come una caratteristica obsoleto:

(C11, 6.11.5p1) "Il posizionamento di una specifica classe di archiviazione altro che all'inizio della dichiarazione gli specificatori in una dichiarazione sono una caratteristica obsoleta. "

5

Disclaimer: Questa non è una risposta tecnica ma pratica. Fare riferimento alle altre risposte per questioni tecniche. Questa risposta si legge supponente e soggettiva, ma ti prego di sopportarmi mentre cerco di spiegare il quadro generale.

struct è una bestia strana, perché la roba si mette tra la parentesi di chiusura } e il punto e virgola ; si riferisce al contenuto all'interno o davanti a quelle staffe. Io so perché questo è, e grammaticalmente ha senso, ma personalmente lo trovo molto intuitivo come parentesi graffe di solito significa campo di applicazione:

esempi

intuitivo:

// declares a variable named `foo` of unnamed struct type. 
struct { 
    int x, y; 
} foo; 

foo.x = 1; 


// declares a type named `Foo` of unnamed struct type 
struct { 
    int x, y; 
} typedef Foo; 

Foo foo2; 
foo2.x = 2; 


// declares a type named `Baz` of the struct named `Bar` 
struct Bar { 
    int x, y; 
} typedef Baz; 

// note the 'struct' keyword to actually use the type 'Bar' 
struct Bar bar; 
bar.x = 3; 
Baz baz; 
baz.x = 4; 

quindi non ci sono molte cose sottili che possono andare storte con la sintassi densa di struct se typedef se usato in questo modo. Come mostrato sotto è molto facile dichiarare una variabile invece di un tipo per caso. Il compilatore ha solo un aiuto limitato perché quasi tutte le combinazioni sono grammaticalmente corrette. Semplicemente non significano necessariamente ciò che provi ad esprimere. È un pit of despair.

esempi sbagliati:

// mixed up variable and type declaration 
struct foo { 
    int x, y; 
} Foo; 

// declares a type 'foo' instead of a variable 
typedef struct Foo { 
    int x, y; 
} foo; 

// useless typedef but compiles fine 
typedef struct Foo { 
    int x, y; 
}; 

// compiler error 
typedef Foo struct { 
    int x, y; 
}; 

Per ragioni di leggibilità e manutenzione preferisco dichiarare tutto separatamente e mai messo nulla dietro la parentesi graffa di chiusura. Il costo di linee aggiuntive di codice è facilmente superato dalla sintassi intuitiva. Io sostengo che questo approccio makes it easy to do the right things and annoying to do the wrong things.

esempi intuitivi:

// declares a struct named 'TVector2' 
struct TVector2 { 
    float x, y; 
}; 

// declares a type named 'Vector2' to get rid of the 'struct' keyword 
// note that I really never use 'TVector2' afterwards 
typedef struct TVector2 Vector2; 

Vector2 v, w; 
v.x = 0; 
v.y = 1;