📙 作者简介 :RO-BERRY
📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持
目录
- 1.前言
- 2.gcc/g++使用方法:以gcc为例
- 3.程序翻译的四个阶段
- 4.函数库
-
- [4.1 函数库的分类](#4.1 函数库的分类)
- [4.2 动静态库的区别与优缺点](#4.2 动静态库的区别与优缺点)
- [4.3 动静态库链接方式](#4.3 动静态库链接方式)
1.前言
Linux下皆文件,在Linux下文件不分文件的后缀,文件的类型是看文件前面的字符
今天要学习的gcc和g++就是Linux下的程序
它们是要看文件后缀的!
- gcc: C语言编译器,只能编译C语言
- g++: C++编译器,C/C++都可以编译
2.gcc/g++使用方法:以gcc为例
编译文件:
- 编译文件并形成a.out的执行文件
- gcc test.c
- 编译文件并形成test的执行文件,这里的test可以自取名字
- * gcc test.c -o test
执行可执行程序
当我们编译好程序后,会在当前目录生成一个
可执行程序
- 执行形成的a.out的执行文件
- ./a.out
- 执行形成的test的执行文件
- ./test
注:.代表要在当前目录下找可执行文件.必须加上
gcc/g++选项
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S 编译到汇编语言不进行汇编和链接
-c 编译到目标代
-o 文件输出到 文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息。GNU 调试器可利用该信息。
-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-O0
-O1
-O2
-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
3.程序翻译的四个阶段
3.1 预处理(进行宏替换)
预处理功能主要包括
宏定义,文件包含,条件编译,去注释等。
预处理指令是以#号开头的代码行。
创建预处理文件
我们使用指令看一看
gcc --E test.c --o test.i
选项"-E",该选项的作用是让 gcc 在预处理结束后停止编译过程。
选项"-o"是指目标文件,".i"文件为已经过预处理的C原始程序。
查看预处理文件
vim test.i
可以看到上面全部都是宏定义,文件包含,条件编译,去注释等操作,一共有八百多行,最后才是我们写的文件操作
3.2 编译(生成汇编)
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
用户可以使用"-S"选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
创建编译文件:
gcc --S hello.i --o hello.s
查看编译文件:
vim test.s
这里的汇编代码没学过真不好懂
3.3 汇编(生成机器可识别代码)
汇编阶段是把编译阶段生成的".s"文件转成目标文件
在此使用选项"-c"就可看到汇编代码已转化为".o"的二进制目标代码了
创建汇编文件
gcc --c hello.s --o hello.o
查看汇编文件
vim test.o
此时使用vim打开文件是乱码
可以使用指令od查看二进制文件:
3.4 链接(生成可执行文件或库文件)
在成功编译之后,就进入了链接阶段。
链接阶段是将编译成功的文件变成可执行文件
gcc -o test test.o
这里我们将test.o文件进行编译并生成test可执行文件
链接阶段比较复杂,它大致的流程是这样的:
.o文件 + 系统库 = 可执行程序
在本篇文章的后半段讲完动静态库后再解释!
4.函数库
我们的C程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现"printf"函数的呢?
最后的答案是:
系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径"/usr/lib"下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数"printf"了,而这也就是链接的作用
这里我们引入一个新指令:
ldd 可执行程序名
查看一个可执行程序依赖的第三方库
可以发现,在路径lib64里有一个文件叫libc.so.6
这个就是刚刚的C语言程序所要依赖的库
可见,libc.so.6实际上就是C的标准库
其中Linux环境下,动态库的后缀是.so,静态库的后缀是.a。在Windows环境下,动态库的后缀是.dll,静态库的后缀是.lib。所有的库文件,都遵守相同的命名规则,即:libname.后缀.xxx。
小Tips:gcc编译器会默认找到C的标准库,它会把我们写的源代码经过编译得到的目标文件与库文件进行链接。这也是为什么gcc不能去编译C++的源文件,因为gcc默认找的是C的标准库,它找不到C++的库。
现在我们就可以理解链接过程了
链接过程就是将.o文件和动/静态库结合的过程
4.1 函数库的分类
函数库一般分为静态库和动态库两种。
-
静态库(static library, Statically-linked library, LIB)或称静态链接库,是一个外部函数与变量的集合体。静态库的文件内容,
通常包含一堆程序员自定的变量与函数,在编译期间由编译器与连接器将它集成至应用程序内,并制作成目标文件以及可以独立运作的可执行文件。
而这个可执行文件与编译可执行文件的程序,都是一种程序的静态创建(static build)。其特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。
即静态库中的指令全部被直接包含在最终生成的可执行文件中。静态库的后缀是.a
-
动态库(Dynamic library, Dynamic-link library, DLL)或称动态链接库,是微软公司在微软视窗操作系统中实现共享函数库概念的一种实现方式。
动态链接库可被多个程序同时使用。
所谓动态链接,就是把一些经常会共用的代码(静态链接的OBJ程序库)制作成DLL档,当程序有需求时函数才进行链接。透过动态链接方式,存储器浪费的情形将可大幅降低
。静态链接库则是直接链接到可执行文件。动态库的后缀是.so
gcc hello.o --o hello
gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
4.2 动静态库的区别与优缺点
- 动态库是共享库,通过函数地址来关联程序
- 静态库是私有库,可以独立运行
动态库
: 是C/C++或其他第三方提供的所有方法的集合,被所有程序以动态链接的方式关联起来
静态库
: 是C/C++或其他第三方提供的所有方法的集合,被所有程序以拷贝的方式,将需要的代码拷贝至自己的可执行程序当中!
库 | 优点 | 缺点 |
---|---|---|
静态库 | 有效的节省资源(磁盘空间、内存空间、网络空间等) | 一旦缺失,所有程序都无法运行 |
静态库 | 不依赖库,编译成功的可执行程序,可以独立执行,不需要再向外部要求读取库函数中的内容 | 体积大,比较消耗资源 |
4.3 动静态库链接方式
链接分为两类:动态链接 和静态链接。
静态链接
:由链接器在链接时将库的内容加入到可执行程序中。在编译器使用静态库进行静态链接的时候,会将自己的方法拷贝到目标程序中,该程序以后不再依赖静态库。动态链接
:连接器在链接时仅仅建立与所需库函数的之间的链接关系,在程序运行时才将所需资源调入可执行程序。动态库是被所有程序所共享的,一般也被叫做共享库。这意味着,动态库只需要一个就够了,它可以满足所有程序的需求。
Linux中,编译形成可执行程序,优先采用动态链接。
如果想使用静态库编译程序
使用指令:
gcc test.c -static
总的来说,静态库可以产出独立程序,动态库实现了模块化和代码共享,以空间换时间。
链接器会根据是静态链接还是动态链接,调用不同的处理逻辑,最终产出可执行程序。
所以同样的代码,静态链接和动态链接链接后的结果并不相同。