动静态库
头文件与库的区别
-
头文件一般而言,是声明和宏定义。头文件是在预处理阶段使用的
-
库文件是已经编译好的二进制代码。是一种目标文件,库文件是在链接阶段使用的
-
对于头文件和库我们可以这样理解,就是头文件提供的是一个函数的声明,并没有这个函数的具体代码,而库就是存放这个函数的具体实现代码
我们在写代码的时候,经常依赖别人给我们提供的函数,比如头文件中的reverse函数,我们在写的时候,这个头文件中包含有这个函数的声明,而这个函数的定义是存在于库中的,使用库的意义在于,我们可以将函数的定义直接编译成二进制,使得别人看不见这个函数的具体实现方法,别人使用时,只需要根据声明中的说明即可使用,这样使得软件作者既实现了发布方便或替换方便,方便程序的部署与开发。也方便了一些发布者不想让别人看到自己写的函数的具体实现方法,保护了代码隐私。当然,库中肯定不止有函数的定义,这里只是举了一个简单的例子,方便理解。
库的种类
库文件是已经编译好的二进制代码,这个二进制代码可以是动态的,如.so;也可以是静态的,如.a
如果是动态的,则最后生成的程序文件在运行时,需要这个动态库的支持,动态库又叫做共享目标文件
如果是静态的,则最后生成的 可执行程序文件运行时可以脱离这个库文件而独立运行。 静态库又叫做可重定位目标文件
目标文件
在生成我们的可执行程序之前,我们的程序一共会经过四个阶段:预处理,编译,汇编,链接。
在经过前面三个阶段后,汇编阶段会生成所谓的目标文件,windows下为.obj文件,Linux下为.o文件,目标文件是还没经过链接的文件,也就是说此时的目标文件中,有些符号还未被调整成正确的有效地址,仍然临时地址,如何理解呢?可以理解成,此时你写的程序内使用的reverse函数中还只是只有声明的部分,需要通过找到reverse存在的库的目标文件链接起来,这样才能成功调用reverse函数。
PC端的可执行文件格式主要包括了windows下的PE(Portable Execute)以及linux下的ELF(Execute Linkable Format),而目标文件属于生成可执行程序文件过程中的中间文件,其格式几乎与可执行程序的文件一模一样,因此可以看成是一种类型的文件,只不过是需要再通过最后一个阶段链接阶段将目标文件中各种各样的段合并起来,本质上文件格式并没有改变什么,想要深入了解可以参考一下这篇博客:目标文件详解,而我们的ELF格式的文件也是有分类型的:可重定位文件(relocate file),可执行文件(executable file),共享目标文件(shared object file)
-
可执行文件
Linux下和windows下的.exe文件,也就是可以直接执行的文件
-
可重定位文件
windows下的.obj文件和Linux下的.o文件,也就是需要通过链接后形成可执行程序的目标文件
-
共享目标文件
动态链接文件,可以和其他可重定位文件和共享目标文件一起链接,生成新的目标文件
静态库的特征、包装与使用
特征
因为静态库是直接把依赖的库的代码链接到可执行文件中,也就是在进行链接一步时,把库中需要的内容直接拷贝一份到最终的可执行文件中,比如说你在自己的代码中写的reverse函数,头文件中是只有声明的,链接一步时, 需要进行合并符号表,而reverse函数的定义存在于库中,也就是需要合并库的符号表,若选择了静态库,即静态链接方式,就是把库中的符号表拷贝一份进可执行文件中,使得可执行文件的大小会明显地变得非常大。
包装
-
先写好 .c文件和 .h 文件
-
将 .c 文件编译成目标文件
-
使用ar命令生成静态库
-
将头文件和库文件打包起来
将所有的头文件放在include目录下,静态库文件放在lib目录下,再将include目录和lib目录放在同一个目录下,这样将此目录发送给其他人即可使用:
- 可以使用Makefile文件一次性全部生成:
使用
-
使用:将mylib文件夹放在main函数所在的目录下
问题
动态库的特征、包装与使用
特征
静态库是库的代码与自己的代码一起在代码区来回跳,也就是执行可执行程序时就把库代码拷贝一份到自己的代码区了。动态库是一开始在硬盘中,执行到需要库的代码时,再将库加载进内存中,然后将库中对应所需的函数等方法建立与页表的映射关系,映射到共享区当中,然后代码区执行的时候再跳转到共享区:
包装
-
先写好.c .h 文件
-
将.c 文件以fPIC生成无关码方式编译形成.o文件,作用:告诉编译器此目标文件在任何路径都无所谓
-
直接用编译器使用 -shared选项生成动态库.so 文件
-
将.h 文件和 .so 文件包装起来
-
使用到的命令:
-
-
展示:
使用
问题
- 其实,我们使用 -I -L -l选项时,其实是告诉编译器路径,但一旦可执行程序编译完毕,编译器就做完了他自己的事情了,我们要运行可执行程序时,就与编译器没有关系了,而是操作系统的事情,操作系统此时就找不到路径了,静态库不用找是因为库中代码已经被拷贝进自己的代码中了,并不需要再去找了, 所以为了让操作系统能够顺利找到并执行,有以下方法:
成功正常运行
但这种属于内存级别的环境变量,在下一次重启时就会被清掉了:
35)]
- [外链图片转存中…(img-55lGQr3v-1707561573535)]
成功正常运行
[外链图片转存中…(img-A6itzueN-1707561573535)]
但这种属于内存级别的环境变量,在下一次重启时就会被清掉了:
[外链图片转存中…(img-yaa4exwB-1707561573536)]