C語言中 *.c和*.h文件的區別!

         C文件就是C語言系列的源文件,而H文件則是C語言的頭文件,即C系列中存放函數和全局變量的文件,因爲C中的函數是被封裝起來的,即無法看到其代碼。

        子程序不要定義在*.h中。函數定義要放在*.c中,而*.h只做聲明.否則多引用幾次,就會發生函數重複定義的錯誤。*.h只做聲明,編譯後不產生代碼。這樣做目的是爲了實現軟件的模塊化,使軟件結構清晰,而且也便於別人使用你寫的程序。

         純粹用 C 語言語法的角度,你當然可以在*.h 中放任何東西,因爲 #include 完全等價於把*.h 文件 Ctrl-C Ctrl-V 到*.c 中,*.h 中應該都是一些宏定義和變量、函數聲明,告訴別人你的程序“能幹什麼、該怎麼用”。*.c 中是所有變量和函數的定義,告訴計算機你的程序“該怎麼實現”。當然,如果一個*.h 被多個*.c 包含,而且*.h 中有對象(變量或函數)的定義,就會發生重複定義的錯誤了,聲明可以無窮多次,定義只能一次。
        一般來說,一個C文件應該是一個模塊,如果你的程序僅僅有一個模塊(僅僅一個C文件),就可以不用建立H文件了。否則你的模塊肯定不是獨立的,你的模塊裏面的實現要被別的模塊調用。這個時候你最好生成一個頭文件(H文件),在頭文件裏面可以聲明你的那些函數是公共的。當別的模塊包含你的頭文件後,就可以使用你的公共聲明瞭。 
        一個C對應一個H,這樣管理起來方便,比如你有一個"my.c",那麼就再添加一個"my.h":

#ifndef   _MY_H

#define  _MY_H

extern void my(void);

#endif

其實在H文件裏寫函數也無所謂,只是不符合習慣而已。只要按照以上的格式寫,一個H文件添加多少次都無所謂,呵~
 

       Come from: http://topic.csdn.net/t/20060429/15/4723725.html
       簡單的說,其實要理解C文件與H文件有什麼不同之處,首先需要弄明白編譯器的工作過程,一般說來編譯器會做以下幾個過程: 
       1.預處理階段
       2.詞法與語法分析階段
       3.編譯階段,首先編譯成純彙編語句,再將之彙編成跟CPU相關的二進制碼,生成各個目標文件
       4.連接階段,將各個目標文件中的各段代碼進行絕對地址定位,生成跟特定平臺相關的可執行文件,當然,最後還可以用objcopy生成純二進制碼,也就是去掉了文件格式信息。

        編譯器在編譯時是以C文件爲單位進行的,也就是說如果你的項目中一個C文件都沒有,那麼你的項目將無法編譯,連接器是以目標文件爲單位,它將一個或多個目標文件進行函數與變量的重定位,生成最終的可執行文件,在PC上的程序開發,一般都有一個main函數,這是各個編譯器的約定,當然,你如果自己寫連接器腳本的話,可以不用main函數作爲程序入口!!!

       有了這些基礎知識,再言歸正傳,爲了生成一個最終的可執行文件,就需要一些目標文件,也就是需要C文件,而這些C文件中又需要一個main函數作爲可執行程序的入口,那麼我們就從一個C文件入手,假定這個C文件內容如下: 
#include <stdio.h> 
#include "mytest.h"

int main(int argc,char **argv)
{
        test = 25; 
        printf("test.................%d/n",test);
}

mytest.h文件的內容: 
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標準,就算不看人家的頭文件,你一樣可以知道怎麼使用 。

例子:
//a.h
void foo();

//a.c
#include "a.h"  //
我的問題出來了:這句話是要,還是不要?
void foo()
{
      return;
}

//main.c
#include "a.h"
int main(int argc, char *argv[])
{
      foo();
 
  return 0;
}

    針對上面的代碼,請回答三個問題:
1.a.c
中的 #include "a.h" 這句話是不是多餘的?爲什麼經常見 xx.c 裏面 include 對應的 xx.h
2.
如果 a.c 中不寫,那麼編譯器是不是會自動把 .h 文件裏面的東西跟同名的 .c 文件綁定在一起?
3.
第三個問題我給他改了一下:如果 a.c 中不寫include<>,那麼編譯器是不是會自動把 .h 文件裏面的東西跟同名的.c文件綁定在一起?

下面是一位牛人的原話:

    從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一般只用來放聲明,而放不定義,參見拙著過馬路,左右看)也無害處,又不會影響編譯,何樂而不爲呢?
翻回頭再看上面的3個問題,很好解答了吧?

它的解答如下:
1.不一定。這個例子中顯然是多餘的。但是如果.c中的函數也需要調用同個.c中的其它函數,那麼這個.c往往會include同名的.h,這樣就不需要爲聲明和調用順序而發愁了(C語言要求使用之前必須聲明,而include同名.h一般會放在.c的開頭)。有很多工程甚至把這種寫法約定爲代碼規範,以規範出清晰的代碼來。
2.
答:1中已經回答過了。
3.
答:不會。問這個問題的人絕對是概念不清,要不就是想混水摸魚。非常討厭的是中國的很多考試出的都是這種爛題,生怕別人有個清楚的概念了,絕對要把考生搞暈。
 

補充一下:

      一個程序僅有一個H文件,沒有C文件,可以嗎?

      答案是肯定的,不可以!在VC6.0下可以看到編譯按鈕的顏色!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章