C語言頭文件的作用_第1頁
C語言頭文件的作用_第2頁
C語言頭文件的作用_第3頁
C語言頭文件的作用_第4頁
C語言頭文件的作用_第5頁
已閱讀5頁,還剩3頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、C語言頭文件的作用最近在工作當中遇到了一點小問題,關于C語言頭文件的應用問題,主要還是關于全局變量的定義和聲明問題.學習c語言已經有好幾年了,工作使用也近半年了,但是對于這部分的東西的確還沒有深入的思考過.概念上還是比較模糊的,只是之前的使用大多比較簡單,并沒有牽涉到太復雜的工程,所以定義和聲明還是比較簡單而明了了的.但是最近的大工程讓我在這方面吃到了一點點苦頭,雖然看了別人的代碼能夠很快的改正,但是這些改正背后的原因卻不知道.我想大多數喜歡c語言的程序員應該是和我一樣的,總喜歡去追究程序問題背后的底層原因,而這也恰恰是我喜歡c語言的最根本的原因.今天看過janders老兄在csdn上的一篇文

2、章后,理解的確加深了很多,而且還學到一些以前不怎 么知道的知識.現將文章轉載過來,并對文章當中的一些拼寫錯誤做了簡單的糾正,同時對文字及布局做了少許修改.(如果想看原文的,請參考本文底部的鏈接.)C語言中的.h文件和我認識由來已久,其使用方法雖不十分復雜,但我卻 是經過了幾個月的 不懂”時期,幾年的一知半解”時期才逐漸認識清楚他的本來 面目。揪其原因,我的駑鈍和好學而不求甚解固然是原因之一,但另外還有其他原因。原因一:對于較小的項目,其作用不易被充分開發,換句話說就是即使不 知道他的詳細使用方法,項目照樣進行,程序在計算機上照樣跑。原因二:現在 的各種C語言書籍都是只對C語言的語法進行詳細的不

3、能再詳細的說明,但對 于整個程序的文件組織構架卻只字不提,找了好幾本比較著名的C語言著作,卻沒有一個把.h文件的用法寫的比較透徹的。下面我就斗膽提筆,來按照我對.h 的認識思路,向大家介紹一下。讓我們的思緒乘著時間機器回到大學一年級。C原來老師正在講臺上講著我 們的第一個C語言程序:Hello world!文件名First.cmai n()printf( “ Hello world! ” );例程-1看看上面的程序,沒有.h文件。是的,就是沒有,世界上的萬物都是經歷 從沒有到有的過程的,我們對.h的認識,我想也需要從這個步驟開始。這時確 實不需要.h文件,因為這個程序太簡單了,根本就不需要。那

4、么如何才能需要呢?讓我們把這個程序變得稍微復雜些,請看下面這個,文件名First.cprin tStr()printf( “ Hello world! ” );mai n()prin tStr();例程-2還是沒有,那就讓我們把這個程序再稍微改動一下文件名First.cmai n()prin tStr();prin tStr()printf( “ Hello world! ” ); 例程-3等等,不就是改變了個順序嘛,但結果確是十分不同的.讓我們編譯一下例程-2 和例程-3,你會發現例程-3是編譯不過的.這時需要我們來認識一下另一個 C語 言中的概念:作用域.我們在這里只講述與.h文件相關的頂

5、層作用域,頂層作用域就是從聲明點 延伸到源程序文本結束,就printStr()這個函數來說,他沒有單獨的聲明,只有 定義,那么就從他定義的行開始,到first.c文件結束,也就是說,在在例程-2的 main()函數的引用點上,已經是他的作用域.例程-3的main()函數的引用點 上,還不是他的作用域,所以會編譯出錯.這種情況怎么辦呢?有兩種方法,一 個就是讓我們回到例程-2,順序對我們來說沒什么,誰先誰后不一樣呢,只要能 編譯通過,程序能運行,就讓main()文件總是放到最后吧.那就讓我們來看另一 個例程,讓我們看看這個方法是不是在任何時候都會起作用.文件名First.cplay2()Play

6、1();Play1()Play2();mai n()Play1();例程-4也許大部分都會看出來了,這就是經常用到的一種算法,函數嵌套,那么讓 我們看看,playl和play2這兩個函數哪個放到前面呢?這時就需要我們來使用第二種方法,使用聲明文件名First.cPlay1();Play2();Play2()Play1(); Play1()Play2();mai n()Play1();例程-4經歷了我的半天的嘮叨,加上四個例程的說明,我們終于開始了用量變引起 的質變,這篇文章的主題.h文件快要出現了。一個大型的軟件項目,可能有幾千個,上萬個Play,而不只是play1,play2 這么簡單,這樣

