c++程序编译过程及相关概念

编译

把源文件中的源代码翻译成机器语言,保存到目标文件中。如果编译通过,就会把CPP转换成OBJ文件。

编译单元:

每个cpp就是一个编译单元,每个编译单元相互之间是独立且相互不知的一个编译单元(Translation Unit)是指一个.cpp文件以及这所include的所有.h文件,.h文件里面的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件,后者拥有PE(Portable Executable,即Windows可执行文件)文件格式,并且本身包含的就是二进制代码,但是不一定能执行,因为并不能保证其中一定有main函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由链接器进行链接成为一个.exe或.dll文件

编译过程

目标文件:

编译后生成的文件,以机器码的形式包含了编译单元里所有的函数和数据、导出符号表、未解决符号表、地址重定向表
导出符号表:即该目标文件可以提供的符号及地址。
未解决符号表:即找不到地址的符号的列表,告诉链接器这些符号没找到地址。
地址重定向表:链接的时候,链接器会为目标文件的“未解决符号表”里的符号在其他目标文件中寻找地址,但是每个目标文件的地址都是从0x0000开始的,这样直接将对方文件中符号的地址拿过来用显然会是不正确的,为了区分不同的文件,链接器在链接时就会对每个目标文件的地址进行调整。这样就可以保证地址不会重复。因为被加上了起始地址,所以符号在自身文件中的实际地址就不对了,需要再用一张地址重定向表记录符号相对自身文件的地址。

目标文件的类型:

可重定位文件(.o、.obj文件):其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。每个cpp会被编译成一个.o文件
共享的目标文件(库文件):这种文件存放了适合于在两种上下文里链接的代码和数据。
第一种是链接程序(静态库)可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或 者一组相关函数的代码
第二种是动态链接程序(动态库)将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象动态链接库在程序执行时才被调用
可执行文件 一个可以被操作系统创建一个进程来执行之的文件

.o文件在编译后就能获得,但是库文件、可执行文件都需要在链接后才能获得

编译过程

1.编译预处理: 读取源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。
(1)宏定义指令,如# define Name TokenString,#undef等
(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。
(3)头文件包含指令,如#include "FileName"或者#include <FileName>等。
(4)特殊符号,预编译程序可以识别一些特殊的符号。

2.编译,优化阶段:经过预编译得到的输出文件中,将只有常量。如数字、字符串、变量的定义,以及关键字,如main,if,else,for,while,{,},+,-,*,\,等等。预编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。 优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

3.汇编过程:汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。生成目标文件(.o文件、.obj文件)。

链接过程

链接:链接程序的主要工作就是将有关的目标文件(库文件、.o文件)彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
具体工作: 当链接器进行链接的时候,首先决定各个目标文件在最终可执行文件里的位置。然后访问所有目标文件的地址重定义表,对其中记录的地址进行重定向(加上一个偏移量,即该编译单元在可执行文件上的起始地址)。然后遍历所有目标文件的未解决符号表,并且在所有的导出符号表里查找匹配的符号,并在未解决符号表中所记录的位置上填写实现地址。最后把所有的目标文件的内容写在各自的位置上,再作一些另的工作,就生成一个可执行文件。

链接方式

静态链接函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中
动态链接:函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

编译过程

C/C++中提供的一些特性

extern:这就是告诉编译器,这个变量或函数在别的编译单元里定义了,也就是要把这个符号放到未解决符号表里面去(外部链接)。
默认链接属性:对于函数和变量,默认链接是外部链接,对于const变量,默认内部链接
外部链接的利弊:外部链接的符号在整个程序范围内都是可以使用的,这就要求其他编译单元不能导出相同的符号(不然就会报 duplicated external symbols)。

头文件里一般只可以有声明不能有定义:头文件可以被多个编译单元包含,如果头文件里面有定义的话,那么每个包含这头文件的编译单元都会对同一个符号进行定义,如果该符号为外部链接,则会导致duplicated external symbols链接错误。
头文件中的inline 函数可以被多个cpp包含而不造成符号冲突:因为它会被直接嵌入到调用的地方,内部联结不形成外部符号,对外不可见。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理中的程序分割是指将一个大型的C代码文件分割成多个小的模块或文件。这样做有助于提高代码的可读性、维护性和复用性。 程序分割的主要目的是将不同功能或模块的代码分别放在不同的文件中,并通过合理的模块化设计来减少代码的耦合度。这样做可以方便团队协作开发,每个开发人员可以负责不同模块的开发,提高开发效率。 在进行程序分割时,通常可以遵循一些原则和规范。首先,可以根据功能将代码分离成不同的模块。比如,可以将与用户界面相关的代码放在一个文件中,与数据处理相关的代码放在另一个文件中。其次,可以将一些通用的函数、数据结构或宏定义放在独立的头文件中,方便代码的复用。此外,可以根据代码的层次关系将代码分割成多个层次,如应用层、业务逻辑层、数据访问层等。 对于C代码的程序分割,可以使用编译器提供的预处理指令来实现。可以使用#include指令将头文件包含到源文件中,使用#define宏定义来定义常量或宏。这样可以将不同模块的代码分散在不同文件中,通过编译器将它们合并起来。在编译过程中,编译器会在编译文件之前将它们连接到一起,并生成可执行文件或库文件。 程序分割的好处是可以提高代码的可读性和可维护性,并且在进行代码修改时只需要修改相应的模块,而无需修改整个代码文件。此外,程序分割还可以提高代码的复用性,不同模块的代码可以在其他项目中进行重用,从而减少代码的重复编写。 总之,程序分割是编译原理中的重要概念,通过将大型的C代码文件分割成多个小的模块或文件,可以提高代码的可读性、维护性和复用性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值