只要积极创造,机遇无时不有;只要善于探索,真理无处不在。💓💓💓
目录
✨说在前面
🍋知识点一:Linux编译器-g++、gcc
•🌰1. 背景知识
•🌰2. gcc如何完成
•🌰3. 条件编译的作用
•🌰4. 函数库
🍇库的定义
🍇动态库与静态库
🍇动静态库对比
🍇验证工作
🍇技术上理解库
• ✨SumUp结语
✨说在前面
亲爱的读者们大家好!💖💖💖,我们又见面了,上篇文章我们讲解了linux中的重要工具-yum和vim,大家可以回顾一下,加深印象~
我们今天给大家讲解一下Linux中的另外一个重要工具-编译器g++、gcc以及他们在Linux命令中的使用。更重要的是,由编译和链接这部分知识所牵涉出来的动态库、静态库的链接问题。动静态库是很重要的,在后面我们还会进行二次学习,希望大家可以好好学习本篇博客,为以后的学习打下坚实的基础。
👇👇👇
💘💘💘相关链接如下(直接点击即可)【Linux】Linux重要工具
🎉🎉🎉xshell + 云服务器🎉🎉🎉
博主主页传送门:愿天垂怜的博客
🍋知识点一:Linux编译器-g++、gcc
•🌰1. 背景知识
对于这一部分内容,需要一些前置的编译链接的相关知识。如果大家忘记了或者对这部分内容还有欠缺,可以看看我的这两篇文章:
【C语言】编译与链接过程详解:从源码到可执行程序,-CSDN博客
【C语言】详解预处理-CSDN博客
•🌰2. gcc如何完成
🍇预处理
预处理功能主要包括宏定义、文件包含、条件编译、去注释等。 预处理指令是以#号开头的代码行。 gcc –E hello.c –o hello.i 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
我们可以看到.i为后缀的文件处理了宏、条件编译,并且展开了头文件,所以main函数所在的位置是841行,而上面都是头文件的内容。
🍇编译
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
🍇汇编
汇编阶段是把编译阶段生成的“.s”文件转成目标文件,读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了。
注意:以.o为后缀的文件称为二进制可重定位目标文件。
🍇链接
在编译代码的过程中,我们通常将多个.c文件编译形成.o文件,然后结合链接库一起打包形成可执行文件,而对于.i、.s为后缀的文件在编译过程中不会正在形成。
举例如下:
•🌰3. 条件编译的作用
从大家从学习面向过程语言开始,就学习了条件编译。那么大家当时是否疑惑,为什么要有条件编译,条件编译究竟有什么作用呢?
我们利用上节课学习的vim写出下面代码:
这份代码相信大家都能看懂,由于定义M被注释了,所以应该会输出 professional version(专业版)。但是注意,在编译的过程中,我们可以加上宏定义的指令,如下
因此,条件编译有什么意义呢?我们举出下面三个场景:
scene1:如上,软件对专业度、收费情况进行区分(业务),使用条件编译,可以进行代码动态裁剪。
scene2:内核源代码也是采用条件编译进行动态裁剪。
scene3:开发工具,应用软件。
•🌰4. 函数库
🍇库的定义
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
定义:库是一套方法或数据集,为我们的开发提供最基本的保证(基本接口、功能,加速我们二次开发)。
🍇动态库与静态库
1. 动态库(.so / .ddl):动态链接既涉及编译时的准备工作(如记录外部符号引用、生成占位符地址等),也涉及运行时的实际链接过程(如加载动态库、解析符号地址等)。程序的编译链接阶段会进行一些相关的设置,以确保程序具备在运行时到动态库中寻找所需方法的能力。
我们可以用一个例子来理解动态库链接的过程。
假设你初中刚毕业,看到了高中的课表,其中计划下午13:00进行上网,但是不知道在哪里上网,于是你询问学长得知在学校东门左100米处有一个XX网吧,可以在这个地方上网。
在这个例子中,初中刚毕业,是在上高中之前通过询问学长的方式得到上网的地址,而高中就相当于程序,当上高中之后,我直接根据之前询问学长所得知的网吧地址直接去就行了,这就是动态链接在运行时的实际链接过程。
注意:动态库被多个程序共享,一旦缺失,会导致所有程序无法执行!
2. 静态库(.a / .lib):程序在编译链接的时候将库的代码连接到可执行文件中,程序运行的时候将不再需要静态库。
我们依然继续刚才的例子,学校发现了很多学生都喜欢在网吧打游戏,这对学生的成绩造成了很大的影响,所以学校取缔了这个网吧,于是你中午没上网,导致你不想听下午的课程了,这就对应了动态库的不可缺失性。
但是你就是想完成这样一个过程,于是你找到了这个网吧的老板,要求在电脑仓库中拿出进而我之前上网的机子同款的机子买下来,然后带到学校去进行操作,这就是静态链接。静态链接会将静态库中对应函数的方法直接拷贝给我自己,因此会造成静态链接的程序体积很大。
注意:静态链接只有在链接的时候有用,一旦形成可执行程序,该程序可以不再需要此静态库。
🍇动静态库对比
1. 链接动态库形成的可执行程序一定很小。
2. 可执行程序对静态库的依赖小,而动态库不能缺失。
3. 程序运行,需要加载到内存。如果是静态链接,会导致内存中出现大量重复代码,浪费资源。
4. 动态链接比较节省内存和磁盘资源。
🍇验证工作
1. 我们可以在 /usr/lib64 目录下查看系统的库:
2. 我们可以用 ldd 命令查看该文件依赖哪些动态库:
而 libc.so 就是C语言的标准库,包含了多种C函数方法的实现。
3. 我们可以通过在选项后面增加 -static 来强制静态链接:
注意:C++和C在库的链接上是一样的,唯一的不同是C的编译器使用的是gcc,而C++使用的工具是g++,大家可以敲一敲键盘自行体验C++的工静态库链接过程。
🍇技术上理解库
我们知道,在编译的过程中,我们会将一份或多份的.c文件转换成.o文件再和库进行链接。假设我们希望提供被人程序以及方法的声明,而不希望方法的实现被看到,我们应该怎么操作呢?
我们先写一份代码:
我们希望在 code1.h 中声明 func1 ,在 code1.c 中实现 func1,func2 同理,那么我们新创建一个 lib 目录,在该目录下进行操作:
完成code1和code2相应内容代码:
我们可以将 lib 保留,将备份文件拷贝出去:
接着,我们先将.c文件编译,然后我们就可以将含有 func1 和 func2 的.c文件删除了:
接下来我们直接链接.o文件,得到可执行程序:
这样,我们就像别人隐藏了主函数中特定接口的实现了。
其实,借助这个示例,我想表达的是,所谓库,本质也是许许多多个.o文件构成的。我们写的代码编译成.o后,和库链接,其实就是链接这些.o文件。
• ✨SumUp结语
到这里本篇文章的内容就结束了,本节给大家讲解了Linux的又一类重要工具-编译器。大家可以将我文章中写的操作自己实操一遍。加深印象。希望大家能够认真学习,迎接接下来的挑战,期待大家继续捧场~💖💖💖