<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 頭文件與之實(shí)現文件的的關(guān)系

頭文件與之實(shí)現文件的的關(guān)系

作者: 時(shí)間:2016-11-26 來(lái)源:網(wǎng)絡(luò ) 收藏
你理解簡(jiǎn)單的含義嗎?

關(guān)于兩者以前的關(guān)系,要從N年以前說(shuō)起了~ long long ago,once aupon a time .......
那是一個(gè)被遺忘的年代,在編譯器只認識.c(.cpp))文件,而不知道.h是何物的年代。
那時(shí)的人們寫(xiě)了很多的.c(.cpp)文件,漸漸地,人們發(fā)現在很多.c(.cpp)文件中的聲明語(yǔ)句就是相同的,但他們卻不得不一個(gè)字一個(gè)字地重復地將這些內容敲入每個(gè).c(.cpp)文件。但更為恐怖的是,當其中一個(gè)聲明有變更時(shí),就需要檢查所有的.c(.cpp)文件,并修改其中的聲明,啊~簡(jiǎn)直是世界末日降臨!
終于,有人(或許是一些人)再不能忍受這樣的折磨,他(們)將重復的部分提取出來(lái),放在一個(gè)新文件里,然后在需要的.c(.cpp)文件中敲入#includeXXXX這樣的語(yǔ)句。這樣即使某個(gè)聲明發(fā)生了變更,也再不需要到處尋找與修改了---世界還是那么美好!
因為這個(gè)新文件,經(jīng)常被放在.c(.cpp)文件的頭部,所以就給它起名叫做“頭文件”,擴展名是.h.
從此,編譯器(其實(shí)是預處理器)就知道世上除了.c(.cpp)文件,還有個(gè).h的文件,以及一個(gè)叫做#include命令。

本文引用地址:http://dyxdggzs.com/article/201611/322063.htm

雖然后來(lái)又發(fā)生很多的變化,但是這樣的用法一直延續至今,只是時(shí)日久遠了,人們便淡忘了當年的緣由罷了。
提到了頭文件,就說(shuō)說(shuō)它的作用吧~
想到了林銳GG寫(xiě)的高質(zhì)量C/C++編程上頭文件的作用的簡(jiǎn)短描述:
(1)通過(guò)頭文件來(lái)調用庫功能。在很多場(chǎng)合,源代碼不便(或不準)向用戶(hù)公布,只要向用戶(hù)提供頭文件和二進(jìn)制的庫即可。用戶(hù)只需要按照頭文件中的接口聲明來(lái)調用庫功能,而不必關(guān)心接口怎么實(shí)現的。編譯器會(huì )從庫中提取相應的代碼。
(2)頭文件能加強類(lèi)型安全檢查。如果某個(gè)接口被實(shí)現或被使用時(shí),其方式與頭文件中的聲明不一致,編譯器就會(huì )指出錯誤,這一簡(jiǎn)單的規則能大大減輕程序員調試、改錯的負擔。


預處理是編譯器的前驅,作用是把存儲在不同文件里的程序模塊集成為一個(gè)完整的源程序.
#include本身只是一個(gè)簡(jiǎn)單的文件包含預處理命令,即為把include的后面文件放到這條命令這里,除此之外,沒(méi)有其它的用處(至少我也樣認為).


我對乾坤一笑兄的觀(guān)點(diǎn),十分贊同,基礎的東東一定要弄明白.
我下面就乾坤一笑兄的例子做講,完備他的一些讓人迷惑不解的時(shí)候~

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


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

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

針對上面的代碼,請回答三個(gè)問(wèn)題:
a.c中的#include "a.h"這句話(huà)是不是多余的?
1.為什么經(jīng)常見(jiàn)xx.c里面include對應的xx.h?
2.如果a.c中不寫(xiě),那么編譯器是不是會(huì )自動(dòng)把.h文件里面的東西跟同名的.c文件綁定在一起?
3.第三個(gè)問(wèn)題我給他改了一下:如果a.c中不寫(xiě)include<>,那么編譯器是不是會(huì )自動(dòng)把.h文件里面的東西跟同名的.c文件綁定在一起?

下面是乾坤一笑的原話(huà):

