C/C++ 编译过程概述
C/C++的编译过程可以分为四个主要阶段:预处理、编译、汇编和链接
1. 预处理(Preprocessing)
预处理阶段由预处理器完成,主要是对源代码文件进行一些替换操作,常见的预处理任务包括:
- 宏替换:展开源代码中的宏定义,如
#define PI 3.1415926
。 - 头文件包含:用实际的头文件内容替换
#include
指令,例如#include <stdio.h>
或#include "myheader.h"
。 - 条件编译:根据预处理指令(如
#ifdef
,#ifndef
,#if
,#endif
)选择性地编译代码。 - 移除注释:删除源代码中的注释。
预处理完成后,生成的文件通常被称为.i文件(对于C语言)或者.cpp.i文件(对于C++),这取决于源代码的原始扩展名。
2. 编译(Compilation)
编译阶段将预处理后的源代码转换成汇编代码或直接转换成目标代码(.o 文件)。在这个过程中,编译器会做以下工作:
- 词法分析:将源代码分解成一个个有意义的符号(称为“标记”或“tokens”)。
- 语法分析:检查这些标记是否符合该编程语言的语法规则。
- 语义分析:检查代码是否有意义,比如类型检查、引用未定义的变量等。
- 代码优化:在某些情况下,编译器会对代码进行优化,提高效率。
- 生成中间代码或目标代码:最终生成汇编代码或目标代码文件。
3. 汇编(Assembling)
汇编阶段将编译器产生的汇编代码转换为目标代码(.o 文件),即机器语言格式的文件。这个过程比较简单,通常不需要特别复杂的处理。
4. 链接(Linking)
链接阶段负责将多个目标文件和库文件合并成一个可执行程序或动态链接库。链接器的主要任务包括:
- 符号解析:解决不同文件中函数和全局变量的引用。
- 重定位:调整内存地址,使得程序在内存中的位置可以正确执行。
- 合并段:合并各个模块中的相同类型的段(如.text段和.data段)。
链接完成后,就生成了一个可执行文件或共享库。
示例流程
假设我们要编译一个简单的C++程序,其主要文件为 main.cpp
,并且还有另一个头文件 utils.h
和源文件 utils.cpp
。以下是整个编译过程的示例:
-
预处理:
$ g++ -E main.cpp -o main.i
-
编译:
$ g++ -c main.i -o main.o $ g++ -c utils.cpp -o utils.o
-
汇编:
如果生成的是汇编代码,则需要使用汇编器将其转换为目标文件。但通常情况下,编译器会直接输出目标文件,所以这一步可以省略。 -
链接:
$ g++ main.o utils.o -o main
上述命令使用 g++
编译器,假设没有使用任何外部库,最终会生成名为 main
的可执行文件。