¿Cómo declarar una estructura en un encabezado que será utilizado por varios archivos en c?

115

Si tengo un archivo source.c con una estructura:

struct a { 
    int i;
    struct b {
        int j;
    }
};

¿Cómo se puede utilizar esta estructura en otro archivo (es decir func.c)?

¿Debo crear un nuevo archivo de encabezado, declarar la estructura allí e incluir ese encabezado func.c?

¿O debería definir toda la estructura en un archivo de encabezado e incluir eso en ambos source.cy func.c? ¿Cómo se puede declarar la estructura externen ambos archivos?

¿Debería typedefhacerlo? ¿Si es así, cómo?

Ingeniero de software
fuente
Tenga en cuenta que la definición de estructura no es válida C.Como mínimo, debería haber un punto y coma después de la llave de cierre para struct b, pero luego su estructura adeclara un tipo que no se utiliza (probablemente debería definir un nombre de miembro, tal vez k, después de la llave de cierre interior y antes del punto y coma.
Jonathan Leffler

Respuestas:

140

si esta estructura va a ser utilizada por algún otro archivo func.c, ¿cómo hacerlo?

Cuando se utiliza un tipo en un archivo (es decir, archivo func.c), debe ser visible. La peor forma de hacerlo es copiarlo y pegarlo en cada archivo fuente que lo necesite.

La forma correcta es ponerlo en un archivo de encabezado e incluir este archivo de encabezado cuando sea necesario.

¿Abriremos un nuevo archivo de encabezado y declararemos la estructura allí e incluiremos ese encabezado en la función c?

Esta es la solución que más me gusta, porque hace que el código sea altamente modular. Codificaría tu estructura como:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

Pondría funciones que usan esta estructura en el mismo encabezado (la función que es "semánticamente" parte de su "interfaz").

Y, por lo general, podría nombrar el archivo después del nombre de la estructura y usar ese nombre nuevamente para elegir la definición de guardias de encabezado.

Si necesita declarar una función usando un puntero a la estructura, no necesitará la definición completa de la estructura. Una simple declaración hacia adelante como:

struct a ;

Será suficiente, y disminuye el acoplamiento.

¿O podemos definir la estructura total en el archivo de encabezado e incluirla tanto en source.cy en func.c?

Esta es otra forma, algo más fácil, pero menos modular: algún código que necesite solo su estructura para funcionar, aún tendría que incluir todos los tipos.

En C ++, esto podría conducir a una complicación interesante, pero esto está fuera de tema (sin etiqueta C ++), así que no daré más detalles.

luego, cómo declarar esa estructura como externa en ambos archivos. ?

Tal vez no entiendo el punto, pero Greg Hewgill tiene una muy buena respuesta en su publicación ¿Cómo declarar una estructura en un encabezado que va a ser utilizado por múltiples archivos en c? .

¿Lo escribiremos entonces cómo?

  • Si está utilizando C ++, no lo haga.
  • Si está utilizando C, debería hacerlo.

La razón es que la administración de estructuras en C puede ser un problema: debe declarar la palabra clave struct en todos los lugares donde se usa:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Mientras que un typedef le permitirá escribirlo sin la palabra clave struct.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

Es importante que aún conserve un nombre para la estructura. Escritura:

typedef struct
{
   /* etc. */
} MyStruct ;

simplemente creará una estructura anónima con un nombre typedef-ed, y no podrá declararlo hacia adelante. Así que mantén el siguiente formato:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Por lo tanto, podrá usar MyStruct donde quiera para evitar agregar la palabra clave struct, y aún usar MyStructTag cuando un typedef no funcione (es decir, declaración de reenvío)

Editar:

Se corrigió la suposición incorrecta acerca de la declaración de estructura C99, como lo señaló con razón Jonathan Leffler .

Editar 2018-06-01:

Craig Barnes nos recuerda en su comentario que no es necesario mantener nombres separados para el nombre de la "etiqueta" de la estructura y su nombre de "typedef", como lo hice anteriormente para mayor claridad.

De hecho, el código anterior bien podría escribirse como:

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC, esto es realmente lo que hace C ++ con su declaración de estructura más simple, detrás de escena, para mantenerlo compatible con C:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Volviendo a C, he visto ambos usos (nombres separados y los mismos nombres), y ninguno tiene inconvenientes que yo sepa, por lo que usar el mismo nombre hace que la lectura sea más simple si no usa "espacios de nombres" separados de C para estructuras y otros símbolos .

paercebal
fuente
2
¿Puede comentar o señalar la sección del estándar C99 que garantiza su comentario "Si está usando C99, no lo haga"?
Jonathan Leffler
Tienes razón. Probé recientemente un C99 y me sorprendió ver que mi estructura sin tipo no se reconocía cuando se usaba en C ++. Busqué opciones de compilador, y luego, en todos los documentos estándar que pude poner mis manos en mis manos, pero no encontré nada que pudiera explicar lo creí posible ...
paercebal
2
... De todos modos, gracias por el comentario. Lo corrigí ahora mismo.
paercebal
No es necesario utilizar nombres diferentes para la structetiqueta y el typedefnombre. C usa un espacio de nombres diferente para las structetiquetas, por lo que puede usar MyStructpara ambos.
Craig Barnes
1
@CraigBarnes Tienes razón, pero quería que quedara claro con solo leer el código. Si hubiera dado el mismo nombre, podría haber confundido a los novatos de C sobre la necesidad de escribir el nombre * dos veces "en la misma" declaración ". Agregaré una nota mencionando su comentario. ¡Gracias!
paercebal
34

Para una definición de estructura que se va a utilizar en más de un archivo fuente, definitivamente debería ponerla en un archivo de encabezado. Luego, incluya ese archivo de encabezado en cualquier archivo de origen que necesite la estructura.

La externdeclaración no se usa para definiciones de estructura, sino que se usa para declaraciones de variable (es decir, algún valor de datos con un tipo de estructura que haya definido). Si desea utilizar la misma variable en más de un archivo fuente, declare como externen un archivo de encabezado como:

extern struct a myAValue;

Luego, en un archivo fuente, defina la variable real:

struct a myAValue;

Si olvida hacer esto o lo define accidentalmente en dos archivos fuente, el vinculador le informará sobre esto.

Greg Hewgill
fuente
Es posible que obtenga un error del vinculador, o puede que no. En C, el modelo de vinculación permite definiciones "comunes", por lo que pueden funcionar varias definiciones sin inicializador (y tal vez con el mismo inicializador). Es una 'extensión común'. Creo que algunos compiladores también admiten definiciones provisionales (faltantes).
Jonathan Leffler
Luego, en un archivo fuente, defina la variable real:; ... o definirlo accidentalmente en dos archivos fuente ... :)
Johannes Schaub - litb
Una técnica que he usado para el problema de declarar / definir es definir condicionalmente GLOBALcomo externo nada en la parte superior del encabezado y luego declarar las variables como GLOBAL struct a myAValue;. De la mayoría de los archivos de origen, los arreglos para la #define GLOBAL externversión que se utiliza ( declarar las variables) y desde exactamente un archivo de origen que hace que definen el vacío para ser utilizado por lo que se las variables definidas .
TripeHound
Puede tener el mismo nombre de estructura que el nombre typedef en C, pero no en C ++.
xuhdev
6

ah:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

Listo, ahora solo necesita incluir ah en los archivos donde desea usar esta estructura.

fmsf
fuente