• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Impossible is nothing  
              愛過知情重醉過知酒濃   花開花謝終是空   緣份不停留像春風來又走   女人如花花似夢
            公告
            日歷
            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456
            統計
            • 隨筆 - 8
            • 文章 - 91
            • 評論 - 16
            • 引用 - 0

            導航

            常用鏈接

            留言簿(4)

            隨筆分類(4)

            隨筆檔案(8)

            文章分類(77)

            文章檔案(91)

            相冊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

             

            首先,C++標準中提到,一個編譯單元[translation unit]是指一個.cpp文件以及它所include的所有.h文件,.h文件里的代碼將會被擴展到包含它的.cpp文件里,然后編譯器編譯該.cpp文件為一個.obj文件,后者擁有PE[Portable Executable,windows可執行文件]文件格式,并且本身包含的就已經是二進制碼,但是,不一定能夠執行,因為并不保證其中一定有main函數。當編譯器將一個工程里的所有.cpp文件以分離的方式編譯完畢后,再由連接器(linker)進行連接成為一個.exe文件。

            舉個例子:

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

                void f();//這里聲明一個函數f

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

                #include”test.h”

                void f()

                {

                  …//do something

                }  //這里實現出test.h中聲明的f函數

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

                #include”test.h”

                int main()

                {

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

                }

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

                 call f [C++中這個名字當然是經過mangling[處理]過的]

            在編譯時,這個call指令顯然是錯誤的,因為main.obj中并無一行f的實現代碼。那怎么辦呢?這就是連接器的任務,連接器負責在其它的.obj[本例為test.obj]尋找f的實現代碼,找到以后將call f這個指令的調用地址換成實際的f的函數進入點地址。需要注意的是:連接器實際上將工程里的.obj“連接”成了一個.exe文件,而它最關鍵的任務就是上面說的,尋找一個外部連接符號在另一個.obj中的地址,然后替換原來的“虛假”地址。

            這個過程如果說的更深入就是:

                  call f這行指令其實并不是這樣的,它實際上是所謂的stub,也就是一個

                  jmp 0x23423[這個地址可能是任意的,然而關鍵是這個地址上有一行指令來進行真正的call f動作。也就是說,這個.obj文件里面所有對f的調用都jmp向同一個地址,在后者那兒才真正”call”f。這樣做的好處就是連接器修改地址時只要對后者的call XXX地址作改動就行了。但是,連接器是如何找到f的實際地址的呢[在本例中這處于test.obj],因為.obj.exe的格式都是一樣的,在這樣的文件中有一個符號導入表和符號導出表[import tableexport table]其中將所有符號和它們的地址關聯起來。這樣連接器只要在test.obj的符號導出表中尋找符號f[當然C++f作了mangling]的地址就行了,然后作一些偏移量處理后[因為是將兩個.obj文件合并,當然地址會有一定的偏移,這個連接器清楚]寫入main.obj中的符號導入表中f所占有的那一項。

            這就是大概的過程。其中關鍵就是:

                編譯main.cpp時,編譯器不知道f的實現,所有當碰到對它的調用時只是給出一個指示,指示連接器應該為它尋找f的實現體。這也就是說main.obj中沒有關于f的任何一行二進制代碼。

                編譯test.cpp時,編譯器找到了f的實現。于是乎f的實現[二進制代碼]出現在test.obj里。

                連接時,連接器在test.obj中找到f的實現代碼[二進制]的地址[通過符號導出表]。然后將main.obj中懸而未決的call XXX地址改成f實際的地址。

                完成。

             

             

            然而,對于模板,你知道,模板函數的代碼其實并不能直接編譯成二進制代碼,其中要有一個“具現化”的過程。舉個例子:

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

             template<class T>

             void f(T t)

             {}

             int main()

             {

               …//do something

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

               …//do other thing

              }

            也就是說,如果你在main.cpp文件中沒有調用過ff也就得不到具現,從而main.obj中也就沒有關于f的任意一行二進制代碼!!如果你這樣調用了:

              f(10); //f<int>得以具現化出來

            f(10.0); //f<double>得以具現化出來

            這樣main.obj中也就有了f<int>,f<double>兩個函數的二進制代碼段。以此類推。

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

            看下面的例子:[將模板和它的實現分離]

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

                template<class T>

                class A

                {

                 public:

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

                 };

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

              #include”test.h”

              template<class T>

              void A<T>::f()  //模板的實現,但注意:不是具現

              {

                …//do something

              }

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

               #include”test.h”

               int main()

               {

                  A<int> a;

            a.        f(); //編譯器在這里并不知道A<int>::f的定義,因為它不在test.h里面

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

               //A<int>::f的實現體,在本例中就是test.obj,然而,后者中真有A<int>::f

               //二進制代碼嗎?NO!!!因為C++標準明確表示,當一個模板不被用到的時

               //侯它就不該被具現出來,test.cpp中用到了A<int>::f了嗎?沒有!!所以實

               //際上test.cpp編譯出來的test.obj文件中關于A::f的一行二進制代碼也沒有

               //于是連接器就傻眼了,只好給出一個連接錯誤

               //但是,如果在test.cpp中寫一個函數,其中調用A<int>::f,則編譯器會將其//具現出來,因為在這個點上[test.cpp],編譯器知道模板的定義,所以能//夠具現化,于是,test.obj的符號導出表中就有了A<int>::f這個符號的地

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

                     }


                    
            關鍵是:在分離式編譯的環境下,編譯器編譯某一個.cpp文件時并不知道另一個.cpp文件的存在,也不會去查找[當遇到未決符號時它會寄希望于連接器]。這種模式在沒有模板的情況下運行良好,但遇到模板時就傻眼了,因為模板僅在需要的時候才會具現化出來,所以,當編譯器只看到模板的聲明時,它不能具現化該模板,只能創建一個具有外部連接的符號并期待連接器能夠將符號的地址決議出來。然而當實現該模板的.cpp文件中沒有用到模板的具現體時,編譯器懶得去具現,所以,整個工程的.obj中就找不到一行模板具現體的二進制代碼,于是連接器也黔驢技窮了。

            posted on 2006-02-25 18:31 笑笑生 閱讀(165) 評論(0)  編輯 收藏 引用 所屬分類: C++語言
             
            Copyright © 笑笑生 Powered by: 博客園 模板提供:滬江博客
            99久久99久久| 亚洲精品无码久久久| 99久久精品国产免看国产一区| 日产精品久久久久久久性色| 欧美亚洲另类久久综合| 久久久久亚洲精品中文字幕| 99久久精品国产一区二区 | 99久久国产热无码精品免费| 精品久久久久久无码中文字幕| 97精品依人久久久大香线蕉97| 久久成人影院精品777| 久久精品久久久久观看99水蜜桃| 久久精品国产亚洲欧美| 99久久免费国产精品特黄| 久久99精品国产一区二区三区| 久久久久亚洲AV无码专区首JN| 国产精品九九久久免费视频 | .精品久久久麻豆国产精品 | 2021久久精品国产99国产精品 | 久久99精品久久久久子伦| 久久国产亚洲精品| 久久成人国产精品一区二区| 久久亚洲AV成人无码国产| 奇米影视7777久久精品人人爽| 99久久精品国产综合一区| 成人久久精品一区二区三区| 久久伊人精品一区二区三区| 久久最新免费视频| 精品免费久久久久国产一区| 久久久精品国产sm调教网站| 99久久国产宗和精品1上映 | 国产AⅤ精品一区二区三区久久| 国内精品久久久久久99| 日本强好片久久久久久AAA| 伊人久久大香线蕉av一区| 久久久亚洲欧洲日产国码是AV| 国内精品伊人久久久影院| 久久精品国产男包| 亚洲AV无码一区东京热久久| 亚洲精品乱码久久久久66| 精品熟女少妇av免费久久|