從C編譯器角度看,.h和.c皆是浮云,就是改名為.txt、.doc也沒(méi)有大的分別。換句話(huà)說(shuō),就是.h和.c沒(méi)啥必然聯(lián)系。.h中一般放的是同名.c文件中定義的變量、數組、函數的聲明,需要讓.c外部使用的聲明。這個(gè)聲明有啥用?只是讓需要用這些聲明的地方方便引用。因為#include "xx.h"這個(gè)宏其實(shí)際意思就是把當前這一行刪掉,把xx.h中的內容原封不動(dòng)的插入在當前行的位置。由于想寫(xiě)這些函數聲明的地方非常多(每一個(gè)調用xx.c中函數的地方,都要在使用前聲明一下子),所以用#include "xx.h"這個(gè)宏就簡(jiǎn)化了許多行代碼——讓預處理器自己替換好了。也就是說(shuō),xx.h其實(shí)只是讓需要寫(xiě)xx.c中函數聲明的地方調用(可以少寫(xiě)幾行字),至于include這個(gè).h文件是誰(shuí),是.h還是.c,還是與這個(gè).h同名的.c,都沒(méi)有任何必然關(guān)系。
這樣你可能會(huì )說(shuō):???那我平時(shí)只想調用xx.c中的某個(gè)函數,卻include了xx.h文件,豈不是宏替換后出現了很多無(wú)用的聲明?沒(méi)錯,確實(shí)引入了很多垃圾,但是它卻省了你不少筆墨,并且整個(gè)版面也看起來(lái)清爽的多。魚(yú)與熊掌不可得兼,就是這個(gè)道理。反正多些聲明(.h一般只用來(lái)放聲明,而放不定義,參見(jiàn)拙著(zhù)“過(guò)馬路,左右看”)也無(wú)害處,又不會(huì )影響編譯,何樂(lè )而不為呢?
翻回頭再看上面的3個(gè)問(wèn)題,很好解答了吧?
它的解答如下:

答:1.不一定。這個(gè)例子中顯然是多余的。但是如果.c中的函數也需要調用同個(gè).c中的其它函數,那么這個(gè).c往往會(huì )include同名的.h,這樣就不需要為聲明和調用順序而發(fā)愁了(C語(yǔ)言要求使用之前必須聲明,而include同名.h一般會(huì )放在.c的開(kāi)頭)。有很多工程甚至把這種寫(xiě)法約定為代碼規范,以規范出清晰的代碼來(lái)。
2.答:1中已經(jīng)回答過(guò)了。
3.答:不會(huì )。問(wèn)這個(gè)問(wèn)題的人絕對是概念不清,要不就是想混水摸魚(yú)。非常討厭的是中國的很多考試出的都是這種爛題,生怕別人有個(gè)清楚的概念了,絕對要把考生搞暈。

over!

在此里要明確一點(diǎn),編譯器是按照編譯單元進(jìn)行編譯的,所謂的編譯單元,是指一個(gè).c文件以及它所include的所有.h文件.最直觀(guān)的理解就是一個(gè)文件,一個(gè)工程中可以包含很多文件,其中有一個(gè)程序的入口點(diǎn),即我們通常所說(shuō)的main()函數(當然也可以沒(méi)有這個(gè)函數,程序照樣能啟動(dòng),詳細見(jiàn)我的blog中).在沒(méi)有這個(gè)程序入口點(diǎn)的情況下,編譯單元只生成目標文件object file(.o文件,windows下叫做.obj).

這個(gè)例子中總共包含了二個(gè)編譯單元,分別是a.c,main.c,按照我所說(shuō)的,在編譯階段只是生成各自的.o文件.這個(gè)階段不和其它的文件發(fā)生任何的關(guān)系.
而include這個(gè)預處理指令發(fā)生在預處理階段(早先編譯階段,只是編譯器的一個(gè)前驅處理程序).


.h .c不見(jiàn)得是浮云,脫離了編譯器談這些沒(méi)有任何的意義,拋開(kāi)更深層次的這些,比如說(shuō),OS如何啟動(dòng)這個(gè)文件,PE結構(linux下為elf)等等
編譯器首先要識別這個(gè)文件才可能去編譯它,這是前提.如果你改了它的擴展名那么你的編譯器還能認識它嗎~上升到一個(gè)更高的層次上看待這個(gè)問(wèn)題,XX兄說(shuō)的也不錯~我想XX兄說(shuō)的意思就是兩者不可因為名字相同就認為兩者有什么關(guān)系,名字是可以隨便的~
兩者之間的聯(lián)系,我在前面說(shuō)過(guò)了,是由于歷史的原因造成的,再加上人的習慣,我想誰(shuí)也不想多去記那么多文件名吧.(拿我舉個(gè)例子,一個(gè)數
據表如果多于30個(gè)字段,我就覺(jué)得頭大了,現在弄的表有的多達上百個(gè)字段,真希望那位高人研究出什么好的方法來(lái)~,也讓我們的世界美好一些~)

