【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(三)

简介: 【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解

三、GCC编译参数

1. 常用编译选项参数汇总

选项参数 作用解析
-E 预处理生成 .i 文件
-S 编译生成 .s 汇编文件
-c 汇编生成 .o 目标文件
-o 指定目标文件
-O 优化选项,有1-3级
-I (大写i) 指定包含头文件的路径(绝对、相对路径都可)
-l (小写L) 指定库名,libxxx.a或libxxx.so
-L 包含的库路径
-g 生成调试信息,用于gdb调试,如果不加这个选项无法进行gdb调试
-Wall 显示更多警告信息
-D 指定宏
-lstdc++ 编译C++源代码

-E/-S/-c 在上面已经介绍完毕,下面介绍剩下的选项参数。

2. 使用方法介绍

准本工作,首先准备一个hello.c文件,这是一个单独的文件

然后准备一个main.c和一个test.h一个test.c文件,main.c文件和test.c文件放在main目录下,test.h放在header目录下,目录结构如下

文件内容如下

这三个文件的关系是,main.c调用了test.h中的函数,test.c实现了test.h中的函数。下面将使用这三个文件进行演示。

(1)-o 指定目标文件

如果使用GCC编译且不加任何选项的时候,默认会生成一个 a.out 的可执行文件

如果加上 -o 选项就可以自己指定可执行文件名甚至是后缀

这些绿色的文件都是可执行的,前面已经多次强调过,可执行文件和后缀没有关系,不过我们一般会指定为和源文件同名,无后缀的这种格式,即这里的 hello 文件。

(2)-O 优化选项

优化选项,不写就是默认不优化,1-3优化等级越来越高,但实际上并非优化等级越高就越好。

  • O0:关闭所有优化选项,这是编译器默认的编译选项。
  • O1:基本优化,编译器会尝试减小生成代码的尺寸,以及缩短执行时间,但并不执行需要占用大量编译时间的优化。
  • O2:包含-O1的优化并增加了不需要在目标文件大小和执行速度上进行折衷的优化。编译器不执行循环展开以及函数内联。会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。 大多数情况下,推荐使用 O2 这一级优化选项就足够了。
  • O3:最高的优化级别,它会使用更多的编译时间,并且会增大二进制文件的体积并让他们更消耗内存。O3 级优化除了会打开所有 -O2 的优化选项外增加 -finline-functions-funswitch-loops-frename-registers-fweb 等优化选项(这些优化选项随着优化级别的增加会添加更多优化选项,每个选项都会完成一定的优化内容,这里就不深入探讨了,只需要了解 O1-O3 的使用即可)。这一级优化编译时间最长,生成的目标文件也更大,有时性能不增反而降低,甚至产生不可预知的问题或错误,所以大多数情况下不推荐使用。
  • Os:其实还有一个 Os ,它使用了所有 -O2 的优化选项,但又不会缩减代码的尺寸大小,姑且把它算在第二三级之间吧。

(3)-I 指定包含头文件路径及头文件引入的两种方法

我们编译一下前面准备好的main.c和test.c

可以看到,编译错误,找不到头文件test.h,这时有人可能会很奇怪,在main.c中和test.c中已经包含头文件了呀,为啥会找不到呢?其实,这是因为这个头文件和main.c、test.c不在同一个目录下。我们应该知道,在包含头文件的时候,标准库文件一般用尖括号 <> ,编译器回到默认的目录下寻找这些.h文件,如果是自己写的头文件,要用双引号 “” 去包含,编译器会在当前目录(源文件所在目录)进行查找。也就是说,出现这个错误的原因是,gcc编译器找不到test.h这个文件,它不知道这个文件在哪个目录下。

  • #include <>:将指定文件引入到当前文件,搜索策略为,直接在编译器指定的路径处开始搜索,如果找不到被引入文件,则程序报错。因此系统提供的头文件推荐使用这种方式引入。如果是集成开发环境,比如VS,这个默认路径一般在VS安装目录下的一个名为 include 的路径下。在Linux中,一般默认路径是 /usr/include/usr/lib 下的目录。
  • #include “”:将指定文件引入到当前文件,搜索策略为,首先在运行程序所在的目录处进行搜索,搜索失败后再到编译器指定的路径处搜索,如果仍然搜索失败,则直接报错。因此,用户自定义头文件必须用这种方式引入,系统提供的头文件也可以使用这种方式,但是会增加没必要的搜索,所以不推荐。

那么上面问题的解决方法就是加 -I 选项,可以使用相对路径或决定路径:

相对路径

gcc main.c test.c -o main -I ../header/

绝对路径

gcc main.c test.c -o main -I /home/qq/dm/dm_gcc/header/

(4)-l (小写L) 指定库名

通常动态库静态库名字的格式都是 libxxx.solibxxx.a ,所以这个参数的使用方法是直接加库名 -lxxx ,具体使用方法将在我Linux专栏的另一篇文章《自己动手做动态库与静态库》中详细介绍。

(5)-L 包含的库路径

指定动态库和静态库的路径,后面直接加路径即可。具体使用方法将在我Linux专栏的另一篇文章《自己动手做动态库与静态库》中详细介绍。

(6)-g 生成调试信息

这个选项用于gdb调试的时候,只有在编译的时候加 -g 选项,才能进行gdb调试。

