【c++基础系列】c/c++编译流程
大家好,我是Lampard~~
欢迎来到c++基础系列的博客
前文再续,书接上一回。今天和大家讲解一下c/c++中的编译流程
编译流程:
程序编译的过程中就是将用户的文本形式的源代码(c/c++)转化成计算机可以直接执行的机器代码的过程。主要经过四个过程:预处理、编译、汇编和链接。具体示例如下。
OK,接下来我们先举个例子,然后我们把它揉碎了一步步慢慢分析
为了下面步骤讲解的方便,我们需要一个稍微复杂一点的例子:
假设我们自己定义了一个头文件mymath.h
,实现一些自己的数学函数,并把具体实现放在mymath.c
当中。然后写一个test.c
程序使用这些函数。
程序目录结构如下:
├── test.c
└── include
├── mymath.h
└── mymath.c
程序代码如下:
test.cpp
// test.c
#include <stdio.h>
#include "mymath.h"// 自定义头文件
int main(){
int a = 2;
int b = 3;
int sum = add(a, b);
printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}
mymath.h
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int add(int a, int b);
int sum(int a, int b);
#endif
mymath.c
// mymath.c
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
(1) 预处理 (Preprocessing)
预处理用于将所有的包含头文件(include),条件编译指令(#ifdef, #endIf),以及宏定义(#define)替换成其真正的内容。我们可以利用gcc -E来让文件执行到预处理阶段就停止,此时输出的文件是以.i结尾
预处理之后的程序还是文本,可以用文本编辑器打开。且预处理后的文件明显变大。
(2) 编译 (Compilation)
这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。说到汇编就回想起当初在学这门课的时候,是我们的老院长给我们上的课。在他们那一代的编程人手中,一个8位的寄存器都可拆开两部分来用,前半部分代表操作,后半部分代表数据,说实话真的很厉害。我们可以使用gcc -S让编译器的编译阶段之后停止,生成的汇编代码以.s结尾。
test.s文件:
(3) 汇编 (Assemble)
汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式,此时的文件已经不能用文本编辑器打开了。我们可以使用gcc -c指令让编译器执行到这个阶段,生成.obj二进制文件。
(4) 链接 (Linking)
链接过程将多个目标文件以及所需的库文件链接成最终的可执行文件(executable file)。其中库分为动态链接库以及静态链接库。
静态链接(lib)
在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中(进入exe文件)。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
动态链接 (dll)
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息(只记录名称地址)。在此可执行文件被执行时(执行再调用),动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
C/C++编译过程对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。