[英]What's the benefit for a C source file include its own header file
據我所知,如果源文件需要引用其他文件中的函數,那么它需要包含其頭文件,但我不明白為什么源文件包含自己的頭文件。 頭文件中的內容只是作為每個處理時間的函數聲明被復制並粘貼到源文件中。 對於包含自己的頭文件的源文件,這樣的“聲明”對我來說似乎沒有必要,實際上,項目在從源文件中刪除標題后仍然編譯並鏈接沒問題,所以源文件的原因是什么包括它自己的頭?
主要好處是讓編譯器驗證標頭及其實現的一致性。 你這樣做是因為方便,不是因為它是必需的。 絕對可以讓項目在沒有這種包含的情況下正確編譯和運行,但從長遠來看,它會使項目的維護變得復雜。
如果您的文件不包含自己的標題,則可能會意外地遇到函數的前向聲明與函數定義不匹配的情況 - 可能是因為您添加或刪除了參數,並且忘記更新標題。 發生這種情況時,依賴於不匹配函數的代碼仍然會編譯,但調用會導致未定義的行為。 讓編譯器捕獲此錯誤要好得多,當源文件包含自己的頭文件時會自動發生此錯誤。
頭文件告訴人們源文件可以做什么。
因此頭文件的源文件需要知道它的義務。 這就是它被包括在內的原因。
您的似乎是一個邊緣情況,但是包含文件可以被視為該源文件與可能需要這些功能的任何其他源文件之間的一種契約 。
通過在頭文件中編寫“契約”,您可以確保其他源文件知道如何調用這些函數,或者更確切地說,您將確保編譯器將插入正確的代碼並在編譯時檢查其有效性。
但是,如果您(甚至無意中)更改了相應源文件中的函數原型,該怎么辦?
通過在該文件中包含與其他人相同的標題,如果更改無意中“破壞”合同,您將在編譯時被警告。
更新(來自@tmlen的評論):即使在這種情況下它沒有,包含文件也可能使用聲明和編譯指示,如#define,typedef,enum,struct和inline以及編譯器宏,這些都沒有意義不止一次(實際上,在兩個不同的地方寫作是危險的 ,以免副本彼此不同步而帶來災難性后果)。 其中一些(例如結構填充編譯指示 )可能成為難以追蹤的錯誤。
它很有用,因為函數可以在定義之前聲明。
所以碰巧你有聲明,然后是一個call \\ _調用,然后是實現。 你不必,但你可以。
頭文件包含聲明。 只要原型匹配,您就可以隨時調用。 只要編譯器在完成編譯之前找到實現。
實際示例 - 假設項目中包含以下文件:
/* foo.h */
#ifndef FOO_H
#define FOO_H
double foo( int x );
#endif
/* foo.c */
int foo( int x )
{
...
}
/* main.c */
#include "foo.h"
int main( void )
{
double x = foo( 1 );
...
}
請注意, foo.h
中的聲明與foo.c
中的定義不匹配; 返回類型不同。 根據foo.h
的聲明, main.c
調用foo
函數,假設它返回一個double
。
foo.c
和main.c
是彼此分開編譯的。 由於main.c
調用foo.h
聲明的foo
,因此編譯成功。 由於foo.c
不包括foo.h
,編譯器不知道的聲明和定義的類型不匹配的,所以它編譯成功也是如此。
將兩個目標文件鏈接在一起時,函數調用的機器代碼將與函數定義所需的機器代碼不匹配。 函數調用期望返回double
值,但函數定義返回int
。 這是一個問題,特別是如果這兩種類型的大小不同。 最好的情況是你得到一個垃圾結果。
通過在foo.c
包含foo.h
,編譯器可以在運行程序之前捕獲這種不匹配。
並且,正如在前面的回答中指出的,如果foo.h
定義了foo.c
使用的任何類型或常量,那么你肯定需要包含它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.