2010-01-11 11 views
7

Comprendo aproximadamente las reglas con lo que #include hace con el preprocesador C, pero no lo entiendo del todo. En este momento, tengo dos archivos de encabezado, Move.h y Board.h, que ambos escriben en su respectivo tipo (Move and Board). En ambos archivos de encabezado, necesito hacer referencia al tipo definido en el otro archivo de encabezado.Prevención del recursivo C# inclusión

Ahora tengo #include "Move.h" en Board.hy #include "Board.h" en Move.h. Sin embargo, cuando compilo, gcc se voltea y me da un mensaje de error largo (lo que parece infinito recursivo) volteando entre Move.h y Board.h.

¿Cómo incluyo estos archivos para no incluir de forma recursiva indefinidamente?

+1

Tenga en cuenta que en un mundo ideal, se evitan dependencias circulares como esta. Ciertamente no siempre es posible, y algunas veces pueden ser muy útiles, pero cada vez que creas una dependencia circular debes tomarte un momento para pensar en ello y justificar su existencia. –

+0

@Greg D - Lo asesoré y creé otro archivo llamado Types.h donde hago todo mi #definación y definición de tipos. Incluyo eso en ambos archivos, ¡y todo está bien! – twolfe18

Respuesta

14

Necesita buscar en las declaraciones forward, ha creado un bucle infinito de includes, las declaraciones forward son la solución adecuada.

He aquí un ejemplo:

Move.h

#ifndef MOVE_H_ 
#define MOVE_H_ 

struct board; /* forward declaration */ 
struct move { 
    struct board *m_board; /* note it's a pointer so the compiler doesn't 
          * need the full definition of struct board yet... 
          * make sure you set it to something!*/ 
}; 
#endif 

Board.h

#ifndef BOARD_H_ 
#define BOARD_H_ 

#include "Move.h" 
struct board { 
    struct move m_move; /* one of the two can be a full definition */ 
}; 
#endif 

main.c

#include "Board.h" 
int main() { ... } 

Nota: cada vez que se crea una "Junta", tendrá que hacer algo como esto (hay algunas maneras, he aquí un ejemplo):

struct board *b = malloc(sizeof(struct board)); 
b->m_move.m_board = b; /* make the move's board point 
         * to the board it's associated with */ 
+0

