一、对程序的认知 && 初识gcc
1、程序是如何诞生的?
对于一个程序来说,从【编辑】——>【编译】——>【链接】——>【运行】总共要经过这些步骤算是一个程序完成的诞生过程。如果我们要去细谈其中的过程,又可以像下面这样分化:point_down:
- 我们需要将这个【源程序】首先转变为【可执行程序】,中间的这一流程就被成为==翻译环境==,这一环境也就是我们本文所学习的
gcc
所要做的事情。在下一模块我就会对预编译
、编译
、汇编
、链接
这四个小模块展开做讲解:book:
2、gcc的初步认识
:dart: gcc英文全名为
GNU Compiler Collection
,早期的gcc编译器主要用于C语言编译,但是经过几十年的发展,gcc编译器可以用于多种语言的编译,例如C++、Go等目前较为主流的语言。 :dart: 熟悉gcc编译器是对于我们开发C/C++程序的底层基本功,虽然目前各厂商的开发IDE已经非常智能,从某种程度上已经把程序员从底层代码的编译、部署等工作解放出来,但是如果需要开发大型C++项目或者对于编译过程进行优化,那么gcc编译器是需要进行了解和深入的:mag:
- 对于gcc而言,它是一款Linux中的编译器,通常和我们之前所讲的Linux 编辑器vim配合使用。
- 我们对【编辑】【编译】应该要有一个很清晰的认识,我们平时在写C/C++代码的时候,大多都是在一些集成开发环境上(IDE)进行,就像VS、VC。这些软件屏蔽了底层的细节,使用户可以不需要关注这些繁琐复杂的东西,只需要关注程序本身即可,但是却==淡化了我们对程序的演变和形成==
- 这些软件通常是包含了
编辑
、编译
、链接
、调试
这些功能 - 对于
编辑
功能来说有【编辑器】 - 对于
编译
功能来说有【编译器】,VS2019为cl.exe
- 对于
链接
功能来说有【链接器】,VS2019为link.exe
- 对于
调试
功能来说有【调试器】
相信有了这些,你对程序的诞生过程一定有了一个更加深入的理解
3、如何使用gcc
讲了这么多,要如何去使用gcc呢?来看看吧👀
格式 gcc [选项] 要编译的文件 [选项] [目标文件]
具体如何使用,请看下一模块:point_down:
二、gcc逐步分析程序的翻译环境
好,接下去我们就来谈谈如何如何使用gcc来观察程序在翻译环境下的各个步骤。一下的讲解可能比较简略一些,如果想了解得更加深入,可以看看我的另一篇文章——> C生万物 | 程序的翻译环境和执行环境。很多内容也是从这篇文章中归纳出来的
- 下面是从源文件【.c】到可执行文件【.exe】的整体过程
1、预编译【进行宏替换】
在预编译阶段会执行以下四件事
- 头文件的展开 —— 编译器第一件做的事
- 宏定义的替换
- 去注释
- 条件编译的执行
gcc需要执行的指令
gcc -E file.c -o file.i
-E
表示是让 gcc 在预处理结束后停止编译过程-o
表示输出到指定文件【-o 后面紧跟的永远都是你要形成文件的名称】.c
结尾的文件表示源程序;.i
结尾的文件表示已经过预处理的C原始程序
2、编译【C语言——>汇编语言】
在编译阶段会执行以下四件事
- 语法分析
- 词法分析
- 语义分析
- 符号汇总
以上其实就是在将C语言的代码转化为汇编代码的一个流程
gcc需要执行的指令
gcc -S file.i -o file.s
-S
表示只进行编译而不进行汇编,生成汇编代码.i
结尾的文件表示源程序;.s
结尾的文件表示经过编译生成的汇编代码
3、汇编【汇编语言——>可重定位目标二进制文件】
在汇编阶段会执行以下两件事
- 将汇编指令转换为二进制指令【需要特定的文本阅读器readelf】
- 形成符号表
gcc需要执行的指令
gcc -c file.s -o file.o
-c
表示是gcc编译到目标代码.s
结尾的文件表示经过编译生成的汇编代码;.o
结尾的文件表示经过编译生成的汇编代码
- 在汇编这一块,就是将汇编语言翻译成可以重定位的二进制文件,里面的二进制代码我们人眼是看不到的,要特定的阅读器才可以看得明白
- 有几个源文件【.c】就会生成几个目标文件【.o】
此时我们可以来试试执行一下这个目标文件,不过发现没有【x】可执行的权限,于是使用chmod
做一个提权的操作。但是系统却说cannot execute binary file
,上面看到过这是一个二进制文件,对于二进制文件来说是不可以被执行的
对于上面的这三步【预编译】、【编译】、【汇编】都是对一个源文件中的代码进行操作的过程,没有引入其他人的代码。但是到了链接这一步,可能就要与别人的代码或者是库中的代码一起执行
4、链接【生成可执行文件或库文件】
在链接阶段会执行以下两件事
- 合并段表
- 符号表的合并和重定位
gcc需要执行的指令
gcc -E file.c -o file.i
-E
表示是让 gcc 在预处理结束后停止编译过程;-o
表示输出到指定文件.c
表示源程序;.i
表示已经过预处理的C原始程序
- 这里便不展示与其余文件的合并了,这里导一个我之前做过的GIF
- 可以来看看这个可执行文件
my_out
,它也是一个内容也是二进制代码,可供机器识别
- 最后的总结可以看看这张图
5、巧记gcc命令选项与生成文件后缀【⭐】
📚 命令选项:-E
-S
-c
【键盘左上角的Esc,不过中间的S要大写】 📚 文件后缀:.i
.s
.o
【.iso为镜像文件的后缀,ISO也为国际标准化组织】
三、gcc与g++的区别
看了这么久gcc,我们来看看g++,其实这两个编译器的用法是一模一样的,只是对于gcc才说是用来编译C语言代码,对于g++来说是用来编译C++的代码
- 不过这只是笼统的说法,其实对于gcc而言也是可以编译C++代码的,对于g++而言是可以编译C语言代码的
- 但是实际上在一些具体的语法规则上,C++在编译过程中的语法检查会更加严格。此外,C++语言本身在编译 过程中也会引入C++的标准库,如果使用gcc编译器直接编译C++语言会在编译过程中添加额外的参数,这样会显得编译过程较为繁琐(因为大部分情况下我们希望标准库可以直接引入,而不是再需要手动指定,否则对于初级使用者会带来额外的学习负担)。为了更方便使用编译器,我们选择g++来编译C++代码。
- 总结一下,gcc可以完成C++语言的编译,但是使用过程会较为繁琐,而g++就是简化后的编译指令