可以看到,加了 -g 选项后,文件变大了,这是因为里面包含了调试所用的信息,关于 -g 选项的更多知识和 gdb 调试相关讲解,将在Linux专栏的另一篇文章《GDB调试器》中详细介绍。

(7)-Wall 显示更多警告信息

当GCC在编译过程中检查出错误的话,它就会中止编译,并报错。但是当检测到警告时却能继续编译并生成可执行文件,这时因为警告只是针对程序结构的诊断信息,它不能说明程序一定有错误,而是说明程序存在风险,或者可能存在错误。GCC提供了非常丰富的警告,但是如果你不启用这些警告的话,GCC编译器是不会报告检测到的警告信息的。

举个例子,我们写一个int类型的main函数,并且不加return语句

我们直接编译程序,可以看到,编译通过,没人任何报错也没有任何警告,并且程序可以运行并打印出值。实际上,main函数没有return语句至少应该提示警告信息的,甚至在VS中,这个文件直接就无法通过编译且直接报错的。

我们再加上 -Wall 选项,可以看到,虽然生成了可执行文件,但是有警告信息提示。

实际上,即使加了 -Wall 选项,也并非所以警告都会提示,有一些警告是不会提示的,比如隐式类型转换等。我们对下面程序编译,程序中有一个int到char的隐式类型转换

编译一下,虽然没有警告,但是程序没有打印任何东西。

(8)-D 指定宏

我们将前面准备好的hello.c进行一点修改,把宏定义删除

编译运行

因为没有宏定义 PRINT ,所以 printf 语句不会执行,也就不会打印任何东西。我们可以通过 -D 来指导一个宏。

gcc hello.c -D PRINT

可以看到, printf 函数执行了。

(9)-lstdc++ 编译C++源文件

直接用gcc编译C++源文件,是无法编译的

编译C++源文件有两种方法,一种是使用 -lstdc++ 选项,另一种是使用 g++ 编译。

总结

通过这篇文章你是不是对程序的编译过程和GCC编译工具链有了更加深刻的认识呢,其实GCC也没什么神秘的吧,哈哈哈哈。有句诗我非常喜欢“纸上得来终觉浅,觉知此事要躬行”,对于GCC的学习绝不能止步于这篇文章,一定要打开自己的虚拟机或者双系统进入你的Linux,一个命令一个命令的敲,一个文件一个文件的看,动手实践才能把知识变成自己的。当然,对Linux的学习更不能止步于此,这里分享一个学习Linux的小妙招,重点来了哦,那就是一定要关注我的Linux专栏,把里面的每一篇文章都看透,嘻嘻嘻~

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
25天前
|
存储 编译器 C语言
深入理解GCC 和 G++ 编译器
GCC 和 G++ 是 GNU 工具链中的核心编译器,支持 C 和 C++ 程序开发。本文详细介绍其编译流程、常用选项及动态链接与静态链接的区别。编译过程分为预处理、编译、汇编和链接四个阶段,每个阶段有特定任务和命令选项。常用选项如 `-E`、`-S`、`-c` 和 `-o` 分别用于预处理、生成汇编代码、生成目标文件和指定输出文件。动态链接节省空间且易于更新,但依赖运行时库;静态链接独立高效,但文件较大且更新困难。合理选择优化选项(如 `-O0` 至 `-O3`)可提升程序性能。掌握这些知识有助于开发者更高效地编写、调试和优化代码。
56 23
深入理解GCC 和 G++ 编译器
|
6月前
|
前端开发 C语言
gcc动态库升级
gcc动态库升级
|
4月前
|
编译器 Linux C语言
gcc的编译过程
GCC(GNU Compiler Collection)的编译过程主要包括四个阶段:预处理、编译、汇编和链接。预处理展开宏定义,编译将代码转换为汇编语言,汇编生成目标文件,链接将目标文件与库文件合并成可执行文件。
132 11
|
4月前
|
Unix 编译器 Linux
1.8.3 GNU编译器集合和LLVM项目
GNU项目自1987年起致力于开发自由UNIX软件,其成果之一GNU编译器集合(GCC)在指导委员会领导下持续演进,紧跟C语言标准。GCC适用于多种平台如UNIX、Linux和Windows,并常通过gcc或cc命令调用。另一方面,起源于2000年伊利诺伊大学研究项目的LLVM,提供了另一种开源编译器集合,其Clang编译器处理C代码,自2012年起成为FreeBSD默认C编译器,并良好支持最新C标准。
53 1
|
9月前
|
编译器 Linux 开发工具
|
9月前
|
NoSQL 编译器 开发工具
006.gcc编译器
gcc是什么?
99 0
006.gcc编译器
|
9月前
|
存储 NoSQL 算法
从一个crash问题展开,探索gcc编译优化细节
问题分析的过程也正是技术成长之路,本文以一个gcc编译优化引发的crash为切入点,逐步展开对编译器优化细节的探索之路,在分析过程中打开了新世界的大门……
|
6月前
|
编译器 开发工具 C语言
Gcc 链接文件
Gcc 链接文件
53 4
|
6月前
|
编译器 C语言 C++
MinGW安装gcc
MinGW安装gcc
128 0
|
8月前
|
自然语言处理 编译器 Go
GCC:GNU编译器
GCC:GNU编译器
134 0