esto parece una solución muy buena, desafortunadamente no pude hacer que funcione (tengo que mirar en #ifndef y cómo funciona más para entender completamente esto). Gracias por la respuesta sin embargo. – twolfe18

+1

Cuando dices "no funcionó", ¿qué quieres decir? Además, ¿los tipos Move y Board se referencian entre sí, o son los tipos utilizados en los dos archivos de encabezado pero no necesariamente en las definiciones de estructura? (Conector Shameless: También vea mi respuesta para algunos punteros.) –

+0

sí, especifique la forma en que "no funcionó", este código (menos la función principal) debería funcionar prácticamente como está. –

0

Es necesario tener uno de ellos en primer lugar. Hacer una decl adelante en uno de ellos y tienen que uno de, por ejemplo,

#ifndef move 
    struct move; 
    #endif 

podría ser parte del archivo board.h.

y

#ifndef board 
    struct board; 
    #endif 

podría ser parte del archivo move.h

entonces se podría añadir en cualquier orden.

edición Como se señaló en los comentarios ... Yo estaba asumiendo el uso de la typedef construir de la siguiente estructura para el tablero de

typedef struct {…} board; 

ya que nunca he visto a nadie el uso de estructuras de C sin un typedef hice esta suposición ... tal vez las cosas han cambiado desde la última vez que codificado en C (yikies .... que era hace 15 años)

+0

no puede probar si existe una estructura usando el pre-procesador. Si está implicando que debe hacer una macro llamada movimiento también, debe dejar eso en claro. –

+0

@Evan: buen punto, mira mi edición anterior: D – Hogan

+0

no puedes probar con typedefs con el preprocesador tampoco ... necesitarías literalmente agregar algo como '#define board' en alguna parte. –

2

así:

//Board.h 
#ifndef BOARD_H 
#define BOARD_H 
strunct move_t; //forward declaration 
typedef struct move_t Move; 
//... 
#endif //BOARD_H 

//Move.h 
#ifndef MOVE_H 
#define MOVE_H 
#include "Move.h" 
typedef struct board_t Board; 
//... 
#endif //MOVE_H 

De esta manera Board.h se puede compilar sin dependencia en move.h y puede incluir board.h en move.h para que su contenido esté disponible allí.

3

Incluir guardias sería parte de la solución a este problema.

Ejemplo de Wikipedia:

#ifndef GRANDFATHER_H 
#define GRANDFATHER_H 

struct foo { 
    int member; 
}; 

#endif 

http://en.wikipedia.org/wiki/Include_guard

La otra parte como se ha señalado por varios otros hace referencia a adelante. (http://en.wikipedia.org/wiki/Forward_Reference)

Se puede declarar parcialmente una de las estructuras por encima de la otra, así:

#ifndef GRANDFATHER_H 
#define GRANDFATHER_H 

struct bar; 
struct foo { 
    int member; 
}; 

#endif 
0

Desde K & R El lenguaje de programación C (p 91 "Inclusión condicional" en mi copia), con algunos ajustes para usted:

#if !defined (BOARD_H) 
#define BOARD_H 

/* contents of board.h go here */ 

#endif 

y lo mismo para Move.h

de esta manera, una vez a la cabecera se ha incluido una vez, no lo hará Se incluirá de nuevo, ya que el nombre 'BOARD_H' ya se ha definido para el preprocesador.

+0

que comienzan con un doble guión bajo están reservadas, no debe usarlas. Además, un guión bajo seguido de una letra mayúscula también se reserva. –

+0

@Evan - corregido el anterior gracias por el comentario. ¡Creí que mi comentario de edición aparecería arriba, pero aparentemente no! – Nij

2

En primer lugar, parece que no tiene include guards en sus archivos .h, por lo que los está incluyendo recursivamente. Eso es malo.

En segundo lugar, puede hacer una declaración directa. En Move.h:

/* Include guard to make sure your header files are idempotent */ 
#ifndef H_MOVE_ 
#define H_MOVE_ 

#include "Board.h" 

/* Now you can use struct Board */ 
struct Move { struct Board *board; }; 

#endif 

en Board.h:

#ifndef H_BOARD_ 
#define H_BOARD_ 

struct Move; /* Forward declaration. YOu can use a pointer to 
       struct Move from now on, but the type itself is incomplete, 
       so you can't declare an object of the type itself. */ 
struct Board { struct Move *move; }; /* OK: since move is a pointer */ 

#endif 

Tenga en cuenta que si tiene que declarar struct Move y struct Board objetos (en lugar de puntero a uno de ellos), tanto en los archivos, este método no lo hará trabajo. Esto se debe a que uno de los tipos es tipo incompleto en el momento del análisis de uno de los archivos (struct Move en el ejemplo anterior).

lo tanto, si necesita utilizar los tipos, tanto en los archivos, tendrá que separar las definiciones de tipos: archivos han encabezado que definen struct Move y struct Board, y nada más (algo así como el ejemplo anterior), y luego use otro archivo de encabezado que haga referencia tanto a struct Move como a struct Board.

Por supuesto, no se puede tener struct Move contienen una struct Boardstruct Board y contienen una struct Move al mismo tiempo — que será recursividad infinita, y los tamaños struct será infinito, así!

0

Las dependencias circulares son una molestia y deben eliminarse siempre que sea posible. Además de las sugerencias de declaraciones previas dadas hasta ahora (el de Alok es el mejor ejemplo), me gustaría lanzar otra sugerencia a los trabajos: romper la dependencia mutua entre el Tablero y el Movimiento introduciendo un tercer tipo (llámalo BoardMoveAssoc para ilustración; estoy seguro de que puede venir con un nombre menos apestoso):

#ifndef H_BOARD_MOVE_ASSOC 
#define H_BOARD_MOVE_ASSOC 

#include "Move.h" 
#include "Board.h" 

struct BoardMoveAssoc { 
    Move m; 
    Board b; 
}; 

... 
#endif 

Bajo este esquema, embarcar y desplazarse no tienen que saber nada el uno del otro; cualquier asociación entre los dos es administrada por el tipo BoardMoveAssoc.La estructura exacta dependerá de cómo se supone que se relacionan Move y Board; por ejemplo, si varios movimientos se asignan a una sola tarjeta, la estructura puede parecerse más a

struct BoardMoveAssoc { 
    Move m[NUM_MOVES] // or Move *m; 
    Board b; 
}; 

De esta manera, usted no tiene que preocuparse de declaraciones adelantadas o tipos incompletos. Está introduciendo un tercer tipo en la mezcla, pero creo que será más fácil de comprender y mantener.

Cuestiones relacionadas