目录
一.前言
二.源代码的翻译环境
三.gcc相关指令
四.动静态库
1.什么是库?
2.库的命名
3.库的链接方式
4.动静态链接的优缺点
5.小结
一.前言
在Windows系统上我们常用VisualStudio来进行C/C++开发,VS并不是一款单一的软件,而是集成开发环境(IDE),其中包含编辑器,编译器,调试器等等,功能很强大。而在LInux当中,我们需要学会使用各项独立的工具才能进行开发,例如用vim编辑代码,用gcc编译代码,用gdb调试代码。
今天我们就来讲讲如何使用gcc来得到我们需要的可执行程序,并介绍编译过程中的动静态库问题。
二.源代码的翻译环境
我们编写的C语言是自然语言,它是供我们人类阅读的,计算机并不懂。所以需要将它翻译成计算机看得懂的二进制语言,而这个翻译过程,就是经常说的编译。
实际上一个或多个源文件变层可执行程序需要经历编译和链接两个阶段,但我们日常所说的编译是把链接阶段也包含在内的,下面我也不再刻意区分这两个阶段。
程序编译的过程包含以下四个阶段
- 预处理
- 编译
- 汇编
- 链接
而gcc编译器就是来完成这四项工作的
【注:想要进一步理解程序的翻译过程可参考我以前写的程序的编译(翻译)】
三.gcc相关指令
gcc -E code.c -o code.i
-E表示从现在开始进行程序翻译,等预处理完成就停下来,不要继续往后走了。
-o表示把生成的文件重命名
预处理阶段做了四件事:
- 头文件展开
- 条件编译
- 去注释
- 宏替换
得到的code.i文件依然是C语言代码
gcc -S code.i -o code.s
-S表示从现在开始进行程序翻译,等编译完成就停下来,不要继续往后走了。
这里的code.i是否可以换成code.c呢?肯定是可以的,但是没必要,因为这样之前的工作又会被重复做一遍。
这时的code.s文件就是汇编代码了
gcc -c code.s -o code.o
-S表示从现在开始进行程序翻译,等汇编完成就停下来,不要继续往后走了。
完成汇编后,code.o就是一个二进制文件了,但现在它并不能执行,因为还差最后一个链接阶段
gcc code.o mybin
链接并不像前几个阶段一样有专门的选项,因为链接就是翻译的最后一步,通常情况下我们都是直接执行以下指令一步到位,并不会傻傻地把这四个步骤分开做
gcc code.c -o mybin
细节问题 :
我们在test.c文件中写了这样一段代码
编译时发现不能通过
这是因为在for循环里定义临时变量c99标准里才支持的,我现在的这款gcc编译器默认是不支持的,要想采用c99标准编译代码,只需在后面加上std=c99即可
gcc -std=c99 test.c -o test.out
四.动静态库
简而言之:.o文件+库=可执行程序
故链接阶段就是将.o文件(可重定位的二进制文件)和库链接起来
那么接下来就有两个问题:什么是库呢?如何链接.o文件和库的呢?
1.什么是库?
下面介绍一个指令
ldd mybin
ldd是用来查看一个可执行程序依赖的第三方库
可以看出来mybin依赖三个库,其它两个不管,我们把中间的那个拎出来
我们在LInux系统中找一下,看是否有这个文件
果然有这个文件,而且给普通用户开放的权限还挺高,可读可写可执行。
注意库并不是头文件,预处理阶段include过来的并不是它哟,头文件这个路径下?
头文件放的是函数的声明,库提供方法实现。
故头文件+库+我们写的代码=我们的可执行程序
“LInux下一切皆文件“,所以库其实也就是个文件。系统中的头文件和库文件都是文件!所以,所谓的开发环境安装,一定要做什么工作?
安装下载并拷贝头文件和库文件到开发环境中的特定路径下,一定要能被编译器自己找到
在下载VS时,会让你选择安装C++桌面版,游戏开发等环境,说白了就是不同的头文件和库文件嘛
2.库的命名
以libc.so.6这个库为例
库要求以lib开头,so表示这个库是静态库,6是版本号,所以一个库的名字要去掉前缀和后缀,所以这个库的名字就是c,也就是传说中的C标准库。
库分为两类:动态库和静态库,在LInux当中,so表示动态库,a表示静态库。在常规的开发环境中,动态库居多
3.库的链接方式
动态库:是C/C++或其它第三方提供的所有方法的集合,被所有程序以链接的方式关联起来,这种链接方式叫做动态链接
静态库:是C/C++或其它第三方提供的所有方法的集合,被所有程序以拷贝的方式,将需要的代码,拷贝到自己的可执行程序中,这种链接方式叫做静态链接
库中所有的函数,都有入口地址,所谓的动态链接,其实就是把链接的库中的函数地址拷贝到我们的可执行程序中的特定位置。
4.动静态链接的优缺点
动态链接:优点:形成的可执行程序体积比较小,比较节省资源。
缺点:强依赖动态库,动态库没了,所有依赖这个库的程序都无法运行了
静态链接:优点:无视库,可以独立运行
缺点:体积太大,浪费资源
LInux系统中的指令,实际上就是一个个可执行程序
既然是可执行程序,那我们就能查看它所依赖的库
可以看到,pwd指令也是用C语言写的,它也依赖C标准库。实际上,LInux中的绝大多数指令都是用C/C++写的,如果我们将C标准库删掉,那么这个系统基本也就挂了,因为最基本的指令都无法运行,更别说其它操作了。
接下来我们想看看动静态链接的可执行程序的大小差异,如何做呢?
gcc默认是采用动态链接的方式,去找对应的动态库,所以采用如下指令即可
要想静态链接,只需在后面跟上一个-static
但有可能会报出这样的一个错误
这是因为我们的系统里默认是没有安装 C静态库的。我们可以手动安装一下C/C++静态库
sudo yum install -y glibc-static libstdc++-static
最终两个可执行程序都生成了,可以看到它们的体积大小相差了将近100倍,这还是我仅仅在里面写了一个helloworld的情况下。
所以这也能解释为什么编译器默认采用的是动态链接,因为静态链接太浪费空间了。这里的空间不仅仅指磁盘上的空间,还包括内存。你要知道,一个可执行程序要想执行,首先必须是要加载到内存中去的呀,所以使用动态链接加载到内存会更快
5.小结
我们的开发环境,编译器都要为我们做什么?
- 下载开发环境(头文件和库文件)
- 设置合理的查找路径
- 设定好我们形成可执行程序的链接方式
所以编译时出现链接问题,要么是库文件损毁了,要么是路径没或者链接方式没有设定好。