我粗略地了解了#include对 C 预处理器所做的规则,但我完全不理解。现在,我有两个头文件,Move.h和Board.h,它们都是typedef各自的类型(Move和Board)。在两个头文件中,我需要引用另一个头文件中定义的类型。
现在我在Board.h中有#include“Move.h”,在Move.h中有#include“Board.h”。当我编译时,gcc翻转并给我一个很长的(看起来像无限递归)错误信息在Move.h和Board.h之间翻转。
如何包含这些文件,以便我不会无限期地递归包含?
答案 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 Move
和struct Board
个对象(而不是指向其中一个对象),则此方法将不起作用。这是因为在解析其中一个文件时,其中一个类型是不完整类型(在上例中为struct Move
)。
因此,如果您需要在两个文件中使用这些类型,则必须将类型定义分开:具有定义struct Move
和struct Board
的头文件,而不是其他内容(类似于我上面的示例),然后使用另一个引用struct Move
和struct Board
的头文件。
当然,您不能struct Move
包含struct Board
而struct 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;
};
这样,您不必担心前向声明或不完整类型。您正在引入第三种类型,但我相信这将更容易理解和维护。