7、就可能有N個類似play1(); play2(); 這樣的聲明,這個時候 就需要我們想辦法把這樣的play1(); play2(); 也另行管理,而不是把他放 在.c文件中,于是.h文件出現了 .文件名First.hplay1();play2();文件名First.C#in elude “ first.h ”play2()play1(); play1();play2();mai n()play1();例程-4各位有可能會說,這位janders大蝦也太羅嗦了,上面這些我也知道,你還 講了這么半天,請原諒,如果說上面的內容80%的人都知道的話,那么我保證, 下面的內容,80%的人都不完全知道.而且

8、這也是我講述一件事的一貫作風,我 總是想把一個東西說明白,讓那些剛剛接觸C的人也一樣明白.上面是.h文件的最基本的功能,那么.h文件還有什么別的功能呢?讓我來 描述一下我手頭的一個項目吧.這個項目已經做了有10年以上了,具體多少年我們部門的人誰都說不太準 確,況且時間并不是最主要的,不再詳查了。是一個通訊設備的前臺軟件,源文件大小共51.6M,大小共1601個文件,編譯后大約10M,其龐大可想而 知,在這里充斥著錯綜復雜的調用關系,如在second.c中還有一個函數需要調 用first.c文件中的play1函數,如何實現呢?Seco nd.h 文件play1();sec on d.c 文件Pl

9、ay();例程-5在second.h文件內聲明playl函數,怎么能調用到first.c文件中的哪個 playl函數中呢?是不是搞錯了,沒有搞錯,這里涉及到c語言的另一個特性 存儲類說明符.C語言的存儲類說明符有以下幾個,我來列表說明一下說明符用法Auto只在塊內變量聲明中被允許,表示變量具有本地生存期.Exter n出現在頂層或塊的外部變量函數與變量聲明中,表示聲明的對象具有靜態生存期,連接程序知道其名字.Static可以放在函數與變量聲明中,在函數定義時,只用于指定函數名,而不 將函數導出到鏈接程序,在函數聲明中,表示其后邊會有定義聲明的函 數,存儲類型static.在數據聲明中,總是表示

10、疋義的聲明不導出到連接 程序.無疑,在例程-5中的second.h和first.h中,需要我們用extern標志符來 修飾play1函數的聲明,這樣,play1()函數就可以被導出到連接程序,也就是實 現了無論在first.c文件中調用,還是在second.c文件中調用,連接程序都會很聰 明的按照我們的意愿,把他連接到first.c文件中的play1函數的定義上去,而不 必我們在second.c文件中也要再寫一個一樣的 play1函數.但隨之有一個小問題,在例程-5中,我們并沒有用extern標志符來修飾 play1啊,這里涉及到另一個問題,C語言中有默認的存儲類標志符.C99中規 定,所有頂

11、層的默認存儲類標志符都是extern .原來如此啊,哈哈.回想一下例程-4,也是好險,我們在無知的情況下,竟然也誤打誤撞,用到了 extern修飾 符,否則在first.h中聲明的play1函數如果不被連接程序導出,那么我們在在 play2()中調用他時,是找不到其實際定義位置的.那么我們如何來區分 哪個頭文件中的聲明在其對應的.c文件中有定義,而哪 個又沒有呢?這也許不是必須的,因為無論在哪個文件中定義,聰明的連接程序 都會義無返顧的幫我們找到,并導出到連接程序,但我覺得他確實必要的.因為 我們需要知道這個函數的具體內容是什么,有什么功能,有了新需求后我也許要 修改他,我需要在短時間內能找到

12、這個函數的定義,那么我來介紹一下在C語言 中一個人為的規范:在.h文件中聲明的函數,如果在其對應的.C文件中有定義,那么我們 在聲明這個函數時,不使用extern修飾符,如果反之,則必須顯示使用 extern修飾符.這樣,在C語言的.h文件中,我們會看到兩種類型的函數聲明.帶extern的, 還不帶extern的,簡單明了,一個是引用外部函數,一個是自己生命并定義的函 數.最終如下:Seco nd.h 文件Extern play1();上面洋洋灑灑寫了那么多都是針對函數的,而實際上.h文件卻不是為函數所 御用的.打開我們項目的一個.h文件我們發現除了函數外,還有其他的東西,那 就是全局變量.在

