目录
[1. 简介](#1. 简介)
[2. 安装gcc](#2. 安装gcc)
[3. gcc的编译流程](#3. gcc的编译流程)
[3.1 预处理](#3.1 预处理)
[3.2 编译](#3.2 编译)
[3.3 汇编](#3.3 汇编)
[3.4 链接](#3.4 链接)
[4. gcc相关参数](#4. gcc相关参数)
[5. 多文件编译](#5. 多文件编译)
[6. gcc和g++的区别](#6. gcc和g++的区别)
1. 简介
gcc是Linux下的编译工具集,是GNU Compiler Collection的缩写,包含gcc, g++等编译器。这个工具集不仅包含编译器,还包含其他工具集,例如ar,nm等。
gcc是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Objective-C等语言编写的程序。gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合嵌入式领域的开发编译。
对于gcc支持的编译源文件的后缀名及其解释如下:
|-------------|----------------|
| 后缀名 | 所对应的语言 |
| .c | C源程序 |
| .C/.cc/.cxx | C++源程序 |
| .m | Objective-C源程序 |
| .i | 经过预处理的C源程序 |
| .ii | 经过预处理的C++源程序 |
| .s/.S | 汇编语言源程序 |
| .h | 预处理文件(头文件) |
| .o | 目标文件 |
| .a/.so | 编译后的库文件 |
2. 安装gcc
有些纯净版的 Linux 默认没有 gcc 编译器,需要自己安装,如果你不知道自己有没有安装gcc,可以输入一下命令进行查看:
cpp
//查看 gcc 的版本
gcc -v
//或者
gcc --version
//查看 g++ 的版本
g++ -v
//或者
g++ --version
使用gcc -v查看如下:
下滑可以看到gcc当前的版本:
使用gcc --version可以看到,其显示的信息是比较少的,但版本信号是一样的:
使用g++ -v查看:
同样下滑可以看到当前版本:
使用g++ --version:
若是显示不是上面的,或者找不到gcc......,安装步骤如下:
cpp
//需要管理权限
//bubutu上面安装
sudo apt update //更新本地的软件下载列别列表,得到最新的下载地址
sudo apt install gcc g++ //通过下载列表提供的地址下载安装包,并安装
//centos上面安装
sudo yum update
sudo yum install gcc g++
下面演示Ubuntu上下载:
3. gcc的编译流程
gcc的编译流程可分为四个阶段:预编译(预处理)→ 编译和优化 → 汇编 → 链接。
|-----------|----------------|-----------|
| 文件名后缀 | 说明 | gcc参数 |
| .c | 源文件 | 无 |
| .i | 预处理后的C文件 | -E |
| .s | 编译后得到的汇编语言的源文件 | -S |
| .o | 汇编后得到的二进制文件 | -c |
3.1 预处理
编译器将*.c代码的头文件编译进来,例如我们头文件声明的是:include<stdio.h>那么就会将stdio.h编译进来,用户可以使用gcc的选项"-E"进行查看,该选项的作用是让gcc在预处理结束后停止编译过程。
该阶段主要进行三件事:展开头文件、宏替换、去掉注释行。
演示一下,首先我们先创建一个.c的文件:
其中.c文件内容为:
这里对于Linux的一些基础命令和vi/vim编辑器的使用不做过多的描述,想要详细了解的可以看:
Linux常用命令详细解析(含完整命令演示过程)-CSDN博客
Linux命令进阶·vi\vim编辑器详细命令介绍-CSDN博客
主要演示gcc的过程:
对于一下这条命令进行解读:
cpp
gcc -E hello.c -o hello.i
- gcc: 是 GCC 编译器的命令,通常用于编译 C 语言程序。
- -E: 这个选项告诉 GCC 只进行预处理,而不进行编译、汇编或链接。预处理包括宏替换、文件包含和条件编译等。
- hello.c: 这是你输入的源代码文件,通常是 C 语言源代码文件。
- -o hello.i: 该选项指定输出文件的名称。在这里,预处理的结果会被保存到 hello.i 文件中,.i 是预处理后的 C 代码文件扩展名。
我们可以通过cat命令查看文件内容,首先是.c文件:
对.i文件进行查看,可以看到头文件被展开了:
往下翻可以看到,对比.c文件黄色部分的注释被清除了,蓝色部分3的位置开始时NUMBER的宏定义,此时被替换成3:
我们也可以直接通过vim hello.i进行查看:
3.2 编译
在编译阶段,gcc首先要检查代码的规范性,以及收否有语法错误等,以确定代码是否能正常工作,无误后,gcc把代码翻译成汇编语言,用户可以使用"-S"选项进行查看,该选项只是进行编译而不进行汇编,最终生成一个汇编代码。
- gcc: 是 GCC 编译器的命令。
- -S: 这个选项告诉 GCC 执行到汇编阶段。它会把源代码编译成汇编语言代码,而不是生成机器码或可执行文件。
- hello.i: 这是输入文件,应该是一个已经经过预处理的 C 语言源代码文件(通常是 .i 文件)。
- -o hello.s: 这个选项指定输出文件的名称。这里的 hello.s 文件将包含生成的汇编代码。
同样的,我们可以查看此时.s文件的内容:
3.3 汇编
该阶段吧编译阶段生成的.s文件转换成目标文件,用户可以使用-c选项查看,汇编代码转换成后缀名为.o的二进制目标代码。
查看一下,可以发现都是一些二进制文件,不过这里识别不出来,因此显示的是乱码:
3.4 链接
编译成功后,就进入了链接阶段。
函数库一般分为静态库和动态库两种:静态库在编译链接时,把库文件的代码全部加入可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为".a"。动态库与之相反,在编译链接时并没有把库文件的代码加入可执行文件中,而是在程序执行时链接文件加载库,这样可以节省系统的开销,动态库的后缀名一般为".so"。 gcc在编译时默认使用动态库。
可以看到生成一个可自行程序 hello,点击运行看看:
到这里一个程序就算运行完成,不过我们会发现,这个步骤太繁琐,实际上我们要执行程序不需要那么繁琐的步骤,上面只是为了演示其运行流程,实际我们可以直接使用:
cpp
gcc hello.c -o hello2(生成的链接名自己定义)
或者我们可以直接省略-o:
可以看到生成了一个a.out的链接,这是因为如果我们不指定生成的链接名。其会默认给出一个a.out的链接名。
4. gcc相关参数
|-----------------------------------------------|-----------------------------------------------------------|
| 参数名称 | 含义 |
| -c | 只编译不链接成为可执行文件(生成后缀为 .o 的文件) |
| -S | 只编译不汇编和链接(生成后缀为 .s 的预编译文件) |
| -E | 仅执行预处理,不进行编译、汇编和链接(生成后缀为 .i 的预编译文件) |
| -o [file1] [file2]/[file2] -o [file1] | 将文件file2编程file1 |
| -I dir | (这里是大写的i)指定include包含文件的搜索目录 |
| -L dir | 在库文件的搜索路径列表中添加dir目录 |
| -l lib | (这里是小写的L)指定程序要链接的库,lib为库文件名称。 |
| -g | 生成调试信息,方便gdb调试 |
| -D define | 预定义宏 |
| -w | 不输出任何警告信息 |
| -Wall | 开启编译器的所有警告选项 |
| -static | 链接静态库生成目标文件,禁止使用动态库(在支持动态链接的系统上) |
| -share | 尽量使用动态库,但前提是系统存在动态库,生成的目标文件较小 |
| -shared | 生成共享文件,然后可以与其它文件链接生成可执行文件 |
| -fpic | 生成适用于共享库的与地址无关的代码(PIC)(如果机器支持的话) |
| -fPIC | 生成与位置无关的的代码,适用于使用动态库,与"-fpic"的区别在于去除去全局偏移表的任何限制(如果机器支持的话) |
| -fPIE | 使用与地址无关的代码生成可执行文件 |
5. 多文件编译
首先我们随便找个地方存放以下文件,这里我将test文件里面的内容删除了,重新放入的:
每个文件的内容为:
以上那么多.c文件,若是我们要一个个链接太过繁琐:
有一种简单的写法,可以使用通配符:
可以看到生成了一个a.out的链接,运行可得:
6. gcc和g++的区别
(1)在代码编译阶段(第二个阶段)
后缀为.c 的,gcc 把它当作是 C 程序,而g++ 当作是 C++ 程序
后缀为.cpp 的,两者都会认为是 C++ 程序,C++的语法规则更加严谨一些
g++会调用gcc,对于C++代码,两者是等价的,也就是说gcc和g++都可以编译C/C++代码
(2)在链接阶段(最后一个阶段)
gcc 和g++都可以自动链接到标准C库
g++ 可以自动链接到标准 C++ 库,gcc 如果要链接到标准 C++ 库需要加参数 -1stdc++
(3)关于_cplusplus 宏的定义
g++ 会自动定义_cplusplus 宏,但是这个不影响它去编译 C程序
gcc 需要根据文件后缀判断是否需要定义_cplusplus宏(规则参考第一条)
Linux常用命令详细解析(含完整命令演示过程)_linux命令实例详解-CSDN博客
Linux命令进阶·vi\vim编辑器详细命令介绍-CSDN博客
Linux学习_时光の尘的博客-CSDN博客