關於.H和.C的文章

本文轉自http://topic.csdn.net/u/20100426/18/6fe0815f-8575-4b37-ae4d-adcd9500fa3e.html

In general, header files contain declarations and definitions pertaining to functions defined elsewhere. In general, for any external function you call, there will be a header file which contains the prototype declaration for the function, along with any other definitions of constants or structures which you need to use when calling that function.  


Header files let you keep shared declarations and definitions in a single central place, when those declarations and definitions must be kept consistent between separately-compiled source files.  

Note that a header file does not actually contain the definitions of any external functions. After using #include to reference the header file describing some external library function(s), in many cases you will also have to explicitly request that a copy of that external library be read by the linker during the last stage of the compilation process so that the function definitions themselves can be read out of the library and copied into your program's executable.  
簡單的說其實要理解C文件與頭文件(即.h)有什麼不同之處,首先需要弄明白編譯器的工作過程,一般說來編譯器會做以下幾個過程:

1.預處理階段
2.詞法與語法分析階段
3.編譯階段,首先編譯成純彙編語句,再將之彙編成跟CPU相關的二進制碼,生成各個目標文件 (.obj文件)
4.連接階段,將各個目標文件中的各段代碼進行絕對地址定位,生成跟特定平臺相關的可執行文件,當然,最後還可以用objcopy生成純二進制碼,也就是去掉了文件格式信息。(生成.exe文件)
編譯器在編譯時是以C文件爲單位進行的,也就是說如果你的項目中一個C文件都沒有,那麼你的項目將無法編譯,連接器是以目標文件爲單位,它將一個或多個目標文件進行函數與變量的重定位,生成最終的可執行文件,在PC上的程序開發,一般都有一個main函數,這是各個編譯器的約定,當然,你如果自己寫連接器腳本的話,可以不用main函數作爲程序入口!!!!
 (main .c文件 目標文件 可執行文件 )
有了這些基礎知識,再言歸正傳,爲了生成一個最終的可執行文件,就需要一些目標文件,也就是需要C文件,而這些C文件中又需要一個main函數作爲可執行程序的入口,那麼我們就從一個C文件入手,假定這個C文件內容如下:
#include <stdio.h>
#include "mytest.h"
int main(int argc,char **argv)
{
 test = 25;
 printf("test.................%d\n",test);
}
頭文件內容如下:
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標準,就算不看人家的頭文件,你一樣可以知道怎麼使用。

自己的一些總結:

1、在.h文件中,應該聲明結構體、函數、變量,注意變量別初始化,否則會出現上邊說的第二個錯誤。

2、在.h文件中,應該加上

#ifndef head_h
#define head_h
//定義
#endif

這樣才能避免出現上邊的第一個錯誤。

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