目录
1. 简介
gcc-toc" style="margin-left:0px;">2. 安装gcc
gcc%E7%9A%84%E7%BC%96%E8%AF%91%E6%B5%81%E7%A8%8B-toc" style="margin-left:0px;">3. gcc的编译流程
3.1 预处理
3.2 编译
3.3 汇编
3.4 链接
gcc%E7%9B%B8%E5%85%B3%E5%8F%82%E6%95%B0-toc" style="margin-left:0px;">4. gcc相关参数
5. 多文件编译
gcc%E5%92%8Cg%2B%2B%E7%9A%84%E5%8C%BA%E5%88%AB-toc" style="margin-left:0px;">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 | 编译后的库文件 |
gcc">2. 安装gcc
有些纯净版的 Linux 默认没有 gcc 编译器,需要自己安装,如果你不知道自己有没有安装gcc,可以输入一下命令进行查看:
//查看 gcc 的版本
gcc -v
//或者
gcc --version//查看 g++ 的版本
g++ -v
//或者
g++ --version
使用gcc -v查看如下:
下滑可以看到gcc当前的版本:
使用gcc --version可以看到,其显示的信息是比较少的,但版本信号是一样的:
使用g++ -v查看:
同样下滑可以看到当前版本:
使用g++ --version:
若是显示不是上面的,或者找不到gcc......,安装步骤如下:
//需要管理权限
//bubutu上面安装
sudo apt update //更新本地的软件下载列别列表,得到最新的下载地址sudo apt install gcc g++ //通过下载列表提供的地址下载安装包,并安装//centos上面安装
sudo yum updatesudo yum install gcc g++
下面演示Ubuntu上下载:
gcc%E7%9A%84%E7%BC%96%E8%AF%91%E6%B5%81%E7%A8%8B">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的过程:
对于一下这条命令进行解读:
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,点击运行看看:
到这里一个程序就算运行完成,不过我们会发现,这个步骤太繁琐,实际上我们要执行程序不需要那么繁琐的步骤,上面只是为了演示其运行流程,实际我们可以直接使用:
gcc hello.c -o hello2(生成的链接名自己定义)
或者我们可以直接省略-o:
可以看到生成了一个a.out的链接,这是因为如果我们不指定生成的链接名。其会默认给出一个a.out的链接名。
gcc%E7%9B%B8%E5%85%B3%E5%8F%82%E6%95%B0">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的链接,运行可得:
gcc%E5%92%8Cg%2B%2B%E7%9A%84%E5%8C%BA%E5%88%AB">6. gcc和g++的区别
6.1 在代码编译阶段
后缀为.c 的文件:
gcc 把它当作是 C 代码来编译。
g++ 也会将 .c 文件当作 C 代码来编译,但它会默认启用 C++ 编译器的一些特性,例如自动定义 _cplusplus 宏等。因此,g++ 处理 .c 文件时实际上会使用 C++ 编译器进行编译,但不影响文件本身。
后缀为.cpp 的文件:
gcc 和 g++ 都会将 .cpp 文件当作 C++ 代码来编译。尽管 gcc 可以编译 C++ 代码,但它默认使用 C 编译器进行处理,可能需要手动指定编译器选项(如 -std=c++11)来正确处理 C++ 语法。
g++ 会使用 C++ 编译器,并且会自动处理 C++ 相关的特性,例如 C++ 的名称修饰(name mangling)、模板、异常处理等。
g++ 会自动启用 C++ 编译选项,而 gcc 默认用于 C 编译。
6.2 在链接阶段
gcc 和g++都可以自动链接到标准C库
g++ 会自动链接 C++ 标准库 (libstdc++),而 gcc 则不会自动链接 C++ 标准库。如果你使用 gcc 编译 C++ 程序,必须显式指定链接 C++ 标准库(例如:gcc -lstdc++)。
举个例子:
我们创建一个.cpp文件,内容为:
直接使用 g++ 来编译和链接这个 C++ 文件:
g++ example.cpp -o example
g++ 会自动选择 C++ 编译器,并且在链接时自动链接 C++ 标准库(libstdc++)。因此,无需手动指定链接库,g++ 会负责这些操作。最终会生成一个可执行文件 exanple,你可以直接运行它:
使用 gcc 来编译和链接同一个 C++ 程序,你需要手动告诉编译器链接 C++ 标准库,因为 gcc 默认只链接 C 标准库(libc),而不会自动链接 C++ 标准库:
进行手动连接:
gcc example.cpp -o example -lstdc++
6.3 关于_cplusplus 宏的定义
g++ 会自动定义_cplusplus 宏,这是一个用于标识 C++ 编译器的标准宏,这个不影响它去编译 C程序。
gcc 会根据源文件的类型来判断是否定义 _cplusplus 宏。如果编译的是 C 文件(.c 后缀),则不会定义 _cplusplus 宏;如果是 C++ 文件(.cpp 后缀),则会定义它。
Linux常用命令详细解析(含完整命令演示过程)_linux命令实例详解-CSDN博客
Linux命令进阶·vi\vim编辑器详细命令介绍-CSDN博客
Linux学习_时光の尘的博客-CSDN博客