防止递归C #include

时间:2010-01-11 21:43:26

标签: gcc include c-preprocessor

我粗略地了解了#include对 C 预处理器所做的规则,但我完全不理解。现在,我有两个头文件,Move.h和Board.h,它们都是typedef各自的类型(Move和Board)。在两个头文件中,我需要引用另一个头文件中定义的类型。

现在我在Board.h中有#include“Move.h”,在Move.h中有#include“Board.h”。当我编译时,gcc翻转并给我一个很长的(看起来像无限递归)错误信息在Move.h和Board.h之间翻转。

如何包含这些文件,以便我不会无限期地递归包含?

7 个答案:

答案 0 :(得分:14)

你需要研究前向声明,你已经创建了一个无限循环的包含,前向声明是正确的解决方案。

以下是一个例子:

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() { ... }

注意:每当你创建“Board”时,你都需要做这样的事情(有几种方法,这是一个例子):

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 */

答案 1 :(得分:3)

包括警卫将成为解决此问题的一部分。

维基百科的例子:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif

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

其他几个人指出的另一部分是前向参考。 (http://en.wikipedia.org/wiki/Forward_Reference

您可以部分地声明其中一个结构,如下所示:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct bar;
struct foo {
    int member;
};

#endif

答案 2 :(得分:2)

像这样:

//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

这种方式Board.h可以在不依赖move.h的情况下进行编译,您可以在board.h中添加move.h以使其内容可用。

答案 3 :(得分:2)

首先,您的.h文件中似乎缺少include guards,因此您会以递归方式包含它们。那很糟糕。

其次,你可以做前瞻性声明。在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

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

请注意,如果您需要在两个文件中声明struct Movestruct Board个对象(而不是指向其中一个对象),则此方法将不起作用。这是因为在解析其中一个文件时,其中一个类型是不完整类型(在上例中为struct Move)。

因此,如果您需要在两个文件中使用这些类型,则必须将类型定义分开:具有定义struct Movestruct Board的头文件,而不是其他内容(类似于我上面的示例),然后使用另一个引用struct Movestruct Board的头文件。

当然,您不能struct Move包含struct Boardstruct Board同时包含struct Move - 这将是无限递归,并且结构尺寸也将是无限的!

答案 4 :(得分:0)

您需要先拥有其中一个。在其中一个中进行前向decl并将其中一个用于例如

    #ifndef move
    struct move;
    #endif

可以是board.h文件的一部分。

    #ifndef board
    struct board;
    #endif

可能是move.h文件的一部分

然后你可以按任何顺序添加它们。

修改 正如评论中指出的那样......我假设使用了typedef结构,如下所示,用于board struct

   typedef struct {…} board;

因为我从来没有见过任何人在没有typedef的情况下使用C语言中的结构我做了这个假设...也许事情自从我上次用C编码以来已经发生了变化(yikies ......就像15年前一样)< / p>

答案 5 :(得分:0)

来自K&amp; R C编程语言(我的副本中的第91页“条件包含”),并为您做了一些调整:

#if !defined (BOARD_H)
#define BOARD_H

/* contents of board.h go here */

#endif

和Move.h相同

这样,一旦包含一个标题,就不会再次包含它,因为已经为预处理器定义了“BOARD_H”名称。

答案 6 :(得分:0)

循环依赖是一种痛苦,应尽可能消除。除了目前给出的前瞻性声明建议(Alok是最好的例子)之外,我还想提出另一个建议:通过引入第三种类型来打破Board和Move之间的相互依赖(称之为BoardMoveAssoc用于说明;我相信你可以拿出一个不那么糟糕的名字):

#ifndef H_BOARD_MOVE_ASSOC
#define H_BOARD_MOVE_ASSOC

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

struct BoardMoveAssoc {
    Move m;
    Board b;
};

...
#endif

根据这个计划,Board and Move不必彼此了解任何事情;两者之间的任何关联都由BoardMoveAssoc类型管理。确切的结构将取决于Move和Board应该如何相关;例如,如果多个移动被映射到单个板,则该结构可能看起来更像

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

这样,您不必担心前向声明或不完整类型。您正在引入第三种类型,但我相信这将更容易理解和维护。