2010-01-11 98 views
7

我大致瞭解與#包括與Ç預處理器做什麼規則,但我不完全理解它。現在,我有兩個頭文件,Move.h和Board.h,它們都輸入了它們各自的類型(Move和Board)。在這兩個頭文件中,我需要引用其他頭文件中定義的類型。防止遞歸和C的#include

現在我已經在Board.h並在#包括#包括Move.h.「Move.h」「Board.h」當我編譯時,海灣合作委員會翻轉,並給我一個很長的(看起來像無限遞歸)錯誤信息翻轉Move.h和Board.h之間。

如何包含這些文件,以便我不會無限遞歸地包含這些文件?

+1

注意,在一個理想的世界裏,你避免循環依賴這樣的。它當然不總是可能的,有時它們可​​能非常有用,但是當你創建一個循環依賴時,你應該花一點時間思考它併爲它的存在辯護。 – 2010-01-11 21:53:58

+0

@Greg D - 我接受了你的建議,並創建了另一個名爲Types.h的文件,我在這裏完成了所有的#define-ing和typedef-ing。我將它包含在兩個文件中,並且一切都很好! – twolfe18 2010-01-11 22:16:22

回答

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

注:當你創建一個「局」,你需要做這樣的事情(有幾個方面,這裏是一個例子):

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

這看起來像是一個非常好的解決方案,不幸的是我無法實現它(我需要查看#ifndef以及它的工作原理,以便更好地理解這一點)。但感謝您的答案。 – twolfe18 2010-01-11 22:17:29

+1

當你說「沒有工作」,你是什麼意思?另外,您的Move和Board類型是否相互引用,或者是兩個頭文件中使用的類型,但不一定在結構定義中使用? (無恥插件:另請參閱我的答案瞭解一些指針。) – 2010-01-11 22:20:07

+0

是的,請指定它「不工作」的方式,此代碼(減去主函數)應該工作得很好。 – 2010-01-11 22:29:16

0

你需要他們的一個第一。在其中之一中進行正向decl,並以此爲例

#ifndef move 
    struct move; 
    #endif 

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

#ifndef board 
    struct board; 
    #endif 

可能是move.h文件

,那麼你可以以任何順序添加它們的一部分。

編輯 正如有人指出,在評論...我假設使用的typedef的構建董事會結構

typedef struct {…} board; 

,因爲我從來沒有見過使用結構中的人如下沒有一個typedef我做這個假設ç...也許事情自從上次我在C語言編寫的變化(yikies ....這就像15年前)

+0

您無法使用預處理器測試結構是否存在。如果你暗示他也應該製作一個名爲「移動」的宏,那麼你應該清楚這一點。 – 2010-01-11 21:48:28

+0

@Evan:好點,看到我的編輯以上:d – Hogan 2010-01-11 21:58:55

+0

你不能測試與預處理器的typedef要麼...你需要添加字面上像'#定義board'地方。 – 2010-01-11 22:00:21

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.hmove.h以使其內容可用。

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 
0

選自K & R本C程序設計語言(P 91「條件包含」在我的副本),用一些調整,爲您提供:

#if !defined (BOARD_H) 
#define BOARD_H 

/* contents of board.h go here */ 

#endif 

與同爲Move.h

這樣,一旦頭部已被列入一次,就不會再次被包括,因爲已經爲預處理器定義了「BOARD_H」名稱。

+0

以雙下劃線開頭的宏被保留,您不應該使用它們。此外,還保留下劃線後跟大寫字母。 – 2010-01-11 21:59:03

+0

@Evan - 更正了以上感謝您的評論。我認爲我的編輯評論會出現在上面,但顯然不是! – Nij 2010-01-12 22:22:46

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同時—這將是無窮遞歸和結構尺寸將會是無限的!

0

循環依賴是一個痛苦的屁股,應儘可能消除。除了目前給出(阿洛克的就是最好的例子)向前聲明的建議,我想拋出另一個建議融入到作品中:破局之間的相互依存,並通過引入第三類(稱之爲BoardMoveAssoc用於說明移動;我相信你能拿出一個不太蘇茨基名):

#ifndef H_BOARD_MOVE_ASSOC 
#define H_BOARD_MOVE_ASSOC 

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

struct BoardMoveAssoc { 
    Move m; 
    Board b; 
}; 

... 
#endif 

根據這項計劃,董事會和移動不必知道對方的任何事情;兩者之間的任何關聯都由BoardMoveAssoc類型管理。確切的結構將取決於移動和董事會應如何相關;例如,如果多個動作映射到一個單一的主板,該結構可看起來更像

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

這樣,您就不必擔心前置聲明或不完整的類型。你正在引入第三種類型,但我相信這會更容易理解和維護。