C/C++编译器构成

C/C++编译器构成

C/C++编译器有 4 个基本组成部分:预处理器、编译器、汇编器和链接器,它们功能大致如下:

一般我们说编译器是指整套工具链,与其中一个组成部分重名。

  1. 预处理器 cpp:处理以 # 开头的行,将所需库代码插入源程序,进行宏替换等等。
  2. 编译器 ccl:进行语法检查,若语法无误则将源代码翻译成汇编代码。
  3. 汇编器 as:将汇编代码翻译成机器指令,并将这些指令打包成可重定位目标文件。
  4. 链接器 ld:将所有可重定位目标文件与必需的库一起链接生成可执行目标文件。

image-20230308102646186

在编写 C/C++ 程序时,遇到的错误按时期可以划分为 3 类:

  1. 编译期间语法错误;
  2. 链接期间找到对应的静态库或动态库;
  3. 运行时错误。

相较而言,遇到编译期间语法错误最幸运,可以根据错误信息修改程序。

链接错误比较头疼,文件编译顺序错误,或者没有指定库路径,或者路径打错了,或者路径不存在,最玄学的还是路径正确库也存在,但就是编译不过,哈哈哈。

运行时错误最头疼,因为程序遇到这个错误一般都会崩溃而停止运行。我们写个人代码程序遇到这样的错误不需要慌张,插桩法,打断点,继承了调试功能的 IDE 都能提供帮助。可是,如果大公司的服务遇到运行时错误而终止了,“我一分钟几百万上下”这话就不是吹牛的了,那将会造成极大的经济损失,同时公司名誉受损。总之关于运行时错误如何查找诱因、如何规避、如何恢复是一门学问,我知之甚少。

实践演练

通过编译一个 main.cpp 文件观察整个流程。

#include <iostream>

using namespace std;

void test() 
{
    cout << "111" << endl;
}

int main()
{
    printf("%x\n", test);
    printf("%x\n", &test);
    return 0;
}

我们不必记住每个组件的名称,只需要用 gcc(C++是g++)这个集成工具,通过指定选项就可以调用每个组件,如下表格所示。

选项 作用 例子
-E 只进行预处理,得到 .i 文件 gcc -E xxx -o yyy.i
-S 只进行预处理和编译,得到 .s 文件 gcc -S xxx -o yyy.s
-c 只进行预处理、编译和汇编,得到 .o 文件 gcc -c xxx -o yyy.o

当然如果要生成最终的可执行文件,就不带任何选项,如 gcc xxx -o run

  1. 预处理:g++ -E main.cpp -o main.i,本来 15 行的代码,得到的 .i 文件有 26010 行。

image-20230308101103713

  1. 编译:g++ -S main.i -o main.s,当然也可以直接对 .cpp 文件进行g++ -S main.cpp -o main.s,编译器自动完成了之前阶段的工作,结果是一样的,但是只有 181 行。

image-20230308101149163

  1. 汇编:g++ -c main.s -o main.o,当然也可以直接对 .cpp 文件进行g++ -c main.cpp -o main.o,编译器自动完成了之前阶段的工作,结果是一样的,此时生成的 .o 文件是二进制文件,不可以查看内容。

image-20230308101518405

  1. 链接:g++ main.o -o main,当然也可以直接对 .cpp 文件进行g++ -c main.cpp -o main.o,编译器自动完成了之前阶段的工作,结果是一样的,此时生成的文件是二进制文件,不可以查看内容,但是可以直接执行./main
posted @ 2023-03-08 16:18  zwjason  阅读(115)  评论(0编辑  收藏  举报