C++编译过程

2021年6月4日 176点热度 1人点赞 1条评论

这两天在写一个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;
}
  1. -E:预编译

gcc -E helloworld.cpp -o helloworld.i

将带#的语句重新展开到制定文件中,比如#define, #include "*.h"。不进行编译。

  1. -S:编译

gcc -S helloworld.cpp -o helloworld.s

将.o文件编译成编译成汇编文件(.s文件)

  1. 汇编

gcc -c helloworld.cpp -o helloworld.o

相比-S命令,多了assemble,生成目标文件(.o文件)

  1. 链接

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 等。

agedcat_xuanzai

这个人很懒,什么都没留下

文章评论

  • 匿名

    好东西谢谢分享

    2021年6月6日