要更深入了解C++, 必须要知道一个程序从开始到结束都干了些什么, 怎么干的。 所以我从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 [email protected]@[email protected]:near ; CODE XREF: _main+63p
UNDEF:00002DC8 ; int __thiscall Car::~Car(Car *__hidden this)
UNDEF:00002DC8 extrn [email protected]@[email protected]:near
UNDEF:00002DC8 ; CODE XREF: Car::`scalar deleting destructor‘(uint)+26p
UNDEF:00002DCC ; __fastcall _RTC_CheckStackVars(x, x)
UNDEF:00002DCC extrn @[email protected]: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 @[email protected]:near
UNDEF:00002DD0 ; CODE XREF: [email protected]@[email protected]@[email protected]@[email protected]+Fp
UNDEF:00002DD0 ; [email protected][email protected]@@[email protected]@[email protected][email protected]@@@[email protected]@[email protected]@[email protected]@Z+Fp ...
UNDEF:00002DD4 ; __stdcall _CxxThrowException(x, x)
编译后的文件用(用反汇编成汇编代码查看) 其中实现函数会变成一堆汇编指令。而那些引用到的在其他文件里面实现的函数将会变成一个特点的符号(如上面中的调用Car类的构造函数 extrn
[email protected]@[email protected]: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中的所有目标文件合并在一起生成可执行文件。
版权声明:本文为博主原创文章,未经博主允许不得转载。