前言
c程序编译过程有的人会直接简述成四个字:编译链接
但我们知道对于任何一个c程序而言从.c/.cpp文件到最后的.exe/a.out可执行文件,期间都经历了四个步骤
从.c/.cpp到.exe/a.out的四个步骤
1.预处理
问1:预处理要处理什么内容?
答1:1.头文件,2.宏定义:#define,
1.头文件
而头文件里的存放什么呢?
有这么三样
1.函数原型声明
2.全局变量的声明
3.定义的宏和类型
4.全局常量
5.库函数
2.宏定义:#define
例1:#define PI 3.14159
就是定义了一个常量的宏PI,而在预处理过程中就是要将程序中的PI替换成3.14159。
例2:#define F(x) x*x
就是定义了一个表达式的宏F(x),而在预处理过程中就是要将程序中的F(x)替换成x*x。
2.汇编(编译器工作)
编译器将预处理后的C代码转换成汇编代码。这一步生成的代码是特定于目标机器的汇编语言,但仍然是文本形式。这种代码是低级语言,接近机器码,但仍然是人类可读的形式。
这种形式的文本可以用gcc -S filename.c生成出来,再用文本编辑器查看
3.编译(汇编器工作)
汇编器将汇编代码转换成机器代码。这个过程涉及到将汇编指令转换成二进制形式的机器指令,并将它们存储在目标代码文件中。这个过程也包括了代码优化,以提高程序的执行效率。
问2:编译阶段要处理什么呢?
答2:类型别名:typedef(type alias)
类型别名:typedef(type alias)
例如:为结构体起别名:
typedef struct {
int x;
int y;
} Point;
又有typedef long int;
这种编写方式可以在不用改变代码习惯的前提下就将32b的数据,扩充到了64b
而这种别名int在预处理就会被替换成long进行下一步的汇编
然而在Ubuntu的GNU编译器上我发现long int本身就是一个数据类型,包括long,long long int这三个数据类型所占内存都是8B
4.链接(链接程序工作)
链接在范围上可以分为外部链接和内部链接。
对于变量而言,如果不声明extern,那么变量默认就是局部的
对于函数而言,如果不声明static,那么函数的链接性就是全局的
(在很多编译器中对于一个自动生成的main函数,是不是默认就用static修饰呢?)
链接程序将一个或多个目标代码文件,以及可能需要的库文件,链接在一起生成最终的可执行文件。链接器解决代码中的外部引用,确保所有的函数和变量都能正确地被定位。
链接程序将需要确定地址的符号(包含变量和函数)记录下来。通过完整的地址就可以将所需要的符号连接在一起生成可执行文件。