2010-07-06 12 views
13

Tengo curiosidad por saber cuál es la diferencia aquí cuando typedefing una enumeración o estructura. ¿Hay alguna diferencia semánticamente entre estos dos bloques?¿Cuáles son las diferencias entre estos dos estilos typedef en C?

Este:

typedef enum { first, second, third } SomeEnum; 

y esto:

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

mismo trato para estructuras. He visto ambas en uso, y ambas parecen hacer lo mismo en C u Objetivo-C. ¿Hay alguna diferencia real o solo es una preferencia por el estilo que puedes usar?

Respuesta

15

La diferencia es que el segundo enfoque declara un tipo llamado enum SomeEnum y también declara un typedef-name SomeEnum - un alias para ese tipo. En realidad, puede ser combinado en el equivalente de una sola línea

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

que hace que sea bastante obvio que la única diferencia entre los dos enfoques es si hay un nombre después de la palabra clave enum. Con el segundo enfoque, puede declarar el objeto de ese tipo de enumeración utilizando SomeEnum e o enum SomeEnum e, según prefiera.

El primer enfoque solo declara el tipodef-name SomeEnum para un tipo enum originalmente anónimo, lo que significa que está limitado a declaraciones SomeEnum e.

Por lo tanto, siempre que solo use el tipo nombre -de-SomeEnum en sus declaraciones, no habrá ninguna diferencia entre las dos. Sin embargo, en algunos casos es posible que deba usar el nombre original completo del tipo enum SomeEnum. En la primera aproximación, ese nombre no está disponible, por lo que no tendrás suerte.

Por ejemplo, si después de la declaración anterior también se declara una variable llamada SomeEnum en cierto margen anidada

int SomeEnum; 

el nombre de la variable ocultará el typedef-nombre de la enumeración, lo que hace esta declaración ilegal

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

Sin embargo, si se ha utilizado la segunda aproximación al declarar su enumeración, puede solucionar este problema utilizando el nombre de tipo completo

enum SomeEnum e; /* OK */ 

Esto no sería posible si utilizó la primera aproximación al declarar su tipo de enumeración.

Cuando se utiliza con estructuras, el nombre después de la struct es una necesidad cuando se necesita un tipo de auto-referencia (un tipo que contiene un puntero al mismo tipo), como

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

Por último, en el segundo enfoque, el nombre typedef es totalmente opcional. Simplemente puede declarar

enum SomeEnum { first, second, third }; 

y sólo tiene que utilizar enum SomeEnum cada vez que necesita para referirse a este tipo.

7

Sí, hay una diferencia semántica. El segundo fragmento declara un identificador de etiqueta, pero el primero no. Ambos declaran un identificador ordinario.

Eso significa que, por primera, este código no es válido, pero para la segunda, que es:

enum SomeEnum foo; 

Por lo que yo sé, no hay ninguna otra diferencia semántica entre ellos en el código. Para estructuras y uniones, la segunda forma, tal vez combinado con el typedef en una declaración, que se necesita para este tipo de recursivas

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

El identificador ordinario aún no es visible en especificador de la estructura, y por lo tanto necesita hacer referencia a la struct por el identificador de etiqueta ya declarado. Los identificadores de etiqueta se mencionan al anteponerlos por "struct", "union" o "enum", mientras que los identificadores ordinarios se mencionan sin un prefijo (de ahí el nombre "ordinario").

Además de la separación de los identificadores que se refieren a estructuras, uniones y enumeraciones de los que se refieren a valores, los identificadores de la etiqueta también son útiles para crear adelante declaraciones:

/* forward declaration */ 
struct foo; 

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

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

typedef nombres no puede ser declarado en repetidas ocasiones en el mismo alcance (en oposición a C++ donde pueden), y necesitan referirse a un tipo existente, para que no se puedan usar para crear declaraciones hacia adelante.

4

La única diferencia real es que en el segundo caso, se puede usar algo como:

enum SomeEnum x; 

mientras que el primer solamente soporta:

SomeEnum x; 

Para la gente que ha estado escribiendo C un largo tiempo, la definición de struct sin la palabra clave struct a menudo "se siente" extraño ...

2

El primer formulario crea un tipo anónimo enum y crea un alias SomeEnum.

El segundo formulario crea un tipo enum SomeEnum y un alias SomeEnum.

(En C, hay espacios de nombres separados para los tipos. Es decir, struct Foo es diferente de enum Foo que es diferente de Foo.)

Esto es más importante para struct s que enum s ya que necesitaría usar el segundo formulario si su struct fuera autorreferencial. Por ejemplo:

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

typedef struct LinkedListNode LinkedListNode; 

Lo anterior no sería posible con el primer formulario.

0

Para struct s hay una diferencia real que no se trata simplemente de nombrar.

Este es C válida:

struct SomeEnum { struct SomeEnum *first; }; 

Esto no es:

typedef struct { SomeEnum *first; } SomeEnum; 
0

Adición al comentario de user207442, es posible que un módulo de código fuente para declarar variables de tipo "struct foo *" sin siempre teniendo una definición para la estructura. Tal módulo no podrá desreferenciar dichos punteros, pero puede pasarlos a otros módulos y desde ellos.

Por ejemplo, uno podría tener un archivo de encabezado definir un tipo "USERCONSOLE" utilizando "typedef struct _USERCONSOLE * USERCONSOLE;". Codifique que # include's ese archivo de encabezado podría tener variables del tipo USERCONSOLE, y pasar dichas variables a/desde módulos que saben qué es realmente _USERCONSOLE, sin que el archivo de encabezado tenga que exponer la definición real de la estructura.

Cuestiones relacionadas