C++ 编译,运行过程 详解。
??????? 首先,初略的說一下之前C++的編譯過程,C++編譯過程包括預編譯-》匯編-》編譯-》鏈接。稱為一個可執行文件。(Windows平臺下為.exe文件)。
預編譯主要展開包含的頭文件,宏定義等操作。例如一個簡單的main程序,編譯預編譯后,的文件對比。
? 可以看到里面的宏已經被去掉了。如果定了那個宏,那么宏里面的內容也會顯示出來。頭文件也是,如果你包含了你一個.h 文件,那么整個.h文件會包含進來。
???? 匯編過程,就是把已經預編譯的文件編譯成匯編代碼的過程,整個過程會包含語法,詞法的分析,和一些優化操作。
???? 編譯過程其實是跟匯編可以合成一個階段,變成目標代碼。也就是二進制文件。
???? 鏈接過程是將單個編譯后的文件鏈接成一個可執行程序。前面的預編譯、匯編、編譯都是正對單個文件,以一個文件為一個編譯單元,而鏈接則是將所有關聯到的編譯后單元文件和應用的到庫文件,進行一次鏈接處理,之前編譯過的文件 如果有用到其他文件里面定義到的函數,全局變量,在這個過程中都會進行解析。
首先看看編譯后的文件樣子(已VS2012編譯后的OBJ文件為例子,不同編譯器 樣式可能會不同。)
編譯前的文件
#include "Car.h"
int main(int argc, char* argv[])
{
?Car* p = new Car();
?delete p;
?return 1;
}
編譯后的樣子(由于編譯后的文件 信息太多 只貼出里面未解析符號部分。)
?UNDEF:00002DC4 ; int __thiscall Car::Car(Car *__hidden this)
UNDEF:00002DC4???????????????? extrn ??0Car@@QAE@XZ:near ; CODE XREF: _main+63p
UNDEF:00002DC8 ; int __thiscall Car::~Car(Car *__hidden this)
UNDEF:00002DC8???????????????? extrn ??1Car@@QAE@XZ:near
UNDEF:00002DC8???????????????????????????????????????? ; CODE XREF: Car::`scalar deleting destructor'(uint)+26p
UNDEF:00002DCC ; __fastcall _RTC_CheckStackVars(x, x)
UNDEF:00002DCC???????????????? extrn @_RTC_CheckStackVars@8:near
UNDEF:00002DCC???????????????????????????????????????? ; CODE XREF: std::_String_alloc<0,std::_String_base_types<char,std::allocator<char>>>::_Alloc_proxy(void)+68p
UNDEF:00002DCC???????????????????????????????????????? ; $LN19+72p ...
UNDEF:00002DD0 ; __fastcall __security_check_cookie(x)
UNDEF:00002DD0???????????????? extrn @__security_check_cookie@4:near
UNDEF:00002DD0???????????????????????????????????????? ; CODE XREF: __ehhandler$??$construct@PADAAPAD@?$allocator@D@std@@QAEXPAPADAAPAD@Z+Fp
UNDEF:00002DD0???????????????????????????????????????? ; __ehhandler$??$construct@U_Container_proxy@std@@U12@@?$allocator@U_Container_proxy@std@@@std@@QAEXPAU_Container_proxy@1@$$QAU21@@Z+Fp ...
UNDEF:00002DD4 ; __stdcall _CxxThrowException(x, x)
編譯后的文件用(用反匯編成匯編代碼查看) 其中實現函數會變成一堆匯編指令。而那些引用到的在其他文件里面實現的函數將會變成一個特點的符號(如上面中的調用Car類的構造函數 extrn??0Car@@QAE@XZ:near)這些符號稱做為解析的符號,表示在鏈接的時候需要被解析。符號的生成名稱具體跟編譯器有關,但是會保證一個類的某個函數名稱在同一個編譯里面必須是唯一的,因為我們在預編譯階段已經把Car.h包含進來所以編譯器能正確生成這個函數的名字,然后在鏈接的時候 會找到改名字的函數,把此標識名字替換為函數的地址。這樣就實現的鏈接。
在符號解析(symbol resolution)階段,鏈接器按照所有目標文件和庫文件出現在命令行中的順序從左至右依次掃描它們,在此期間它要維護若干個集合:(1)集合E是將被合并到一起組成可執行文件的所有目標文件集合;(2)集合U是未解析符號(unresolved symbols,比如已經被引用但是還未被定義的符號)的集合;(3)集合D是所有之前已被加入到E的目標文件定義的符號集合。一開始,E、U、D都是空的。
(1): 對命令行中的每一個輸入文件f,鏈接器確定它是目標文件還是庫文件,如果它是目標文件,就把f加入到E,并把f中未解析的符號和已定義的符號分別加入到U、D集合中,然后處理下一個輸入文件。
(2): 如果f是一個庫文件,鏈接器會嘗試把U中的所有未解析符號與f中各目標模塊定義的符號進行匹配。如果某個目標模塊m定義了一個U中的未解析符號,那么就把 m加入到E中,并把m中未解析的符號和已定義的符號分別加入到U、D集合中。不斷地對f中的所有目標模塊重復這個過程直至到達一個不動點(fixed point),此時U和D不再變化。而那些未加入到E中的f里的目標模塊就被簡單地丟棄,鏈接器繼續處理下一輸入文件。
(3): 如果處理過程中往D加入一個已存在的符號,或者當掃描完所有輸入文件時U非空,鏈接器報錯并停止動作。否則,它把E中的所有目標文件合并在一起生成可執行文件。
總結
以上是生活随笔為你收集整理的C++ 编译,运行过程 详解。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++程序从编译到最终生成可执行文件
- 下一篇: 基于C++有限状态机的实现技术