<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jasmine214--love

    只有當你的內心總是充滿快樂、美好的愿望和寧靜時,你才能擁有強壯的體魄和明朗、快樂或者寧靜的面容。
    posts - 731, comments - 60, trackbacks - 0, articles - 0

    C語言中的.h文件的作用

    Posted on 2011-03-04 17:03 幻海藍夢 閱讀(15957) 評論(5)  編輯  收藏 所屬分類: C語言學習

    C語言中的.h文件和我認識由來已久,其使用方法雖不十分復雜,但我卻是經過了幾個月的“不懂”時期,幾年的“一知半解”時期才逐漸認識清楚他的本來面目。揪其原因,我的 駑鈍和好學而不求甚解固然是原因之一,但另外還有其他原因。原因一:對于較小的項目,其作用不易被充分開發,換句話說就是即使不知道他的詳細使用方法,項 目照樣進行,程序在計算機上照樣跑。原因二:現在的各種C語言書籍都是只對C語言的語法進行詳細的不能再詳細的說明,但對于整個程序的文件組織構架卻只字不提,找了好幾本比較著名的C語言著作,卻沒有一個把.h文件的用法寫的比較透徹的。下面我就斗膽提筆,來按照我對.h的認識思路,向大家介紹一下。
     
    讓我們的思緒乘著時間機器回到大學一年級。C原來老師正在講臺上講著我們的第一個C語言程序: Hello world!

     文件名 First.c
    main()
    {
         printf(“Hello world!”);
    }
         例程-1

    看看上面的程序,沒有.h文件。是的,就是沒有,世界上的萬物都是經歷從沒有到有的過程的,我們對.h的認識,我想也需要從這個步驟開始。這時確實不需要.h文件,因為這個程序太簡單了,根本就不需要。那么如何才能需要呢?讓我們把這個程序變得稍微復雜些,請看下面這個,

    文件名 First.c
     
     printStr()
    {
         printf(“Hello world!”);
    }
    main()
    {
    printStr();
    }
         例程-2

     
    還是沒有, 那就讓我們把這個程序再稍微改動一下.
     
    文件名 First.c
    main()
    {
    printStr();
    }
     
     
     printStr()
    {
         printf(“Hello world!”);
    }
         例程-3
     
    等等,不就是改變了個順序嘛, 但結果確是十分不同的. 讓我們編譯一下例程-2和例程-3,你會發現例程-3是編譯不過的.這時需要我們來認識一下另一個C語言中的概念:作用域.
    我們在這里只講述與.h文件相關的頂層作用域, 頂層作用域就是從聲明點延伸到源程序文本結束, printStr()這個函數來說,他沒有單獨的聲明,只有定義,那么就從他定義的行開始,first.c文件結束, 也就是說,在在例程-2main()函數的引用點上,已經是他的作用域. 例程-3main()函數的引用點上,還不是他的作用域,所以會編譯出錯. 這種情況怎么辦呢? 有兩種方法 ,一個就是讓我們回到例程-2, 順序對我們來說沒什么, 誰先誰后不一樣呢,只要能編譯通過,程序能運行, 就讓main()文件總是放到最后吧. 那就讓我們來看另一個例程,讓我們看看這個方法是不是在任何時候都會起作用.
    文件名 First.c
       play2()
       {
     ……………….
     play1();
     ………………..
     
       }
       play1(){
        ……………..
     play2();      
        ……………………
       }
    main()
    {
    play1();
    }
    例程-4
     
    也許大部分都會看出來了,這就是經常用到的一種算法, 函數嵌套, 那么讓我們看看, play1play2這兩個函數哪個放到前面呢?
     
    這時就需要我們來使用第二種方法,使用聲明.
    文件名 First.c
    play1();
    play2();
    play2()
    {
     ……………….
     play1();
     ………………..
        }
        play1()
        {
         …………………….
     play2();
     ……………………
        }
    main()
    {
    play1();
    }
    例程-4
     
    經歷了我的半天的嘮叨, 加上四個例程的說明,我們終于開始了用量變引起的質變, 這篇文章的主題.h文件快要出現了。
    一個大型的軟件項目,可能有幾千個,上萬個play, 而不只是play1,play2這么簡單, 這樣就可能有N個類似 play1(); play2(); 這樣的聲明, 這個時候就需要我們想辦法把這樣的play1(); play2(); 也另行管理, 而不是把他放在.c文件中, 于是.h文件出現了.
     
    文件名 First.h
    play1();
    play2();
    文件名 First.C
    #include “first.h”
    play2()
    {
     ……………….
     play1();
     ………………..
    }
        play1();
    {
    ……………………..
         play2();
      ……………………
    }
    main()
    {
    play1();
    }
    例程-4
     
    各位有可能會說,這位janders大蝦也太羅嗦了,上面這些我也知道, 你還講了這么半天, 請原諒, 如果說上面的內容80%的人都知道的話,那么我保證,下面的內容,80%的人都不完全知道. 而且這也是我講述一件事的一貫作風,我總是想把一個東西說明白,讓那些剛剛接觸C的人也一樣明白.
    上面是.h文件的最基本的功能那么.h文件還有什么別的功能呢? 讓我來描述一下我手頭的一個項目吧.
     
    這個項目已經做了有10年以上了,具體多少年我們部門的人誰都說不太準確,況且時間并不是最主要的,不再詳查了。是一個通訊設備的前臺軟件, 源文件大小共 51.6M, 大小共1601個文件, 編譯后大約10M, 其龐大可想而知在這里充斥著錯綜復雜的調用關系,如在second.c中還有一個函數需要調用first.c文件中的play1函數, 如何實現呢?
     
    Second.h 文件
     
    play1();
     
    second.c文件
     
    ***()
    {
    …………….
    Play();
    ……………….
    }
    例程-5
     
    second.h文件內聲明play1函數,怎么能調用到first.c文件中的哪個play1函數中呢? 是不是搞錯了,沒有搞錯, 這里涉及到c語言的另一個特性:存儲類說明符.
    C語言的存儲類說明符有以下幾個, 我來列表說明一下

      

     說明符  用    法
     Auto  只在塊內變量聲明中被允許, 表示變量具有本地生存期.
     Extern  出現在頂層或塊的外部變量函數與變量聲明中,表示聲明的對象具有靜態生存期, 連接程序知道其名字.
     Static  可以放在函數與變量聲明中,在函數定義時,只用于指定函數名,而不將函數導出到鏈接程序,在函數聲明中,表示其后邊會有定義聲明的函數,存儲類型static.在數據聲明中,總是表示定義的聲明不導出到連接程序.
     

    無疑, 在例程-5中的second.hfirst.h,需要我們用extern標志符來修飾play1函數的聲明,這樣,play1()函數就可以被導出到連接程序, 也就是實現了無論在first.c文件中調用,還是在second.c文件中調用,連接程序都會很聰明的按照我們的意愿,把他連接到first.c文件中的play1函數的定義上去, 而不必我們在second.c文件中也要再寫一個一樣的play1函數.
    但隨之有一個小問題, 在例程-5,我們并沒有用extern標志符來修飾play1, 這里涉及到另一個問題, C語言中有默認的存儲類標志符. C99中規定, 所有頂層的默認存儲類標志符都是extern . 原來如此啊哈哈回想一下例程-4, 也是好險, 我們在無知的情況下, 竟然也誤打誤撞,用到了extern修飾符, 否則在first.h中聲明的play1函數如果不被連接程序導出,那么我們在在play2()中調用他時, 是找不到其實際定義位置的 .
     
    那么我們如何來區分哪個頭文件中的聲明在其對應的.c文件中有定義,而哪個又沒有呢?這也許不是必須的,因為無論在哪個文件中定義,聰明的連接程序都會義無返顧的幫我們找到,并導出到連接程序, 但我覺得他確實必要的. 因為我們需要知道這個函數的具體內容是什么,有什么功能, 有了新需求后我也許要修改他,我需要在短時間內能找到這個函數的定義, 那么我來介紹一下在C語言中一個人為的規范:
     
    .h文件中聲明的函數,如果在其對應的.c文件中有定義,那么我們在聲明這個函數時,不使用extern修飾符, 如果反之,則必須顯示使用extern修飾符.
     
    這樣,C語言的.h文件中,我們會看到兩種類型的函數聲明. extern,還不帶extern, 簡單明了,一個是引用外部函數,一個是自己生命并定義的函數.
    最終如下:
    Second.h 文件
     
    Extern play1();
     
     
    上面洋洋灑灑寫了那么多都是針對函數的,而實際上.h文件卻不是為函數所御用的. 打開我們項目的一個.h文件我們發現除了函數外,還有其他的東西, 那就是全局變量
     
    在大型項目中,對全局變量的使用不可避免, 比如,first.c中需要使用一個全局變量G_test, 那么我們可以在first.h,定義 TPYE G_test. 與對函數的使用類似, second.c中我們的開發人員發現他也需要使用這個全局變量, 而且要與first.c中一樣的那個, 如何處理? ,我們可以仿照函數中的處理方法, second.h中再次聲明TPYE G_test, 根據extern的用法,以及c語言中默認的存儲類型, 在兩個頭文件中聲明的TPYE G_test,其實其存儲類型都是extern, 也就是說不必我們操心, 連接程序會幫助我們處理一切. 但我們又如何區分全局變量哪個是定義聲明,哪個是引用聲明呢?這個比函數要復雜一些, 一般在C語言中有如下幾種模型來區分:
     
    1、初始化語句模型
    頂層聲明中,存在初始化語句是,表示這個聲明是定義聲明,其他聲明是引用聲明。C語言的所有文件之中,只能有一個定義聲明。
    按照這個模型,我們可以在first.h中定義如下TPYE G_test=1;那么就確定在first中的是定義聲明,在其他的所有聲明都是引用聲明。
    2省略存儲類型說明
    在這個模型中,所有引用聲明要顯示的包括存儲類extern,而每個外部變量的唯一定義聲明中省略存儲類說明符。
    這個與我們對函數的處理方法類似,不再舉例說明。
     
        這里還有一個需要說明,本來與本文并不十分相關,但前一段有個朋友遇到此問題,相信很多人都會遇到,那就是數組全局變量。
     
    他遇到的問題如下:
    在聲明定義時,定義數組如下:
    int G_glob[100];
     
    在另一個文件中引用聲明如下:
    int * G_glob;
     
    vc中,是可以編譯通過的,這種情況大家都比較模糊并且需要注意,數組與指針類似,但并不等于說對數組的聲明起變量就是指針。上面所說的的程序在運行時發現了問題,在引用聲明的那個文件中,使用這個指針時總是提示內存訪問錯誤,原來我們的連接程序并不把指針與數組等同,連接時,也不把他們當做同一個定義,而是認為是不相關的兩個定義,當然會出現錯誤。正確的使用方法是在引用聲明中聲明如下:
     
    int G_glob[100];
     
    并且最好再加上一個extern,更加明了。
     
    extern int G_glob[100];
     
        另外需要說明的是,在引用聲明中由于不需要涉及到內存分配,可以簡化如下,這樣在需要對全局變量的長度進行修改時,不用把所有的引用聲明也全部修改了。
     
    extern int G_glob[];
     
        C語言是現今為止在底層核心編程中,使用最廣泛的語言,以前是,以后也不會有太大改變,雖然現在java,.net等語言和工具對c有了一定沖擊,但我們看到在計算機最為核心的地方,其他語言是無論如何也代替不了的,而這個領域也正是我們對計算機癡迷的程序員所向往的。



    好了,看完文章,對與C語言頭文件的作用應該有了跟多的理解吧,如果這些你原本都知道了,那么僅當是溫習一下而已,如果原本不知道,那么恭喜你,現在又學到一些技巧和知識.

    對于全局變量的定義和聲明,其實還有另外一個解決的方法,聰明的你可能早已經猜到了:),沒錯,就是用宏定義的技巧實現.比如a.h文件當中有:
    #ifdef AAA
     int i=0;
    #else
     int 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一次,否則在連接這些目標文件時會出現
    重復定義的錯誤,即使你的單獨目標文件編譯沒有任何的問題.

    當然,這里說的僅僅是對全局變量的聲明技巧,強烈的推介大家在頭文件中使用宏定義實現對整個頭文件的防止重復包含,當然了,這個技巧大多數的c語言程序員都懂.
    #ifndef XXX
    #define XXX

    #endif

    這樣做會讓你的程序更加穩健,很大程度上減少了不必要的麻煩...

    最后給出一點點全局變量使用需要注意的問題,這也僅僅是個建議,或者說一種編程習慣 ;)
    1) 所有全局變量全部以g_開頭,并且盡可能聲明成static類型.
    2) 盡量杜絕跨文件訪問全局變量.如果的確需要在多個文件內訪問同一變量,應該由該變量定義所在文件內提供GET/PUT函數實現.
    3) 全局變量必須要有一個初始值,全局變量盡量放在一個專門的函數內初始化.
    4) 如調用的函數少于三個,請考慮改為局部變量實現.

    如果文中有什么不對的地方,歡迎指正,相互學習:)
    原文:http://blogold.chinaunix.net/u2/75758/showart_1715158.html

    Feedback

    # re: C語言中的.h文件的作用  回復  更多評論   

    2012-08-01 15:27 by 董璐
    2) 盡量杜絕跨文件訪問全局變量.如果的確需要在多個文件內訪問同一變量,應該由該變量定義所在文件內提供GET/PUT函數實現.


    上面這條可否給個示例?非常感謝:donglu918@163.com

    # re: C語言中的.h文件的作用  回復  更多評論   

    2013-10-22 15:15 by seeking
    例程-1 和 例程-2 無法通過,printf函數屬于stdio.h頭文件,如果不加,怎么引用printf?這篇文章主題很好,但是內容正確性讓人置疑!

    # re: C語言中的.h文件的作用  回復  更多評論   

    2014-12-16 20:42 by dodo
    文章是不錯。但是內容有錯誤的。
    比如aaa.h文件里面包含的是aaa.c文件可以給外部調用的函數定義.
    而bbb.c文件要調用aaa.c里面的函數,只需要包含aaa.h頭文件即可。
    而你講的方法是讓我們在bbb.h里面extern aaa.c里面的函數。這是不可取的。
    另外aaa.h不推薦定義了aaa.c文件里面所有的函數聲明,而只應該聲明給外部調用的函數。不給外部調用的函數放在aaa.c文件頂部位置。

    # re: C語言中的.h文件的作用  回復  更多評論   

    2015-12-01 20:14 by asdf
    例程5什么亂碼七遭的,寫完有沒有自己check,容易給人造成誤解

    # re: C語言中的.h文件的作用  回復  更多評論   

    2015-12-01 20:17 by asdf
    越往下看,越發現寫的一團混亂,完全看不懂,浪費我10分鐘的時間,真垃圾啊
    主站蜘蛛池模板: 小草在线看片免费人成视久网| 中文字幕亚洲综合久久综合| 亚洲精品美女久久7777777| 日韩精品免费视频| 亚洲精品国产精品乱码视色 | 西西大胆无码视频免费| 亚洲明星合成图综合区在线| 99re这里有免费视频精品| 亚洲欧洲日产国码久在线观看| 久久国产精品一区免费下载| 99热亚洲色精品国产88| 成人免费777777| 亚洲精品视频在线播放| 男人进去女人爽免费视频国产| 欧洲 亚洲 国产图片综合| 亚洲中文久久精品无码| 另类免费视频一区二区在线观看| 亚洲欧洲日产国码久在线观看| 日本特黄特色aa大片免费| 视频一区二区三区免费观看| 亚洲五月午夜免费在线视频| 中文在线免费观看| 亚洲AV人无码综合在线观看| 国产精品成人免费福利| 亚洲欧美日韩一区二区三区| 国产一级做a爱免费视频| 韩国免费a级作爱片无码| 亚洲色一区二区三区四区| 亚洲精品无码AV中文字幕电影网站| 美女无遮挡拍拍拍免费视频 | MM131亚洲国产美女久久| 青青草免费在线视频| 国产精品亚洲精品日韩动图| 亚洲精品亚洲人成在线观看下载 | 免费大片黄手机在线观看| 一级毛片a免费播放王色电影| 人人狠狠综合久久亚洲88| 国产麻豆视频免费观看| 国产精品白浆在线观看免费| 一个人看的免费观看日本视频www| 亚洲v高清理论电影|