關於標頭檔案

2021-06-11 02:49:06 字數 2982 閱讀 2715

自定義標頭檔案通常放在使用該標頭檔案的原始檔所在的目錄中,並使用 #include "myhead.h" 來包含。 

標頭檔案是不編譯的,因為c語言編譯過程之前有個預編譯過程。在這個過程中用標頭檔案中的內容替換原始檔中「#include」命令,所以在編譯器看來,沒有標頭檔案,只有原始檔。預編譯過程還包括條件編譯

標頭檔案為了防止被重複包含,通常的格式是: 

#ifndef _myhead_h_ 

#define _myhead_h_ 

(這裡是標頭檔案的內容) 

#endif  

預處理:預編譯處理是指在編譯系統對檔案進行編譯---詞法分析、語法分析、**生成及優化之前,對一些特殊的編譯語句先進行處理,然後將處理結果與源程式一起編譯,生成目標檔案。

條件編譯

條件編譯指令將決定那些**被編譯,而哪些是不被編譯的。可以根據表示式的值或者某個特定的巨集是否被定義來確定編譯條件。

#endif用於結束條件編譯,編譯時與前面最近的#if   #ifdef或#ifndef作為一對,經常一起使用,編譯兩者之間的部分程式段。

簡單的說其實要理解c檔案與標頭檔案(即.h)有什麼不同之處,首先需要弄明白編譯器的工作過程,一般說來編譯器會做以下幾個過程:

1.預處理階段

2.詞法與語法分析階段

3.編譯階段,首先編譯成純彙編語句,再將之彙編成跟cpu相關的二進位製碼,生成各個目標檔案(.obj檔案)

編譯器在編譯時是以c檔案為單位進行的,也就是說如果你的專案中乙個c檔案都沒有,那麼你的專案將無法編譯,聯結器是以目標檔案為單位,它將乙個或多個目標檔案進行函式與變數的重定位,生成最終的可執行檔案,在pc上的程式開發,一般都有乙個main函式,這是各個編譯器的約定,當然,你如果自己寫聯結器指令碼的話,可以不用main函式作為程式入口!!!!

(main .c檔案 目標檔案.o 可執行檔案 )

有了這些基礎知識,再言歸正傳,為了生成乙個最終的可執行檔案,就需要一些目標檔案,也就是需要c檔案,而這些c檔案中又需要乙個main函式作為可執行程式的入口,那麼我們就從乙個c檔案入手,假定這個c檔案內容如下:

#include #include "mytest.h"

int main(int argc,char **argv)

標頭檔案內容如下:

int test;

現在以這個例子來講解編譯器的工作:

1.預處理階段:編譯器以c檔案作為乙個單元,首先讀這個c檔案,發現第一句與第二句是包含乙個標頭檔案,就會在所有搜尋路徑中尋找這兩個檔案,找到之後,就會將相應標頭檔案中再去處理巨集,變數,函式宣告,巢狀的標頭檔案包含等,檢測依賴關係,進行巨集替換,看是否有重複定義與宣告的情況發生,最後將那些檔案中所有的東東全部掃瞄進這個當前的c檔案中,形成乙個中間「c檔案」

2.編譯階段,在上一步中相當於將那個標頭檔案中的test變數掃瞄進了乙個中間c檔案,那麼test變數就變成了這個檔案中的乙個全域性變數,此時就將所有這個中間c檔案的所有變數,函式分配空間,將各個函式編譯成二進位製碼,按照特定目標檔案格式生成目標檔案,在這種格式的目標檔案中進行各個全域性變數,函式的符號描述,將這些二進位製碼按照一定的標準組織成乙個目標檔案