乾坤一笑的第三個(gè)問(wèn)題很有代表性,多次在網(wǎng)上看到,現在的編譯器絕對沒(méi)有那么智能,而且也沒(méi)有必須那么做.下面我們主要聊聊編譯器的處理過(guò)程.(我想初學(xué)者有疑問(wèn)的正在于此,即是對于編譯過(guò)程.h .c(.cpp)的變化不太了解,)

下面我說(shuō)舉個(gè)簡(jiǎn)單的例子來(lái)聊聊~
例子如下:
//a.h
classA
{
pubic:
intf(intt);
};

//a.cpp
#include"a.h"
intA::f(intt)
{
returnt;
}

//main.cpp
#include"a.h"
voidmain()
{
Aa;
a.f(3);
}
在預處理階段,預處理器看到#include "文件名"就把這個(gè)文件讀進(jìn)來(lái),比如它編譯main.cpp,看到#include"a.h",它就把a.h的內容讀進(jìn)來(lái),它知道了,有一類(lèi)A,包含一個(gè)成員函數f,這個(gè)函數接受一個(gè)int型的參數,返回一個(gè)int型的值。再往下編譯很容易就把Aa這行讀懂了,它知道是要拿A這個(gè)類(lèi)在棧上生成一個(gè)對象。再往下,它知道了下面要調用A的成員函數f了,參數是3,由于它知道這個(gè)函數要一個(gè)整形數用參數,這個(gè)3正好匹配,那就正好把它放到棧上,生成一條調用f(int)函數的指令(一般可能是一句call),至于這個(gè)f(int)函數到底在哪里,它不知道,它留著(zhù)空,鏈接時(shí)再解決。它還知道f(int)函數要返回一個(gè)int,所以也許它也為這一點(diǎn)做好了準備(在例子中,我們沒(méi)用這個(gè)返回值,也許它就不處理)。再往下到文件末尾了main.cpp編譯好了,生成了main.obj。整個(gè)編譯過(guò)程中根本就不需要知道a.cpp的內容。
同理,編譯器再編譯a.cpp,把f()函數編譯好,編譯a.cpp時(shí),它也不用管別的,把f()編譯好就行了。生成了a.obj。
最后一步就是鏈接的階段了,鏈接器把項目中所有.cpp生成的所有.obj鏈接起來(lái),
在這一步中,它就明確了f(int)函數的實(shí)現所在的地址,把main.obj中空著(zhù)的這個(gè)地址位置填上正確的地址。最終生成了可執行文件main.exe。

明白了嗎?不明白那就多說(shuō)幾句了,我們在學(xué)編譯原理的時(shí)候都知道,編譯器是分階段進(jìn)行的,每一個(gè)階段將源程序從一種表示轉換成另一種表示,一般情況下都進(jìn)行如下順序:源程序->詞法分器->語(yǔ)法分析器->語(yǔ)義分析器->中間代碼生成器->代碼優(yōu)化器->代碼生成器->目標程序.
其中這中間6項活動(dòng)都要涉及的兩項主要活動(dòng)是:符號管理器與錯誤處理器.
歸根原因,這里有一個(gè)叫做符號表的東東在里面讓你著(zhù)魔一樣不明白,其實(shí)符號表是一個(gè)數據結構.編譯器的基本一項功能就是要記錄源程序中使用的標識符并收集與每個(gè)標識符相關(guān)的各種屬性信息.屬性信息表明了該標識符的存儲位置/類(lèi)型/作用域(在那個(gè)階段有效)等信息,通俗的說(shuō)一下就是,當編譯器看到一個(gè)符號聲明時(shí),例如你的函數名它就會(huì )把它放到這個(gè)符號表中去登記一下~符號表里存放著(zhù)你的函數的入口地址,參數個(gè)數,返回信息等等一堆東西~而在聯(lián)接階段主要是處理工程中的符號表與調用對應處理關(guān)系,即我們通常所說(shuō)的解引用.
經(jīng)過(guò)前面的,不知明白與否?

最后引用一下XXX兄的結尾三點(diǎn):
搞清楚語(yǔ)法和概念說(shuō)易也易,說(shuō)難也難。竅門(mén)有三點(diǎn):
1.不要暈著(zhù)頭工作,要抽空多思考思考,多看看書(shū);
2.看書(shū)要看好書(shū),問(wèn)人要問(wèn)強人。爛書(shū)和爛人都會(huì )給你一個(gè)錯誤的概念,誤導你;
3.勤能補拙是良訓,一分辛苦一分才;


關(guān)鍵詞: 頭文件實(shí)現文

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>