CLI 下 G++ 的使用方法

学习一下 CLI 下如何使用 G++/GCC 编译器。

缘由

学习如何使用编译器对了解程序的产生过程是很有帮助的(废话😁),不至于脱离了 IDE,就完全不会编程了。
实际上,通过阅读编译过程中产生的中间文件,会对编译器的工作过程有一个很清晰的认识。

这里以 G++ 编译器的使用为例,并且只作简单的一些探讨(GCC 与 G++会有不同的地方,但基本使用方法差别不大)。

编译过程

G++ 的编译过程主要分为四个阶段:预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。

预处理

在预处理的过程中,编译器会对一些宏指令(预编译指令,包括引入头文件之类)进行替换。
以下面的源代码为例:

1
2
3
4
5
6
7
#include <iostream>
#define number 10

int main() {
std::cout << number << std::endl;
return 0;
}

在终端输入:

1
$ g++ -E test.cpp -o test.i

使用-E指令,让编译器生成预处理后的.i文件。
接着用文本编辑器打开 test.i 文件(windows 下没有 tail 这样直接查看文件尾部的命令😑),直接看最下面的部分:

1
2
3
4
5
int main() {
std::cout << 10 << std::endl;

return 0;
}

原先设定的宏number已经被替换为 10 了,同时因为引入了iostream这个头文件,所以上面还多了两万多行的代码,其实就是把iostream头文件的内容直接插入到test.i这个文件内了。

编译

预处理之后,可以直接对test.i文件进行编译,生成汇编代码。
在终端输入:

1
$ g++ -S test.i -o test.s

使用-S指令,让编译器生成编译后的.s文件。
test.s打开后可以发现是一堆汇编指令,但内容相比test.i要少了很多,也就是说,编译器在这个过程中又删除掉了不需要的部分。

汇编

有了汇编代码后,就可以利用汇编器将汇编代码编译为二进制文件了,对于 G++ 而言,汇编器应该内嵌到了 G++ 之中,所以直接使用 G++ 就可以了。

1
$ g++ -c test.s -o test.o

test.o文件打开后就是十六进制数,这就是生成的二进制文件。

链接

最后,再将二进制文件与 G++ 的标准库进行链接,生成可执行程序test.exe并执行。

1
2
3
$ g++ test.o -o test.exe
$ test.exe
10

执行test.exe这个文件后,CLI 给出的结果是 10,这个结果与源码的内容是吻合的。

分离式编译

上述内容都是在一个源文件完成的,但实际过程中,肯定不可能只有一个源文件,而这些源文件可能会存在相互使用的文件,所以就需要编译器支持分离式编译(separate compilation)

简而言之,就是在编译main.cpp之前,先将要使用的其他源文件编译完成,再进行main.cpp的编译。也就是说,在编译一个源文件之前要确保它的“依赖”已经编译完成。
以下面 3 个文件为例:

func.h
1
2
#pragma once
void func();

func.cpp
1
2
3
4
#include <iostream>
void func() {
std::cout << "hello world!" << std::endl;
}
main.cpp
1
2
3
4
5
6
7
#include <iostream>
#include "func.h"

int main() {
func();
return 0;
}

CLI 编译过程依次是:

1
2
3
4
5
$ g++ -c func.cpp
$ g++ -c main.cpp
$ g++ func.o main.o -o main.exe
$ main.exe
hello world!

这样就算是编译完成了。

总结

这篇文章,只是简单记录了一下编译器的工作过程和使用方法,不至于在没有 IDE 的时候望洋兴叹。实际上,这里并没有深入探讨类、函数、容器等复杂结构的编译过程,这些问题又都包含了很多其他的问题,留作下次探讨了。


Buy me a coffee ? :)
0%