3.連線階段,將上一步成生的各個目標檔案,根據一些引數,連線生成最終的可執行檔案,主要的工作就是重定位各個目標檔案的函式,變數等,相當於將個目標檔案中的二進位製碼按一定的規範合到乙個檔案中再回到c檔案與標頭檔案各寫什麼內容的話題上:理論上來說c檔案與標頭檔案裡的內容,只要是c語言所支援的,無論寫什麼都可以的,比如你在標頭檔案中寫函式體,只要在任何乙個c檔案包含此標頭檔案就可以將這個函式編譯成目標檔案的一部分(編譯是以c檔案為單位的,如果不在任何c檔案中包含此標頭檔案的話,這段**就形同虛設),你可以在c檔案中進行函式宣告,變數宣告,結構體宣告,這也不成問題!!!

那為何一定要分成標頭檔案與c檔案呢?又為何一般都在頭件中進行函式,變數宣告,巨集宣告,結構體宣告呢?(標頭檔案不定義,不初始化)而在c檔案中去進行變數定義,函式實現呢??原因如下:

1.如果在標頭檔案中實現乙個函式體,那麼如果在多個c檔案中引用它,而且又同時編譯多個c檔案,將其生成的目標檔案連線成乙個可執行檔案,在每個引用此標頭檔案的c檔案所生成的目標檔案中,都有乙份這個函式的**,如果這段函式又沒有定義成區域性函式,那麼在連線時,就會發現多個相同的函式,就會報錯

2.如果在標頭檔案中定義全域性變數,並且將此全域性變數賦初值,那麼在多個引用此標頭檔案的c檔案中同樣存在相同變數名的拷貝,關鍵是此變數被賦了初值,所以編譯器就會將此變數放入data段,最終在連線階段,會在data段中存在多個相同的變數,它無法將這些變數統一成乙個變數,也就是僅為此變數分配乙個空間,而不是多份空間,假定這個變數在標頭檔案沒有賦初值,編譯器就會將之放入bss段,聯結器會對bss段的多個同名變數僅分配乙個儲存空間

3.如果在c檔案中宣告巨集,結構體,函式等,那麼我要在另乙個c檔案中引用相應的巨集,結構體,就必須再做一次重複的工作,如果我改了乙個c檔案中的乙個宣告,那麼又忘了改其它c檔案中的宣告,這不就出了大問題了,程式的邏輯就變成了你不可想象的了,如果把這些公共的東東放在乙個標頭檔案中,想用它的c檔案就只需要引用乙個就ok了!!!這樣豈不方便,要改某個宣告的時候,只需要動一下頭檔案就行了

4.在標頭檔案中宣告結構體,函式等,當你需要將你的**封裝成乙個庫,讓別人來用你的**,你又不想公布原始碼,那麼人家如何利用你的庫呢?也就是如何利用你的庫中的各個函式呢??一種方法是公布原始碼,別人想怎麼用就怎麼用,另一種是提供標頭檔案,別人從頭檔案中看你的函式原型,這樣人家才知道如何呼叫你寫的函式,就如同你呼叫printf函式一樣,裡面的引數是怎樣的??你是怎麼知道的??還不是看人家的標頭檔案中的相關宣告啊!!!當然這些東東都成了c標準,就算不看人家的標頭檔案,你一樣可以知道怎麼使用

關於time h標頭檔案

編輯 1 2 3 4 5 6 7 8 include include intmain 編輯 time t time time t timer 得到從標準計時點 一般是1970年1月1日午夜 到當前時間的秒數。clock t clock void 得到從程式啟動到此次 函式呼叫時累計的毫秒數。編輯 函...

關於unistd標頭檔案

unistd.h 是 c 和 c 程式語言中提供對 posix 作業系統 api的訪問功能的 標頭檔案的名稱。該標頭檔案由 posix.1 標準 單一unix規範的基礎 提出,故所有遵循該標準的作業系統和 編譯器均應提供該標頭檔案 如 unix 的所有官方版本,包括 mac os x linux 等...

關於errno標頭檔案

看見網上很多地方都用到這個標頭檔案,一直不理解,今天找了一些資料,可以方便自己理解 errno是乙個巨集,它定義在對應的標頭檔案裡面,這個在上面的鏈結裡也有說到 errno是乙個全域性變數,它儲存了最近一次的錯誤。我常看見的乙個 是 errno eexisteexist的中文翻譯是錯誤已經存在 也就...