2011-07-05 150 views
2

頭文件應該有#include s?包含在頭文件中

我一般認爲這種層次包括壞的意見。假設你有這樣的:

了foo.h:

#include <stdio.h> // we use something from this library here 
struct foo { ... } foo; 

的main.c

#include "foo.h" 
/* use foo for something */ 
printf(...) 

一天main.c中的實現變化,你不再使用了foo.h,編譯會打破,你必須手動添加<stdio.h>

對戰有這樣的:

foo.h中

// Warning! we depend on stdio.h 
struct foo {... 

的main.c

#include <stdio.h> //required for foo.h, also for other stuff 
#include "foo.h" 

而當你停止使用富,刪除它打破什麼,但刪除文件stdio.h會打破foo.h.

是否應該禁止從.h文件中包含?

+0

可能的重複[是否有一個標準的C++的#include慣例?](http://stackoverflow.com/questions/691079/is-there-a-standard-include-convention-for-c)。這是C++,而不是C,但原理是相同的。那裏有很多好的建議。 http://stackoverflow.com/questions/181921/your-preferred-c-c-header-policy-for-big-projects是另一個。 –

回答

4

你已經概述了關於這個問題的兩個主要哲學。

我自己的看法(我認爲這就是人們可以真正擁有的),它的標題應該儘可能地獨立。我不想知道foo.h的所有依賴關係,只是爲了能夠使用該頭文件。我也鄙視不得不以特定的順序包含標題。

然而,foo.h的開發者也應該承擔儘可能無依賴性的責任。例如,foo.h標題應該被寫爲不依賴於stdio.h(如果這是可能的話)(使用前向聲明可以提供幫助)。

請注意,C標準禁止包含另一個標準頭的標準頭,但C++標準不包括。因此,您可以看到從一個C++編譯器版本轉換到另一個版本時所描述的問題。例如,在MSVC中,包括用於引入<iterator><vector>,但在MSVC 2010中不再出現,因此之前編譯的代碼可能不再有任何更多,因此您可能需要特別包括<iterator>

然而,儘管C標準似乎提倡了第二種哲學,但請注意,它也要求沒有標題依賴於另一個標題,並且可以按任意順序包含標題。因此,您可以獲得兩全其美的好處,但是C庫的實現者要付出一定的代價。他們必須跳過一些環節才能做到這一點(特別是支持可以通過任何幾個標題引入的定義,如NULLsize_t)。我猜那些起草C++標準的人決定增加對模仿者的複雜性已經不合理了(我不知道C++庫實現者在多大程度上利用了'漏洞' - 看起來MS似乎正在收緊這些,即使它沒有技術要求)。

+0

如果''聲明瞭使用迭代器的方法,爲什麼不應該在''中拉?爲什麼用戶應該這樣做?如果用戶在*''之後拉入''*會怎麼樣? –

+0

我可能不應該說「收緊」 - 我並不是想暗示這對用戶來說肯定是一件好事(如果''沒有從''拉入'開始)。在''之後(或之前)拉動'',如果已經包含在內,那麼在''中使用的任何包含守衛都不會妨礙它第二次造​​成問題。 –

+1

但這是讓我困擾的事情:爲什麼要求用戶在''中拉動'',而不是僅僅說''#include '就會起作用「?這就像故意增加複雜性並以某種「純度」的名義引入錯誤。 –

2

那麼,主要不應該依靠"foo.h"首先爲stdio。包含兩次東西並沒有什麼壞處。
另外,也許是富。 h並不真的需要stdio。更可能的是foo.c(實施)需要stdio。長話短說,我認爲每個人都應該包括他們需要的任何東西,並依靠include guards

+0

'stdio'是一個任意選擇,但struct {foo}可能使用'stdio'中定義的類型;它使得有必要在main.c和foo.c中包含stdio.h和foo.h。 – Tordek

+0

一個常見的例子是需要'size_t'類型的頭文件。 – caf

0

如果頭文件需要一個特定的標題,將其添加到頭文件

#ifndef HEADER_GUARD_YOUR_STYLE 
#define HEADER_GUARD_YOUR_STYLE 

#include <stdio.h> /* FILE */ 
int foo(FILE *); 

#endif /* HEADER GUARD */ 

如果代碼文件,並不需要一個標題,不添加它

/* #include <stdio.h> */ /* removed because unneeded */ 
#include <stddef.h> /* NULL */ 
#include "header.h" 
int main(void) { 
    foo(NULL); 
    return 0; 
} 
0

爲什麼難道你沒有在頭文件對應的* .c文件中包含東西嗎?

1

一旦你進入了成百上千個頭文件的項目,這是站不住腳的。假設我有一個名爲"MyCoolFunction.h"的頭文件,其中包含MyCoolFunction()的原型,並且該函數將指針指向結構體作爲參數。我應該能夠假設包括MyCoolFunction.h將包括所有必需的並允許我使用該功能,而無需查看.h文件以查看還需要包含哪些內容。

2

我一般的建議是:

  • 一個文件應該#include它需要什麼。
  • 它不應該期望別的什麼#include它需要的東西。
  • 它不應該#include它不需要的東西,因爲別的東西可能需要它。

真正的考驗是這樣的:你應該能夠編譯包括任何單一#include源文件並沒有錯誤或警告超越「沒有main()」。如果你通過這個測試,那麼你可以期望別的東西能夠沒有問題地將你的文件保存到#include。我寫了一個名爲「H檢查」短腳本,我用它來測試這一點:

#!/usr/bin/env bash 
# hcheck: Check header file syntax (works on source files, too...) 
if [ $# -eq 0 ]; then 
    echo "Usage: $0 <filename>" 
    exit 1 
fi 

for f in "[email protected]" ; do 
    case $f in 
     *.c | *.cpp | *.cc | *.h | *.hh | *.hpp) 
      echo "#include \"$f\"" > hcheck.cc 
      printf "\n\033[4mChecking $f\033[0m\n" 
      make -s $hcheck.o 
      rm -f hcheck.o hcheck.cc 
      ;; 
    esac 
done 

我敢肯定,有幾件事情,該腳本可以做的更好,但它應該是一個很好的起點。

如果這太多了,並且您的頭文件幾乎總是有相應的源文件,那麼另一種技術是要求相關頭文件是源文件中的第一個#include。例如:

了foo.h:

#ifndef Foo_h 
#define Foo_h 

/* #includes that Foo.h needs go here. */ 

/* Other header declarations here */ 

#endif 

foo.c的:

#include "Foo.h" 
/* other #includes that Foo.c needs go here. */ 

/* source code here */ 

這也說明foo.h中的 「包括衛士」 別人提及。

通過首先將#include "Foo.h"Foo.h必須#include它的依賴關係,否則你會得到一個編譯錯誤。