13、大型項目中,對全局變量的使用不可避免,比如,在first.c中需要使用一 個全局變量G_test,那么我們可以在first.h中,定義TPYE G_test. 與對函數 的使用類似,在second.c中我們的開發人員發現他也需要使用這個全局變量,而且要與first.c中一樣的那個,如何處理?對,我們可以仿照函數中的處理方 法,在sec on d.h 中再次聲明TPYE G_test, 根據exter n 的用法,以及c語言 中默認的存儲類型,在兩個頭文件中聲明的TPYE G_test,其實其存儲類型都是 extern,也就是說不必我們操心,連接程序會幫助我們處理一切.但我們又如 何區分全局變量

14、哪個是定義聲明,哪個是引用聲明呢?這個比函數要復雜一些, 一般在C語言中有如下幾種模型來區分:1、初始化語句模型頂層聲明中,存在初始化語句是,表示這個聲明是定義聲明,其他聲明是引用聲 明。C語言的所有文件之中,只能有一個定義聲明。按照這個模型,我們可以在first.h中定義如下TPYE G_test=1 ;那么就確定 在first中的是定義聲明,在其他的所有聲明都是引用聲明。2、省略存儲類型說明在這個模型中,所有引用聲明要顯示的包括存儲類extern ,而每個外部變量的唯一定義聲明中省略存儲類說明符。這個與我們對函數的處理方法類似,不再舉例說明。這里還有一個需要說明,本來與本文并不十分相關,但

15、前一段有個朋友遇到 此問題,相信很多人都會遇到,那就是數組全局變量。他遇到的問題如下:在聲明定義時,定義數組如下:int G_glob100;在另一個文件中引用聲明如下:int * G_glob;在vc中,是可以編譯通過的,這種情況大家都比較模糊并且需要注意,數 組與指針類似,但并不等于說對數組的聲明起變量就是指針。上面所說的的程序在運行時發現了問題,在引用聲明的那個文件中,使用這個指針時總是提示內存 訪問錯誤,原來我們的連接程序并不把指針與數組等同,連接時,也不把他們當做同一個定義,而是認為是不相關的兩個定義,當然會出現錯誤。正確的使用方 法是在引用聲明中聲明如下:int G_glob100

16、;并且最好再加上一個 extern,更加明了。extern int G_glob100;另外需要說明的是,在引用聲明中由于不需要涉及到內存分配,可以簡化如 下,這樣在需要對全局變量的長度進行修改時,不用把所有的引用聲明也全部修改了。exter n int G_glob;C語言是現今為止在底層核心編程中,使用最廣泛的語言,以前是,以后也 不會有太大改變,雖然現在java,.net等語言和工具對c有了一定沖擊,但我們 看到在計算機最為核心的地方,其他語言是無論如何也代替不了的, 而這個領域 也正是我們對計算機癡迷的程序員所向往的。好了 ,看完文章,對與c語言頭文件的作用應該有了跟多的理解吧,如果這

17、些你原本都知道了 ,那么僅當是溫習一下而已,如果原本不知道,那么恭喜你,現在又學到一些技巧和知識.對于全局變量的定義和聲明,其實還有另外一個解決的方法,聰明的你可能早已經猜到了 :),沒錯,就是用宏 定義的技巧實現比如a.h文件當中有:#ifdef AAAint i=0;#elseint i;#endif那么,在a.c文件當中,有如下語句:#define AAA#include "a.h"而對于其他的任何包含a.h文件的頭文件或者.c源文件,只需要直接包含a.h就行了#include "a.h"這樣就可以達到在a.c文件當中定義變量一次,而在其他的文件當中聲明該變量的目的.當然了,你完全可以根據自己的需要來決定在哪個需要包含a.h的文件當中定義宏 AAA,但是我要說的是在同一個工程的不同的需要包含 a.h的文件當中,你只能定義AAA 一次,否則在連接這些目標文件時會出現 重復定義的錯誤,即使你的單獨目標文件編譯沒有任何的問題 .當然,這里說的僅僅是對全局變量的聲明技巧,強烈的推介大家在頭文件中使用宏定義實現對整個頭文件的防止重復包

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論