2011-01-15 24 views
28

He visto las estructuras C declaradas de diferentes maneras antes. ¿Por qué es eso y qué, en todo caso, cada uno hace diferente?¿Cuál es la forma sintácticamente correcta de declarar una estructura C?

Por ejemplo:

struct foo { 
    short a; 
    int b; 
    float c; 
}; 

typedef struct { 
    short d; 
    int e; 
    float f; 
} bar; 

typedef struct _baz { 
    short a; 
    int b; 
    float c; 
} baz; 

int main (int argc, char const *argv[]) 
{ 
    struct foo a; 
    bar b; 
    baz c; 

    return 0; 
} 
+0

Esto no responde su pregunta para C, pero creo que son todas correctas para C++, aunque se prefiere la primera. Esto también puede haber cambiado entre diferentes versiones de C, pero dejaré que alguien más hable de eso, ya que no soy un experto en la historia de C/C++. –

+2

C++ es diferente: cuando escribes 'struct Foo {};' o 'clase Foo {};' allí, obtienes 'Foo' gratis sin' typedef'. –

+0

Sin embargo, todos seguirán funcionando, aunque podrían hacer cosas ligeramente diferentes (como contaminar el espacio de nombres con etiquetas adicionales). –

Respuesta

31

Bueno, la diferencia obvia es demostrada en su main:

struct foo a; 
bar b; 
baz c; 

La primera declaración es de un in- typedef ed struct y necesita la palabra clave struct de usar. El segundo es de typedef ed anonymous struct, por lo que usamos el nombre typedef. El tercero combina el primero y el segundo: su ejemplo usa baz (que es convenientemente corto) pero podría fácilmente usar struct _baz con el mismo efecto.

Actualización: larsmans' answer menciona un caso más común donde tiene que usar al menos struct x { } para hacer una lista vinculada. El segundo caso no sería posible aquí (a menos que abandones la sensatez y uses un void *) porque el struct es anónimo, y el typedef no se produce hasta que se defina el struct, lo que no te permite hacer un (type-safe) puntero al tipo struct. La primera versión funciona bien para este uso, pero la tercera es generalmente preferida en mi experiencia. Dale un representante por eso.

Una diferencia más sutil está en la colocación del espacio de nombres. En C, struct las etiquetas se colocan en un espacio de nombre separado de otros nombres, pero los nombres typedef no lo son. Así que la siguiente es legal:

struct test { 
    // contents 
}; 

struct test *test() { 
    // contents 
} 

Pero el siguiente no lo es, porque sería ambiguo lo que el nombre test es:

typedef struct { 
    // contents 
} test; 

test *test() { 
    // contents 
} 

typedef hace que el nombre más corto (siempre un plus), pero lo coloca en el mismo espacio de nombres que sus variables y funciones. Usualmente esto no es un problema, pero es una diferencia sutil más allá del simple acortamiento.

+0

¡Gran respuesta, gracias! –

+0

+1 para el problema del espacio de nombres. –

+0

¿Sería ilegal utilizar el mismo nombre tanto para la etiqueta como para el typedef? O no es una buena idea? ¿O eso está perfectamente bien? Por ejemplo: typedef struct mystruct {} mystruct; – SeanRamey

27

es en gran medida una cuestión de preferencia personal. Me gusta dar a los tipos nuevos un nombre que comience con una letra mayúscula y omita el struct, por lo que generalmente escribo typedef struct { ... } Foo. Eso significa que no puedo escribir struct Foo.

La excepción es cuando un struct contiene un puntero a su propio tipo, p.

typedef struct Node { 
    // ... 
    struct Node *next; 
} Node; 

En este caso tiene que declarar también el tipo struct Node, ya que el typedef no es en su alcance dentro de la definición struct. Tenga en cuenta que ambos nombres pueden ser los mismos (no estoy seguro de dónde se originó la convención de subrayado, pero creo que los compiladores de C antiguos no podían manejar typedef struct X X;).

+0

Doh! Olvidé el caso de uso obvio de "lista vinculada". +1 –

6

Todos sus usos son sintácticamente correctos.Yo prefiero el uso siguiente

/* forward declare all structs and typedefs */ 
typedef struct foo foo; 
. 
. 
/* declare the struct itself */ 
struct foo { 
    short a; 
    int b; 
    foo* next; 
}; 

Observe que esta facilidad permite utilizar el typedef ya dentro de la declaración de la struct sí mismo, y que incluso para struct que hacen referencia mutuamente.

+3

+1 para un área que he perdido, y añado que si usa 'typedef struct foo foo;' en sus encabezados y no proporciona una definición de 'struct foo', el código de usuario tendrá un puntero opaco (es decir, pueden pasar 'foo *' apunta y recibe de las funciones de la biblioteca, pero no puede acceder a los miembros internos.) Esto es bueno para la encapsulación de datos, y un modismo común: el tipo estándar 'FILE * 'es una de esas instancias de esto. –

4

La confusión se produce porque algunas de las declaraciones están, de hecho, declarando hasta tres construcciones C. Debe tener en cuenta la diferencia entre: 1. Una declaración typedef, 2. Una definición de estructura, y 3. Una declaración struct.

Son todas construcciones C muy diferentes. Todos hacen cosas diferentes; pero puedes combinarlos en una construcción compuesta, si quieres.

Miremos cada declaración por turno.

//================================================ 
struct foo { 
    short a; 
    int b; 
    float c; 
}; 

Aquí estamos usando la sintaxis de definición de estructura más básica. Estamos definiendo foo como C tipo que luego puede usarse para declarar variables de ese tipo mediante la siguiente sintaxis:

foo myFoo; // Declare a struct variable of type foo. 

// =============== =============================== Esta próxima declaración es un poco como la sintaxis anterior en ese declara un tipo C, pero usa la sintaxis typedef. Desglosémoslo en sus componentes usando la declaración básica anterior.

typedef foo bar; // Declare bar as a variable type, the alias of foo. 

bar myBar;  // This is ssemantically the same as: foo myBar; 

Ahora simplemente reemplace el "foo" con la sintaxis anterior y ¡listo!

typedef struct { 
    short d;  
    int e; 
    float f; 
} bar; 

//================================================================== 
typedef struct _baz { 
    short a; 
    int b; 
    float c; 
} baz; 

La sintaxis anterior es equivalente a la siguiente secuencia de declaraciones.

struct _baz { 
    short a; 
    int b; 
    float c; 
} 

typedef _baz baz; // Declare baz as an alias for _baz. 

baz myBaz;  // Is the same as: _baz myBaz; 

//======================================================== 
Cuestiones relacionadas