c語言之標頭檔案包含問題
隨著**越寫越長,乙個原始檔的體制越來越臃腫。於是提倡將**寫到不同的多個原始檔中去。將**寫到多個原始檔中去就會遇到各個原始檔中函式與變數的呼叫規則問題。
通常人們習慣性的把巨集定義、結構體、聯合體、列舉、外部變數和外部函式宣告等寫入到頭檔案.h中去,而把函式的宣告、變數定義等寫入到.c檔案中去。當某一.c原始檔需要呼叫某一函式的時候,只要將包含這個函式宣告的標頭檔案包含到本檔案中來就可以了。如a.h中宣告了a.c中定義的fun()函式,而b.c需要fun()函式,只需要#include 」a.h」就可以了。b.c怎麼找到fun()的呢,難道a.c和a.h間通過同名建立了一種聯絡,b.c會直接到a.c中去尋找fun()?
很多時候,人們習慣性的把乙個原始檔中的巨集定義、結構體、聯合體、列舉、外部變數和外部函式宣告等寫入到乙個同名的標頭檔案中去。這給人的錯覺就是同名使得兩者被關聯起來,在b.c包含a.h時,會自動去a.c中去尋找fun()函式。其實不然。
再給乙個例子,如a.h中宣告了b.c中定義的fun()函式,而c.c需要fun()函式,只需要#include」a.h」就可以了。這個例子就說明,不是同名的標頭檔案也可以用來宣告原始檔中的函式等。那怎麼通過標頭檔案尋找到函式定義呢?
原理要從編譯器工作過程說起。編譯器工作過程分為4步驟:
1預處理階段 :以c原始檔為單位,處理巨集替換、條件編譯和檔案包含。預處理本質上就是**文字的替換。拿標頭檔案包含來說,就是把包含的標頭檔案中的所有**copy到原始檔中去。於是就形成了乙個「中間c檔案」。
2語法分析階段 :c語言語法分析,你懂得。
3編譯階段:以「中間c檔案」為單位,首先編譯成純彙編語句,再將之彙編成跟cpu相關的二進位製碼,生成各個目標檔案 (.obj檔案)。編譯階段不會去尋找用到的標頭檔案中的函式的定義,只是按照一定規格轉換**格式為二進位制。
4連線階段:將上一步成生的各個目標檔案,根據一些引數,連線生成最終的可執行檔案。主要的工作就是重定位各個目標檔案的函式,變數等。
我們在b.c或c.c中用#include 「a.h」實際上是引入相關宣告,使得編譯可以通過,程式並不關心實現是在**,是怎麼實現的。原始檔編譯後成生了目標檔案(.o或.obj檔案),目標檔案中,這些函式和變數就視作乙個個符號。在link的時候,需要在makefile裡面說明需要連線哪個.o或.obj檔案(在這裡是b.c生成的.o或.obj檔案),此時,聯結器會去這個.o或.obj檔案中找在c.c中實現的函式,再把他們build到makefile中指定的那個可以執行檔案中。通常,編譯器會在每個.o或.obj檔案中都去找一下所需要的符號,而不是只在某個檔案中找或者說找到乙個就不找了。因此,如果在幾個不同原始檔中都包含了同乙個標頭檔案,該標頭檔案中的某些變數預設地就會被編譯超過一次,鏈結的時候就會提示「redefined」。這也就解釋了重定義一般不會在編譯階段出錯,而會在鏈結階段出錯。
總結出來一句話:原始檔呼叫#inlude標頭檔案中的函式不是通過直接查詢同名原始檔找到的,而是通過逐個查詢已經編譯好的.o或者.obj找到的。但是還是建議使用同名的原始檔和標頭檔案,這樣便於形成模組化,使**清晰易懂,便於打理。
編譯器的編譯過程告訴我們另外乙個寫標頭檔案需要注意的地方,標頭檔案的預編譯在插入有用**的同時也會插入大量不需要的東西,乙個標頭檔案被多個原始檔包含還會出現重定義的問題。這些都是需要注意的。插入了不需要的東西在很大程度上是不可避免的,但是精簡了**,看起來簡潔了不少。我們可以把那些經常在一起使用的巨集定義、宣告等放在乙個標頭檔案中,減少不必要的公用。關於重定義,可以通過#ifndef #define #endif來解決。下面以乙個例子來說明。
假設有三個檔案
node.h //定義節點
list.h //對鍊錶的操作函式
test.c //測試函式
包含關係如下:
list.h中
#include"node.h"
test.c中
#include"list.h"
#include"node.h"
#include ... 省略其它必要的標頭檔案
使用命令編譯
$gcc -o testtest.c
編譯時,會出現錯誤:***重定義
為什麼呢?
1)test.c中包含了node.h,因為node.h是定義結構的檔案,而且已經被list.h包含了,所以這裡node.h會預編譯兩次,出現重定義!
所以,可以去掉test.c中的標頭檔案node.h即可
2)修改node.h,避免重定義,這種方法也是推薦的方法
[cpp]view plain
copy
#ifndef _node
#define _node
typedef
struct nodenode;
#endif
使用乙個標記變數_node來表示node結構已經被定義了,將定義過程包含在#ifndef~#endif中,這樣,不管包含多少次node.h檔案,都不會出現重定義。
當然,這不僅僅限於結構的定義。
C語言之標頭檔案包含問題
c語言之標頭檔案包含問題 answer 1 include 會將指定檔案的內容插入到源程式檔案中。當使用的格式時,編譯器會從環境變數include所指定的路徑中尋找file name 檔案,如果沒有定義include,c 編譯器會在指定的路徑中搜尋檔案。如ht ide3000安裝後,預設includ...
c 標頭檔案相互包含問題
c 中標頭檔案互相包含經常會出現編譯錯誤.示例 如下 h ifndef a h define a h include b.h includeusing namespace std class a endif a h a.cpp include a.h a a a a int a getvala vo...
c 標頭檔案包含問題 include class
前向宣告概念 forward declaration 在程式中引入了類型別的b.在宣告之後,定義之前,類b是乙個不完全型別 incompete type 即已知b是乙個型別,但不知道包含哪些成員.不完全型別只能以有限方式使用,不能定義該型別的物件,不完全型別只能用於定義指向該型別的指標及引用,或者用...