对于一个程序来说,从【编辑】——>【编译】——>【链接】——>【运行】总共要经过这些步骤算是一个程序完成的诞生过程。如果我们要去细谈其中的过程,又可以像下面这样分化
gcc
所要做的事情。在下一模块我就会对预编译
、 编译
、汇编
、链接
这四个小模块展开做讲解gcc英文全名为
GNU Compiler Collection
,早期的gcc编译器主要用于C语言编译,但是经过几十年的发展,gcc编译器可以用于多种语言的编译,例如C++、Go等目前较为主流的语言。
熟悉gcc编译器是对于我们开发C/C++程序的底层基本功,虽然目前各厂商的开发IDE已经非常智能,从某种程度上已经把程序员从底层代码的编译、部署等工作解放出来,但是如果需要开发大型C++项目或者对于编译过程进行优化,那么gcc编译器是需要进行了解和深入的
编辑
、编译
、链接
、调试
这些功能编辑
功能来说有【编辑器】编译
功能来说有【编译器】,VS2019为cl.exe
链接
功能来说有【链接器】,VS2019为link.exe
调试
功能来说有【调试器】相信有了这些,你对程序的诞生过程一定有了一个更加深入的理解
讲了这么多,要如何去使用gcc呢?来看看吧
格式 gcc [选项] 要编译的文件 [选项] [目标文件]
具体如何使用,请看下一模块
好,接下去我们就来谈谈如何如何使用gcc来观察程序在翻译环境下的各个步骤。一下的讲解可能比较简略一些,如果想了解得更加深入,可以看看我的另一篇文章——> C生万物 | 程序的翻译环境和执行环境。很多内容也是从这篇文章中归纳出来的
在预编译阶段会执行以下四件事
gcc需要执行的指令
gcc -E file.c -o file.i
-E
表示是让 gcc 在预处理结束后停止编译过程-o
表示输出到指定文件【-o 后面紧跟的永远都是你要形成文件的名称】.c
结尾的文件表示源程序;.i
结尾的文件表示已经过预处理的C原始程序在编译阶段会执行以下四件事
以上其实就是在将C语言的代码转化为汇编代码的一个流程
gcc需要执行的指令
gcc -S file.i -o file.s
-S
表示只进行编译而不进行汇编,生成汇编代码.i
结尾的文件表示源程序;.s
结尾的文件表示经过编译生成的汇编代码在汇编阶段会执行以下两件事
gcc需要执行的指令
gcc -c file.s -o file.o
-c
表示是gcc编译到目标代码.s
结尾的文件表示经过编译生成的汇编代码;.o
结尾的文件表示经过编译生成的汇编代码此时我们可以来试试执行一下这个目标文件,不过发现没有【x】可执行的权限,于是使用chmod
做一个提权的操作。但是系统却说cannot execute binary file
,上面看到过这是一个二进制文件,对于二进制文件来说是不可以被执行的
对于上面的这三步【预编译】、【编译】、【汇编】都是对一个源文件中的代码进行操作的过程,没有引入其他人的代码。但是到了链接这一步,可能就要与别人的代码或者是库中的代码一起执行
在链接阶段会执行以下两件事
gcc需要执行的指令
gcc -E file.c -o file.i
-E
表示是让 gcc 在预处理结束后停止编译过程;-o
表示输出到指定文件.c
表示源程序;.i
表示已经过预处理的C原始程序my_out
,它也是一个内容也是二进制代码,可供机器识别 命令选项:-E
-S
-c
【键盘左上角的Esc,不过中间的S要大写】
文件后缀:.i
.s
.o
【.iso为镜像文件的后缀,ISO也为国际标准化组织】
看了这么久gcc,我们来看看g++,其实这两个编译器的用法是一模一样的,只是对于gcc才说是用来编译C语言代码,对于g++来说是用来编译C++的代码
在上面,我们说到了对于一个程序而言分为四步,预编译 --> 编译 --> 汇编 --> 链接。对于前面三步而言我给出了对应的记忆方法。但是对于最后一步的链接,却不是那么好理解,所以我们专门来谈谈这个链接
首先要说一些必不可少的前言小知识
对于上面这个问题相信你也是非常得困惑,在上面的演示过程中,我也往file.c
里写了一些C语言的代码,其实你把后缀改为.cc
就可以写一些C++的代码了
stdio.h
、stdlib.h
等等printf()
和scanf()
这些库函数,这下你应该知道为什么我们在代码的时候引入stdio.h
这个头文件就可以使用里面的库函数了接下去我们来介绍一个指令,因为在后面的讲解中会使用到
格式:
ldd filename —— 检测可执行程序形成的时候都依赖了哪些库
l -> list 【列出】
d -> dynamic 【动态的】
d -> dependencies【依赖关系】
——> 列出动态依赖关系
a.out
这个可执行程序都依赖了哪些库。然后可以看到对于我框起来的这个就是标准的C语言库知道了这些其实就可以回答上一个小节我所提出的问题了
【最后的答案是】:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是⭐链接⭐的作用
这样就又衍生出了一个问题:
接下去我列举几个常见的指令
【最后的答案是】:程序、工具、指令是一回事【都是用C语言写的,都是经过编译形成的,都和C语言标准库有关】
看完了上面这些,对Linux中的库有了一个基本概念后,我们便可以来谈谈【动态库】与【静态库】,可能现今我的水平有限,讲不了很深入,后续弥补~
动态库
静态库
但是光这么看动静态库就非常晦涩难懂了,不用急,在下一模块,我将用更加形象的方式来介绍它们
本故事均为虚构,如有雷同纯虚巧合U•ェ•*U
故事背景:你是一个初三的学生,在学校被老师管,在家里又被父母管,于是你就想着在上了高中之后要住校,然后就可以无拘无束地玩了,你呢,很喜欢玩游戏,于是就找到你心仪哪所高中的学长询问附近有没有对应的网吧,提前做好打听;
一年后你也考上了自己心仪的高中,终于可以挣脱父母的管教了
我们可以将这个网吧当做的是系统中的库,然后把你所列的这个清单当做是一段程序。那对于做数学作业、写文章这些就相当于是你在写一些自己的算法或者是简单的循环、条件判断语句,不需要调用库中的内容。但是当程序执行到了【上网】阶段后,就无法再自己执行下去了,只能去调用库函数
为什么呢?就相当于是 我们在写代码的时候可能有一段逻辑写不下了,因为这段逻辑很复杂。但是要是实现这个逻辑刚好有这个库函数,所以你就去会调用这个库函数。这个库函数就相当于是网吧
但是你怎么知道这个库函数在哪里的呢?——>你学长告诉你的呗。也就相当于你知道了库函数中这个函数的地址在哪里,你只需要顺着北门然后根据这个地址找过去就可以了
那这么来说你的学长就相当于是一个链接器,网吧就是一个动态库,你知道了动态库中所需要的这个函数的地址,然后你就顺藤摸瓜地找到了这个地方,你的学长就做了一个链接的功能,还
记得我在一开始放出的那张图吗,你就可以把自己想做是目标文件。那个可执行程序也就是最后你可以打游戏的那台机子。你在打游戏的这个过程其实就相当于是在调用库函数的一个过程了。最后你回到学校那也就是调用函数结束,然后要继续往下执行你的程序
上面有说到,这个是网吧就是动态库,我们在上面有说到过这个很多指令都在使用这个C语言的标准动态库
这个警局你可以认为是恶意入侵的黑客,然后将你系统的库搞坏了,那网吧中的点电脑就可以算作是指令,若是库没了,那你的指令就无法执行了,所以我们不要去随便动系统中的已经写好的库,否则你的Linux就无法正常使用了
因此我们其实可以初步看出来这个动态库其实是不安全的,因为是共享的,所以大家都可以使用,但是只要它被破坏了,那大家都没得玩了
故事还在继续。。。
可以看到,其实对于从网吧中拿来的电脑,就是库中的那个函数,也就是将库中的整个函数直接放到你的程序中来,这样你完全不需要再到库中去调用函数了,直接在你的程序中使用即可
当你的同学去到黑网吧,然后得知被封了之后无奈返回后,这个时候你有这自己的电脑,完全不需要再去考虑是否有电脑可用的安全性了,但就是这个电脑放在宿舍可能有些占位子,原本比较宽阔的空间就要被一台电脑给霸占了,那确实是没办法
讲完了上面这些,相信你对【动态库】和【静态库】一定有了一个自己的理解,但是可能还有有点模糊,在下一小节我将对他们做一个进一步的分析
接下去我们对【动态库】和【静态库】做一个分析梳理,更好地理解动静态库的原理
✔【动态链接】 —— 仅仅是把库中的你所用的方法的地址拷贝到程序里 网吧
可以联想到,对于小蚂蚁网吧来说,大家都可以去玩,只需要知道网吧的地址在哪里,然后直接去即可,也就和函数一样直接进行一个调用,对自己的程序而言不需要消耗任何的空间
但是呢一旦这个网吧被封了,也就是动态库丢失了,那此时我们再去调用库中的函数就会失败,这是动态库比较致命的一点
✔【静态链接】 —— 自己代码当中用到的库中的方法直接拷贝到程序里 台式电脑
对于静态库来说,确实是比较方便,不需要再去库里面调函数了,直接把这个函数整体拷贝到我们的程序中即可。也就是我们不需要依赖任何的库,也不用去大象网吧
但是呢这也意味着我们的程序会变得非常庞大,举个最简单的例子就是你完全不写任何自己的函数,所有功能和实现都放在main函数里,这就会显得非常浪费空间
file
指令可以用来查看一个文件的类型,那我们就可以来看看这个动态的可执行文件dynamically linked
动态的链接,这也就意味着这个可执行文件是经过动态链接生成的(gcc编译完默认是动态链接)-static
的选项即可ldd
去列出动态的依赖库,就会报错说not a dynamic exexutable
不是动态可执行文件,所以就进一步可以看到ldd
只能列出动态依赖库,对于静态库是没有办法列出来的file
指令去查看这个文件的文件类型,于是就可以看到【statically linked】静态链接就可以进一步验证这是一个使用静态链接成的可执行文件不过你在使用静态链接的时候可能会报错,因为对于我们的云服务器,默认就只装了【动态库】,而静态库是没有的,所以要安装一下静态库
(sudo) yum -y glibc-static
(sudo) yum -y install libstdc++-static
看完了上面这些,相信你对动静态库有了一个初步的了解,不过我没有讲得很深入,如果后续有更好的内容会继续补充
最后,我们来总结一下本文所学习的内容
.c
的源文件究竟是怎样一步步变成一个.exe
可执行程序的,其中要经过【预编译】【编译】【汇编】【链接】四个步骤链接
做了详细的介绍,谈到了Linux中库的概念,然后了解到了【动静态库】,我用了一个小故事让你更好地感受什么是动静态库,相信你在看了这个故事后一定也会有所收获。了解了什么是动静态库后就逐步上手使用他们进行动态链接与静态链接
,进一步感受到了linux的纯粹以上就是本文所要介绍的所有内容,感谢您的观看