文章目录
前言:
在Liunx中存在着两种编译器gcc
/g++
,gcc是只用来编译c语言的编译器,而g++可以编译c/c++。
程序的翻译过程需要经历四个步骤:预处理,编译,汇编,链接。
一.预处理
- 预处理的功能主要包括:宏替换,去注释,条件编译,头文件展开等
- 预处理指令是以
#
号开头的代码行。 - 实例:
gcc --E code.c --o code.i
- 选项
-E
,该选项的作用是让gcc
在预处理结束后停止编译过程。 - 选项
-o
是指目标文件,i
文件为已经过预处理的C原始程序。
gcc命令提供了多种选项来控制编译过程:
-E
:仅进行预处理。-S
:生成汇编代码而不进行汇编。-c
:生成目标文件而不进行链接。-o
:指定输出文件的名称。-I
:指定头文件搜索路径。-g
:生成调试信息。-O
:指定优化级别(0-3)。-Wall
:生成所有警告信息。
例如:gcc -E code.c -o code.i
这里如果不带-o
选项,直接-E
,它会把所有代码预处理的结果直接给我们打印到显示器上,不便于我们的查看和分析。
所以我们可以带个-o
,保证它把输出结果保存在我们的目标文件code.i
中。
我们把code.c
和code.i
进行对比就会发现:
- 宏被替换了
- 注释不见了
- 条件编译不见了
那为什么我们的代码只有26行在预处理过后变成了七百多行呢?答案是预处理帮我们做了头文件展开。
头文件展开
所谓的头文件展开其实就是把你要包含的头文件的相关内容拷贝进你的源文件里,这个工作就叫做头文件的展开。你包含的头文件里面有可能呀包含了其他的头文件,所以我们的头文件展开是一个递归式的展开。所无论是系统的头文件还是我们自己写的头文件在预处理过后就不需要了。在最后编译时只编译源文件,与头文件无任何关系。
一般情况下我们的头文件都是在include
目录下的:
这里面包含的就是我们c/c++标准的头文件。也就是说从标准库所匹配的头文件是被安装在Liunx系统里的。头文件包含了我们标准c里面的方法与声明,未来我们c标准库的实现是在一个叫做动静态库当中的。这个库也是Liunx提前帮我们装好了的。所以我们在做c语言的开发时不仅需要安装vs,其实它还帮助我们安装了c语言的头文件和标准库。
条件编译
条件编译就相当于对我们的代码进行裁剪。
有什么用呢?
比如我们使用的很多软件有校园版,家庭版,社区版,有些是收费的,有些是免费的,那么收费版和免费版的有什么区别呢?比如这个软件有很多功能,但免费版的只提供给你十几项功能,收费版的会给你提供几十项功能,那么这个软件就需要维护两套代码吗?答案是不需要,实际上企业只需要维护一份代码,免费版减少的功能只需要用条件编译裁掉收费版的即可。所以我们的维护软件可以使用条件编译来实现代码版本的维护。
二.编译(生成汇编)
- 在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
- 用户可以使用
-S
选项来进行查看,该选项只进进编译而不进行汇编,生成汇编代码。 - 实例:
gcc --S code.i --o code.s
例如:gcc -S code.i -o code.s
就生成了对应的汇编
但是汇编语言也不能直接执行,我们需要汇编,生成机器可执行的二进制码。
三.汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的
.s
文件转成目标文件 - 在此可使用选项
-c
就可看到汇编代码已转化为.o
的二进制目标代码了 - 实例:
gcc --c code.s --o code.o
例如:gcc --c code.s --o code.o
我们会发现他里面是乱码
其实它已经变成了二进制数据了
即便是这种二进制文件也不能执行,只能叫做可重定位目标二进制文件。现在的code.o
确实是二进制文件,但是这种程序跑不了,原因是现在编译的代码只是我们自己写的代码,举个例子我们的代码里面用了print
,但是这个print
函数底层不是我们实现的,任何函数都有函数体,之前include<stdio.h>只是函数的声明,这个函数实现在哪我们并没有。所以我们无法执行,因为我们的代码里面还有一些符号并没有被解释。所以我们需要做最后一件事物:链接
四.链接(生成可执行文件或库文件)
- 链接阶段是将.o文件和库文件进行链接,形成可执行程序
- 实例:
gcc code.o --o code
例如:gcc code.o -o code
运行code:./code
可是我们怎么没见到链接什么库呢?首先我们今天用的工具是gcc
,我们code.c中写的printf函数全是c语言中的库,并没有使用第三方的库,gcc是专门用来编译c语言的,所以它在自己内部就能找到c语言的标准库在哪。在这里体现不出来。我们只需知道在这里它隐性地把我们的code.o
文件和c标准库形成一个可执行程序,至此我们就完成了程序的翻译。
📌这里分享一个小技巧:如果我们记不住上面的E
,s
,c
选项的话,请看我们键盘的左上角Esc
,这样就能很快的记住了。对应形成的文件为.i
,.s
,.o
,所以连起来Esc iso
,是不是还挺押韵。
库的介绍
我们再学习c语言时,首先接触到的就是c标准库,像我们使用的printf
,scanf
,strlen
等这样的函数都不是我们自己写的,它们都是在库里面的。
库可以分为几种主要类型:
1. 静态库(Static Library):
- 静态库在编译时与程序链接,成为程序的一部分。
- 常见的文件扩展名包括
.lib(Windows)
、.a(Unix-like系统)
。 - 优点:编译后的程序不依赖于外部库文件,便于分发。
- 缺点:增加了程序的体积,因为库代码被直接复制到程序中。
2. 动态库(Dynamic Library):
- 动态库在程序运行时被加载,程序通过动态链接器与库进行链接。
- 常见的文件扩展名包括
.dll(Windows)
、.so(Unix-like系统,如Linux)
、.dylib(macOS)
。 - 优点:节省内存和磁盘空间,因为多个程序可以共享同一个库文件。
- 缺点:需要确保库文件在运行时可用,增加了程序的依赖性。
3. 框架(Framework):
- 框架是一种特殊的库,它提供了一组相互关联的类和接口,以及用于组织代码和资源的结构。
- 框架通常用于构建大型、复杂的软件系统,如Web应用、桌面应用等。
- 常见的框架包括
Spring(Java)
、Django(Python)
、React(JavaScript)
等。
4. API(应用程序接口)库:
- API库提供了一组函数和接口,用于与操作系统、硬件、其他软件或网络服务进行交互。
- 例如,图形库(如OpenGL、DirectX)提供用于绘制图形和图像的接口,数据库库(如MySQL Connector)提供用于与数据库进行交互的接口。
动静态库和动静态链接
操作部分:
首先在Liunx中动态库一般都是(libXXX.so)
的文件,静态库一般都是libXXX.a
的文件。在windows当中的动态库一般是(XXX.dll)
的文件,静态库是(XXX.lib)
的文件。
在我们的gcc
命令中,如果你直接-c
既没有-E
也没有-s
,那么他就会帮我们进行预处理,编译,汇编然后停下来;如果直接-c
并且也没有带生成目标文件,那么编译器回自动帮我们形成同名.o
文件。
上面我们已经讲了这个code.o
文件叫做可重定位目标二进制文件 ,这个文件还不能执行,需要链接一下才能生成可执行文件。
gcc code.o -o mycode
这样调用的话它就会自动链接形成mycode
可是我们怎么知道它链接了呢?
我们进行ldd mycode
就可以发现我们最终的可执行程序在链接时依赖了什么文件
在依赖文件中我们只需要看这一个
最终形成的可执行程序依赖一个叫做libc.so.6
,它的目录是/lib64/lbc.so.6
这是什么呢?
我们按照上面的路径提醒进入到这个目录里面
在Liunx中动静态库的名字一般是(libXXX.so/.a)
,去掉前缀和后缀就是这个库的名字,上面的libc-2.28.so
去掉前缀去掉后缀就变成了c-2.28
,表示c语言库的2.28版本。我们又称.so
结尾的为动态库
表示默认形成的就是动态链接 形成的可执行程序。
那么我们想让我们的程序静态链接 形成可执行程序又该怎么办呢?
gcc code.o -o mycode-s -static
其中的-static
表示采用静态链接的方式。
前提:你的系统里面要有静态库
总结:
- gcc默认形成的可执行程序是动态链接的
gcc -static
:采用静态链接的方式- 不管是动态链接还是静态链接,前提都得保证你有对应的库
- Linux一般只存在动态库,动态库也称之为共享库
如果我们想在我们的Linux下安装c语言或者c++的静态库,我们可以输入yum install glibc-static/libstdc++-static -y
,一般情况下的静态库是没有装的,需要我们手动去装。
理论部分:
- 动态链接: 只是把库的地址拷贝到我们的可执行程序里。只是告诉我们对应的方法在哪,想用的话直接跳转在相应的地方。
- 静态链接: 把我们要的库的方法实现直接拷贝到我们的可执行程序中。所以一般静态链接的体积会比动态链接的体积要大
动静态链接的优缺点:
- 动态链接
优点: 形成的可执行程序的体积小,节省空间
缺点: 库一旦丢失,所有程序都将运行不了 - 静态链接
优点: 一旦编译好,将不再依赖任何库
缺点: 浪费空间(磁盘,内存,网络)
今天的内容就分享到这里,如果这篇文章对你有帮助,记得点赞,评论+收藏 ,最后别忘了关注作者,作者将带领你探索更多关于Liunx方面的问题。