这两天在写一个C++的小项目,得自己手写编译过程。正好总结一下网上的资料,留作以后查用。
编译流程
总体来说,编译的过程比较复杂,一般可以分为以下几个方面:
预处理(Preprocessing)
在实际编译工作开始之前,预处理器指令指示编译器对源码进行临时扩充,以为之后的步骤做好准备。
在 C++ 中,预处理器指令以 #
号开头,比如 #include
、#define
和 #if
等。在这一阶段,编译器逐个处理 C++ 源码文件。对于 #define
指令,编译器将源码中的宏替换成宏定义中的内容;对于 #if
、#ifdef
和 #ifndef
指令,编译器将有选择地跳过或选中部分源代码;而对于 #include
指令,编译器将把对应的库的源码插入到当前源代码中——这通常是一些通用的声明。被 #include
指令引入的头文件( .h
)往往会包含大量的代码,你引入的越多,最后生成的预编译文件就越大。总的来说,预编译过的文件会比原来的 C++ 源码更大一些。
通过上面这些替换和插入操作,预处理器产生的是被合为一体的输出文件。预处理器还会在代码中插入记号,使编译器能分辨出每一行来自哪个文件,以便在调试过程中能生成对应的错误信息。在开发调试你的 C++ 程序时,这些错误信息能给你很多帮助。
编译和汇编(Compilation & assembly)
在这一阶段,编译器通过两个连续的步骤,将预处理器产生的代码编译成目标文件(object file)。
首先,编译器将去除了预编处理器指令的纯 C++ 代码编译成底层汇编代码。在这一步中,编译器会对代码进行检查优化,指出语法错误、重载决议错误及其他各种编译错误。在 C++ 中,如果一个对象只声明,不进行定义,编译器仍然可以从源代码产生目标文件——因为这个对象也可以指向某些当前代码中还未定义的标识符。
其次,汇编器将上一步生成的汇编代码逐行转换成字节码(也就是机器码)。实际上,如果你希望把代码的不同部分分开编译的话,编译过程在这一步之后就可以停止了。这一步生成的目标文件可以被放在被称为静态库的包中,以备后续使用——也就是说,如果你只修改了一个文件,你并不需要重新编译整个项目的源代码。
链接(Linking)
链接器利用编译器产生的目标文件,生成最终结果。
在这一阶段,编译器将把上一阶段中编译器产生的各种目标文件链接起来,将未定义标识符的引用全部替换成它们对应的正确地址。没有把目标文件链接起来,就无法生成能够正常工作的程序——就像一页没有页码的目录一样,没什么用处。完成链接工作之后,链接器根据编译目的不同,把链接的结果生成为一个动态链接库,或是一个可执行文件。
链接的过程也会抛出各种异常,通常是重复定义或者缺失定义等错误。不只是没进行定义的情况,如果你忘记将对某个库或是目标文件的引用导入进来,让链接器能找到定义的话,也会发生这类错误。重复定义则刚好相反,当有两个库或目标文件中含有对同一个标识符的定义时,就可能出现重复定义错误。
一个简单的例子
典型的helloworld
代码,保存在helloworld.cpp
中:
#include<iostream>
int main(void)
{
std::cout<<"hello world!"<<std::endl;
return 0;
}
- -E:预编译
gcc -E helloworld.cpp -o helloworld.i
将带#
的语句重新展开到制定文件中,比如#define, #include "*.h"。不进行编译。
- -S:编译
gcc -S helloworld.cpp -o helloworld.s
将.o文件编译成编译成汇编文件(.s文件)
- 汇编
gcc -c helloworld.cpp -o helloworld.o
相比-S命令,多了assemble,生成目标文件(.o文件)
- 链接
gcc helloworld.cpp -o helloworld
这条指令最为常用,相比-c指令,多了”链接“,生成了可执行文件。
gcc/g++指令汇总
gcc/g++指令选项 | 功 能 |
---|---|
-E(大写) | 预处理指定的源文件,不进行编译。 |
-S(大写) | 编译指定的源文件,但是不进行汇编。 |
-c | 编译、汇编指定的源文件,但是不进行链接。 |
-o | 指定生成文件的文件名。 |
-llibrary(-I library) | 其中 library 表示要搜索的库文件的名称。该选项用于手动指定链接环节中程序可以调用的库文件。建议 -l 和库文件名之间不使用空格,比如 -lstdc++。 |
-ansi | 对于 C 语言程序来说,其等价于 -std=c90;对于 C++ 程序来说,其等价于 -std=c++98。 |
-std= | 手动指令编程语言所遵循的标准,例如 c89、c90、c++98、c++11 等。 |
文章评论
好东西谢谢分享