1. 编译的概念
编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。
2. 编译的完整过程
C源程序 > 预编译处理(.c) > 编译、优化程序(.s、.asm) > 汇编程序(.obj、.o、.a、.ko) > 链接程序(.exe、.elf、.axf等)
2.1 编译预处理
- 读取C源程序:预处理器(cpp)首先读取C源程序,对其进行处理。
- 处理伪指令:伪指令主要包括以下四个方面:
- 宏定义指令:如
#define Name TokenString
,#undef
等。预编译会将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。 - 条件编译指令:如
#ifdef
,#ifndef
,#else
,#elif
,#endif
等。这些指令允许程序员通过定义不同的宏来决定编译程序对哪些代码进行处理。 - 头文件包含指令:如
#include "FileName"
或者#include <FileName>
等。头文件中一般用伪指令#define
定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
- 宏定义指令:如
- 预处理结果:预处理的结果是一个预处理源文件,其扩展名为
.i
。
2.2 编译
- 编译器:预处理后的源文件会被编译器(如gcc)编译成汇编代码。
- 转换过程:编译器将C语言的高级语句转换为机器可以理解的低级指令。
- 错误检查:编译器会检查语法错误和类型检查,如果发现错误,编译过程会停止,并给出错误提示。
- 输出文件:编译后的输出文件通常以
.s
或.asm
结尾。
2.3 汇编
- 汇编器:汇编器(as)将编译器产生的汇编代码转换为机器码,即二进制形式的目标文件。
- 输出文件:汇编过程中的输出文件通常以
.obj
或.o
结尾。
2.4 链接
- 链接器:链接器(ld)将所有必要的目标文件和库文件链接在一起,生成最终的可执行文件。
- 链接过程:链接器负责解决函数调用和全局变量的引用,确保程序运行时能正确找到这些元素。
- 输出文件:链接后的输出文件通常以
.exe
或.elf
结尾。
3. 运行程序
- 加载可执行文件:当用户运行程序时,操作系统会将可执行文件加载到内存中。
- 执行代码:程序在内存中运行时,会使用计算机的硬件资源,例如CPU、内存、硬盘等。
4. 总结
C语言程序的编译过程是一个复杂的过程,涉及到多个阶段和工具。通过了解这个过程,我们可以更好地理解C语言程序的工作原理,并编写更高效、稳定的程序。