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

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

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

    轉(zhuǎn)貼自:http://blog.csdn.net/pongba/archive/2003/10/24/19130.aspx,作者:劉未鵬

    ?

    首先,C++標(biāo)準(zhǔn)中提到,一個(gè)編譯單元[translation unit]是指一個(gè).cpp文件以及它所include的所有.h文件,.h文件里的代碼將會(huì)被擴(kuò)展到包含它的.cpp文件里,然后編譯器編譯該.cpp文件為一個(gè).obj文件,后者擁有PE[Portable Executable,windows可執(zhí)行文件]文件格式,并且本身包含的就已經(jīng)是二進(jìn)制碼,但是,不一定能夠執(zhí)行,因?yàn)椴⒉槐WC其中一定有main函數(shù)。當(dāng)編譯器將一個(gè)工程里的所有.cpp文件以分離的方式編譯完畢后,再由連接器(linker)進(jìn)行連接成為一個(gè).exe文件。

    舉個(gè)例子:

    //---------------test.h-------------------//

    ??? void f();//這里聲明一個(gè)函數(shù)f

    //---------------test.cpp--------------//

    ??? #include”test.h”

    ??? void f()

    ??? {

    ??? ??…//do something

    ??? }? //這里實(shí)現(xiàn)出test.h中聲明的f函數(shù)

    //---------------main.cpp--------------//

    ??? #include”test.h”

    ??? int main()

    ??? {

    ?????? f(); //調(diào)用ff具有外部連接類型

    ??? }

    在這個(gè)例子中,test. cppmain.cpp各被編譯成為不同的.obj文件[姑且命名為test.objmain.obj],在main.cpp中,調(diào)用了f函數(shù),然而當(dāng)編譯器編譯main.cpp時(shí),它所僅僅知道的只是main.cpp中所包含的test.h文件中的一個(gè)關(guān)于void f();的聲明,所以,編譯器將這里的f看作外部連接類型,即認(rèn)為它的函數(shù)實(shí)現(xiàn)代碼在另一個(gè).obj文件中,本例也就是test.obj,也就是說(shuō),main.obj中實(shí)際沒(méi)有關(guān)于f函數(shù)的哪怕一行二進(jìn)制代碼,而這些代碼實(shí)際存在于test.cpp所編譯成的test.obj中。在main.obj中對(duì)f的調(diào)用只會(huì)生成一行call指令,像這樣:

    ???? call f [C++中這個(gè)名字當(dāng)然是經(jīng)過(guò)mangling[處理]過(guò)的]

    在編譯時(shí),這個(gè)call指令顯然是錯(cuò)誤的,因?yàn)?/span>main.obj中并無(wú)一行f的實(shí)現(xiàn)代碼。那怎么辦呢?這就是連接器的任務(wù),連接器負(fù)責(zé)在其它的.obj[本例為test.obj]尋找f的實(shí)現(xiàn)代碼,找到以后將call f這個(gè)指令的調(diào)用地址換成實(shí)際的f的函數(shù)進(jìn)入點(diǎn)地址。需要注意的是:連接器實(shí)際上將工程里的.obj“連接”成了一個(gè).exe文件,而它最關(guān)鍵的任務(wù)就是上面說(shuō)的,尋找一個(gè)外部連接符號(hào)在另一個(gè).obj中的地址,然后替換原來(lái)的“虛假”地址。

    這個(gè)過(guò)程如果說(shuō)的更深入就是:

    ????? call f這行指令其實(shí)并不是這樣的,它實(shí)際上是所謂的stub,也就是一個(gè)

    ????? jmp 0x23423[這個(gè)地址可能是任意的,然而關(guān)鍵是這個(gè)地址上有一行指令來(lái)進(jìn)行真正的call f動(dòng)作。也就是說(shuō),這個(gè).obj文件里面所有對(duì)f的調(diào)用都jmp向同一個(gè)地址,在后者那兒才真正”call”f。這樣做的好處就是連接器修改地址時(shí)只要對(duì)后者的call XXX地址作改動(dòng)就行了。但是,連接器是如何找到f的實(shí)際地址的呢[在本例中這處于test.obj],因?yàn)?/span>.obj.exe的格式都是一樣的,在這樣的文件中有一個(gè)符號(hào)導(dǎo)入表和符號(hào)導(dǎo)出表[import tableexport table]其中將所有符號(hào)和它們的地址關(guān)聯(lián)起來(lái)。這樣連接器只要在test.obj的符號(hào)導(dǎo)出表中尋找符號(hào)f[當(dāng)然C++對(duì)f作了mangling]的地址就行了,然后作一些偏移量處理后[因?yàn)槭菍蓚€(gè).obj文件合并,當(dāng)然地址會(huì)有一定的偏移,這個(gè)連接器清楚]寫入main.obj中的符號(hào)導(dǎo)入表中f所占有的那一項(xiàng)。

    這就是大概的過(guò)程。其中關(guān)鍵就是:

    ??? 編譯main.cpp時(shí),編譯器不知道f的實(shí)現(xiàn),所有當(dāng)碰到對(duì)它的調(diào)用時(shí)只是給出一個(gè)指示,指示連接器應(yīng)該為它尋找f的實(shí)現(xiàn)體。這也就是說(shuō)main.obj中沒(méi)有關(guān)于f的任何一行二進(jìn)制代碼。

    ??? 編譯test.cpp時(shí),編譯器找到了f的實(shí)現(xiàn)。于是乎f的實(shí)現(xiàn)[二進(jìn)制代碼]出現(xiàn)在test.obj里。

    ??? 連接時(shí),連接器在test.obj中找到f的實(shí)現(xiàn)代碼[二進(jìn)制]的地址[通過(guò)符號(hào)導(dǎo)出表]。然后將main.obj中懸而未決的call XXX地址改成f實(shí)際的地址。

    ??? 完成。

    ?

    ?

    然而,對(duì)于模板,你知道,模板函數(shù)的代碼其實(shí)并不能直接編譯成二進(jìn)制代碼,其中要有一個(gè)“具現(xiàn)化”的過(guò)程。舉個(gè)例子:

    //----------main.cpp------//

    ?template<class T>

    ?void f(T t)

    ?{}

    ?int main()

    ?{

    ?? …//do something

    ?? f(10); //call f<int> 編譯器在這里決定給f一個(gè)f<int>的具現(xiàn)體

    ?? …//do other thing

    ? }

    也就是說(shuō),如果你在main.cpp文件中沒(méi)有調(diào)用過(guò)ff也就得不到具現(xiàn),從而main.obj中也就沒(méi)有關(guān)于f的任意一行二進(jìn)制代碼!!如果你這樣調(diào)用了:

    ? f(10); //f<int>得以具現(xiàn)化出來(lái)

    f(10.0); //f<double>得以具現(xiàn)化出來(lái)

    這樣main.obj中也就有了f<int>,f<double>兩個(gè)函數(shù)的二進(jìn)制代碼段。以此類推。

    然而具現(xiàn)化要求編譯器知道模板的定義,不是嗎?

    看下面的例子:[將模板和它的實(shí)現(xiàn)分離]

    ?//-------------test.h----------------//

    ??? template<class T>

    ??? class A

    ? ??{

    ???? public:

    ??????? void f(); //這里只是個(gè)聲明

    ???? };

    //---------------test.cpp-------------//

    ? #include”test.h”

    ? template<class T>

    ? void A<T>::f()? //模板的實(shí)現(xiàn),但注意:不是具現(xiàn)

    ? {

    ??? …//do something

    ? }

    //---------------main.cpp---------------//

    ?? #include”test.h”

    ?? int main()

    ?? {

    ????? A<int> a;

    a.??????? f(); //編譯器在這里并不知道A<int>::f的定義,因?yàn)樗辉?/span>test.h里面

    ?? //于是編譯器只好寄希望于連接器,希望它能夠在其他.obj里面找到

    ?? //A<int>::f的實(shí)現(xiàn)體,在本例中就是test.obj,然而,后者中真有A<int>::f

    ?? //二進(jìn)制代碼嗎?NO!!!因?yàn)?/span>C++標(biāo)準(zhǔn)明確表示,當(dāng)一個(gè)模板不被用到的時(shí)

    ?? //侯它就不該被具現(xiàn)出來(lái),test.cpp中用到了A<int>::f了嗎?沒(méi)有!!所以實(shí)

    ?? //際上test.cpp編譯出來(lái)的test.obj文件中關(guān)于A::f的一行二進(jìn)制代碼也沒(méi)有

    ?? //于是連接器就傻眼了,只好給出一個(gè)連接錯(cuò)誤

    ?? //但是,如果在test.cpp中寫一個(gè)函數(shù),其中調(diào)用A<int>::f,則編譯器會(huì)將其//具現(xiàn)出來(lái),因?yàn)樵谶@個(gè)點(diǎn)上[test.cpp],編譯器知道模板的定義,所以能//夠具現(xiàn)化,于是,test.obj的符號(hào)導(dǎo)出表中就有了A<int>::f這個(gè)符號(hào)的地

    //址,于是連接器就能夠完成任務(wù)。

    ???????? }


    ????????
    關(guān)鍵是:在分離式編譯的環(huán)境下,編譯器編譯某一個(gè).cpp文件時(shí)并不知道另一個(gè).cpp文件的存在,也不會(huì)去查找[當(dāng)遇到未決符號(hào)時(shí)它會(huì)寄希望于連接器]。這種模式在沒(méi)有模板的情況下運(yùn)行良好,但遇到模板時(shí)就傻眼了,因?yàn)槟0鍍H在需要的時(shí)候才會(huì)具現(xiàn)化出來(lái),所以,當(dāng)編譯器只看到模板的聲明時(shí),它不能具現(xiàn)化該模板,只能創(chuàng)建一個(gè)具有外部連接的符號(hào)并期待連接器能夠?qū)⒎?hào)的地址決議出來(lái)。然而當(dāng)實(shí)現(xiàn)該模板的.cpp文件中沒(méi)有用到模板的具現(xiàn)體時(shí),編譯器懶得去具現(xiàn),所以,整個(gè)工程的.obj中就找不到一行模板具現(xiàn)體的二進(jìn)制代碼,于是連接器也黔驢技窮了。

    ?


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     

    posts - 10, comments - 15, trackbacks - 0, articles - 0

    Copyright © iceboundrock

    主站蜘蛛池模板: 亚洲第一成年男人的天堂| 亚洲AⅤ优女AV综合久久久| 亚洲美女中文字幕| 亚洲Av无码乱码在线播放| 久草免费福利在线| 亚洲色欲色欱wwW在线| 亚洲精品国产日韩无码AV永久免费网 | 久久精品国产99国产精品亚洲| 亚洲一区二区三区香蕉| 日本一区二区三区日本免费| 久久久久av无码免费网| 男的把j放进女人下面视频免费| 免费人成大片在线观看播放电影 | 国产亚洲国产bv网站在线| 国产亚洲精AA在线观看SEE| 亚洲国产成人久久一区久久 | 亚洲老熟女五十路老熟女bbw| 久久亚洲AV成人无码国产| 亚洲国产精品无码久久一线| 亚洲精品一级无码鲁丝片| 国产成人在线观看免费网站| 一个人免费视频在线观看www| 羞羞视频在线免费观看| 国产偷国产偷亚洲高清在线| 亚洲一区二区观看播放| 亚洲午夜在线播放| 中文字幕在线观看亚洲视频| 亚洲va精品中文字幕| 亚洲午夜精品在线| 亚洲人成综合在线播放| 亚洲午夜精品在线| 亚洲国产成人va在线观看网址| 亚洲精品不卡视频| 中文字幕亚洲一区二区三区| 亚洲国产精品一区二区第四页 | 在线播放亚洲精品| 美女视频黄频a免费| 美女18毛片免费视频| 看一级毛片免费观看视频| 国产成人亚洲精品蜜芽影院| 羞羞漫画小舞被黄漫免费|