C和C++编译和链接原理

文章目录


最近在修改产品的代码,增加一个额外的功能,每天陷入了 改代码–编译–数bug–改代码–编译 的循环。上大学时只学过一门课《C语言程序设计》,学完之后感觉自己好像懂了,看到一个程序大概知道每一部分在干嘛;但又好像啥都不懂,甚至在用IDE的时候都不是很明白compile和build的区别,现在想想,当时真是傻得可爱呀。

compile:仅编译cpp文件,生成对应的obj目标文件,但是并不对目标文件进行链接(即不会生成exe可执行文件);
build:编译并链接,最终生成可执行文件exe或者链接库文件lib或者dll.
rebuild:这个相当于clean+build,因为build命令一般仅对发生修改后的文件执行命令。有时工程出现了难以用理论解释的神奇bug时,rebuild一下可能就好了。

每天看着IDE consle里编译时输出的一行行结果,我就在想:这C++在编译的时候都干些啥啊?若干个cpp文件和hpp文件怎么就最后生成了一个exe可执行文件?花了一个小时的时间,我初步理解了C+\编译和链接的过程,其可分为 预编译–编译–链接 三个过程。

预编译

在上学时经常听老师讲#是预编译符号,后面跟的是预编译命令,如define,include,ifndef…define…endif等等。顾名思义,预编译是为编译过程做准备的,其实质是完成预编译命令的展开与替换。例如,#define PI 3.14,预编译则将文中所有的PI替换为PI,同时将符合条件的include内容展开到cpp文件中。

编译

编译过程就是对一个编译单元进行语法检查,cpp文件中的变量和函数是否已经声明(可以调用一个函数而不用定义,只需声明即可,这也就是include的作用,至于函数到底是如何实现的,需要在链接过程中寻找函数的入口地址去调用该函数),语句是否符合C++标准(C++标准一般会随着C++的发展而变化),完成检查后生成obj文件。

  • 编译单元
    每个cpp文件都是一个独立的编译单元,每个编译单元相互独立而不与其他编译单元交互,hpp文件不进行编译,一般hpp文件只存放函数和变量的声明,并没有定义。

链接

编译后生成的obj文件为二进制文件,但是并不一定可以运行,因为其不一定含有main入口函数。链接器根据每个obj文件的信息(未解决符号信息和所包含的已定义符号信息)寻找各obj文件之间的联系,保证每一个未解决的符号都在其他obj文件中找到引用,并且没有重复定义的符号。然后根据obj文件之间的联系将所有的obj单元链接为一个整体,产生最后的可执行文件exe或库文件。检查各个文件之间是否有符号重定义或者缺定义是链接的一个重要作用,这也解释了为什么我们不在头文件中定义变量或者函数,因为头文件有可能被多个cpp文件包含,每个cpp文件否会被编译生成一个obj文件,这样就会导致在链接时出现符号重定义的错误。

总结

C/C++程序从c/cpp文件到exe可执行文件或库文件过程中,包括预编译、编译、链接三个步骤,其主要功能是:

  • 预编译:完成预编译命令的内容替换工作;
  • 编译:对每个单独的cpp文件进行语法检查,检查函数变量是否提前声明、检查语句是否符合C++语法,然后为通过检查的cpp文件生成目标文件obj。
  • 链接:根据每个obj文件的信息寻找各obj文件之间的联系,并根据这种联系将所有的obj文件链接为一个整体,即最后的可执行文件exe或库文件。

祝枫
2018年9月28日于深圳

猜你喜欢

转载自blog.csdn.net/Vince_ZHU/article/details/82890547