1. 程式人生 > 其它 >C語言基礎5-預編譯部分知識(#include+.h檔案和.c檔案的區別

C語言基礎5-預編譯部分知識(#include+.h檔案和.c檔案的區別

技術標籤:C語言


main()

一個C程式以一個名為mian()的函式為開始進行執行。C程式所有的檔案和依賴庫都被編譯到一個程式檔案內,這個程式檔案內只能包含一個main()程式。作業系統使用這個main函式來作為程式的起始。Main()會返回一個整型值,通常這個程式正常執行時這個整型值為0,如果這個值非0,說明程式在執行時遇到了一些錯誤情況。


函式原型(prototype)

一個函式的”原型“是指定義了函式名和函式變數,但是沒有定義程式體。如果我們想要一個在任意檔案內的呼叫者都可以使用某個函式,那我們就需要讓這個呼叫者”看到“這個函式的原型。舉例來講,下面就是一個Twice()函式和Swap()

函式的原型。函式體是空缺的,因此這個原型定義以分號;結尾

int Twice(int num);
void Swap(int* a, int* b);

在ANSI C中:

  • 一個函式可以被宣告為static,在這種情況下,它只能被在它定義處後面的語句使用,且只能在定義的檔案內使用。使用靜態的函式是不需要宣告原型的(我們可以將static函式定義在程式的開始處),這會減少我們的工作量

//待補充


預處理

在C原始檔被提交給編譯器前,會進行預處理步驟,兩個和預處理有關的最常用的C指令是#define和#include

  • #define
    #define MAX 100

  • #include
    #include


.h檔案和.c檔案

一個C語言的傳統是,對於一個包含多個函式的"foo.c"檔案來說:

  • 會有一個額外的"foo.h"檔案包含著"foo.c"的函式原型,這些函式可能在其他地方被呼叫。"foo.c"的一些只會被內部使用的函式,應該被宣告為static。
  • 在"foo.c"檔案的頂部應該包含這樣一個指令#include "foo.h"
  • 任意一個"xxx.c"檔案,如果希望呼叫"foo.c"中的函式,必須通過這樣一個指令進行宣告#include "foo.h"

但是理論上來說c檔案與h檔案裡的內容,只要是c語言所支援的,無論寫什麼都可以,比如在h檔案中寫函式體,只要在任何一個C檔案包含此.h檔案就可以將這個函式編譯成目標檔案的一部分(編譯是以C檔案為單位的,如果不在任何C檔案中包含此.h檔案的話,這段程式碼就形同虛設),我們可以在C檔案中進行函式宣告,變數宣告,結構體宣告,這也是被允許的。那為何要分成h檔案與c檔案呢?又為何一般都在h檔案中進行函式、變數、巨集和結構體宣告,而在c檔案中去進行變數定義,函式實現呢?原因有下面幾個:

  1. 如果在h檔案中實現一個函式體,那麼如果在多個c檔案中引用它,而且又同時編譯多個c檔案,將其生成的目標檔案連線成一個可執行檔案,在每個引用此h檔案的c檔案所生成的目標檔案中,都有一份這個函式的程式碼,如果這段函式又沒有定義成區域性函式,那麼在連線時,就會發現多個相同的函式,就會報錯
  2. 如果在h檔案中定義全域性變數,並且將此全域性變數賦初值,那麼在多個引用此h檔案的C檔案中同樣存在相同變數名的拷貝,關鍵是此變數被賦了初值,所以編 譯器就會將此變數放入DATA段,最終在連線階段,會在DATA段中存在多個相同的變數,它無法將這些變數統一成一個變數,也就是僅為此變數分配一個空 間,而不是多份空間,假定這個變數在h檔案沒有賦初值,編譯器就會將之放入BSS段,聯結器會對BSS段的多個同名變數僅分配一個儲存空間
  3. 如果在C檔案中宣告巨集,結構體,函式等,那麼我要在另一個C檔案中引用相應的巨集,結構體,就必須再做一次重複的工作,如果我改了一個C檔案中的一個聲 明,那麼又忘了改其它C檔案中的宣告,這不就出了大問題了,程式的邏輯就變成了你不可想象的了,如果把這些公共的東東放在一個頭檔案中,想用它的C檔案就 只需要引用一個就OK了!!!這樣豈不方便,要改某個宣告的時候,只需要動一下h檔案就行了

其實,從C編譯器角度看,.h和.c皆是浮雲,就是改名為.txt、.doc也沒有大的分別。換句話說,就是.h和.c沒啥必然聯絡。.h中一般放的是同名.c檔案中定義的變數、陣列、函式的宣告,需要讓.c外部使用的宣告。這個宣告有啥用?只是讓需要用這些宣告的地方方便引用。因為#include “xx.h” 這個巨集其實際意思就是把當前這一行刪掉,把xx.h 中的內容原封不動的插入在當前行的位置。由於想寫這些函式宣告的地方非常多(每一個呼叫xx.c 中函式的地方,都要在使用前宣告一下子),所以用#include “xx.h” 這個巨集就簡化了許多行程式碼——讓前處理器自己替換好了。也就是說,xx.h 其實只是讓需要寫xx.c 中函式宣告的地方呼叫(可以少寫幾行字),至於include 這個.h 檔案是誰,是.h 還是.c,還是與這個.h 同名的.c,都沒有任何必然關係。
這樣你可能會說:啊?那我平時只想呼叫xx.c 中的某個函式,卻include了xx.h 檔案,豈不是巨集替換後出現了很多無用的宣告?沒錯,確實引入了很多垃圾 ,但是它卻省了你不少筆墨,並且整個版面也看起來清爽的多。魚與熊掌不可得兼,就是這個道理。反正多些宣告(.h一般只用來放宣告,而放不定義,參見拙著“過馬路,左右看”)也無害處,又不會影響編譯,何樂而不為呢?