动静态库的实现(打包动静态库供他人使用)

大家好,我是 兔7 ,一位努力学习C++的博主~

如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步

如有不懂,可以随时向我提问,我会全力讲解~

如果感觉博主的文章还不错的话,希望大家关注、点赞、收藏三连支持一下博主哦~!

你们的支持是我创作的动力!

我相信现在的努力的艰辛,都是为以后的美好最好的见证!

人的心态决定姿态!

本文章CSDN首发!

目录

前言

动态库和静态库

1. 静态库

生成静态库

打包给别人使用

如何使用

2 动态库

生成动态库

打包给别人用

如何使用

3 总结

4 使用外部库


前言

        此博客为博主以后复习的资料,所以大家放心学习,总结的很全面,每段代码都给大家发了出来,大家如果有疑问可以尝试去调试。

        大家一定要认真看图,图里的文字都是精华,好多的细节都在图中展示、写出来了,所以大家一定要仔细哦~

        感谢大家对我的支持,感谢大家的喜欢, 兔7 祝大家在学习的路上一路顺利,生活的路上顺心顺意~!

动态库和静态库

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

        其实动静态库本质是可执行程序的"半成品"

        printf,scanf,abs ... ... 这些接口,使用者只要会用就可以,那么这些接口函数的具体实现在哪里呢?

        首先这些函数肯定是由代码写出来的,既然是代码,就必须要被编译,只要是要被编译,就要C程序中的文本代码翻译成可执行程序,也就是将文本翻译成二进制文件的过程。但是不是要将 printf,scanf,abs ... ... 这些函数形成可执行程序,而是要将这些函数编程基础模块供别人使用。

动静态库的实现(打包动静态库供他人使用)_第1张图片

动静态库的实现(打包动静态库供他人使用)_第2张图片

        其实我们在完成 printf 的时候不仅仅是因为我们调用了 printf ,其中还有我们的 Linux 在它的系统层面上编译程序时把库也接进来了。当然,我们也可以看一下:

动静态库的实现(打包动静态库供他人使用)_第3张图片

        那么我这圈起来的文件都在哪里呢?当然我们可以找一找:

        第二行看到的不就是软连接么!当然链接的那个文件我们也可以找到,可以看到这个就是一个库文件,也就是汇编之后的目标文件(.o)。

动静态库的实现(打包动静态库供他人使用)_第4张图片

        接下来就讲讲动静态库各自的特征:

动静态库的实现(打包动静态库供他人使用)_第5张图片

1. 静态库

生成静态库

        生成静态库的工具是 ar 。

动静态库的实现(打包动静态库供他人使用)_第6张图片

动静态库的实现(打包动静态库供他人使用)_第7张图片 

 动静态库的实现(打包动静态库供他人使用)_第8张图片

        这样我们就形成了静态库,如果上面的 Makefile 还是看不懂的话,那么其实和下面的操作是一样的:

动静态库的实现(打包动静态库供他人使用)_第9张图片

打包给别人使用

        首先我们需要在上面的 Makefile 中增加:

动静态库的实现(打包动静态库供他人使用)_第10张图片

动静态库的实现(打包动静态库供他人使用)_第11张图片

        我们可以看到生成了一个 mathlib 的文件,里面就是这种形状:

动静态库的实现(打包动静态库供他人使用)_第12张图片

        这个时候,我们只要将 mathlib 给别人,别人就可以使用了。

如何使用

        我们先写一个用来调用它的程序:

动静态库的实现(打包动静态库供他人使用)_第13张图片

        当我们运行的时候,发现它没有找到 add.h 这个文件。

        至于 stdio.h 为什么可以找到,是因为这个 stdio.h 这个头文件就在系统的 /usr/include/stdio.h 这个库目录下,所以编译器是可以找的到的,而我们的 add.h 这个头文件是在我们当前目录下  include 里,是我们自定义的,所以它找不到,那么为了让其找到:

动静态库的实现(打包动静态库供他人使用)_第14张图片

        我们可以看到,这里虽然还是报错了,但是报的不是没有找到 add.h 这个文件了,所以它是找到了的。

        只不过它还是说 my_add 是不存在的,因为我们没有告诉编译器 my_add 是一个方法,虽然头文件找到了,但是函数没有。因为头文件里只是声明了一下,具体的实现没有给。所以我们需要找到那个库!

        那为什么我们以前编译C的时候没指定呢?

        是因为C库本身就是在对应路径下呢,这些路径都是系统路径,编译器是可以找到的,而我们写的,编译器是找不到的,所以需要加上。

动静态库的实现(打包动静态库供他人使用)_第15张图片动静态库的实现(打包动静态库供他人使用)_第16张图片

         我们发现还是没还是报这个错误,其实是因为虽然我们这里的 mathlib/lib 里只有一个库,但是如果有多个呢?那么到底链接哪一个就是问题了,所以我们必须要指定链接哪一个库!前面说过,库的名字是去掉前缀,去掉后缀,剩下的就是库名!

动静态库的实现(打包动静态库供他人使用)_第17张图片

        我们发现,最后就生成了 a.out 这个可执行程序。

        所以,我们把我们的库给别人,给别人的是一组头文件,一组 lib ,这个头文件里只包含了函数的声明。

动静态库的实现(打包动静态库供他人使用)_第18张图片

        那么我们如果不想使用这么多选项也是可以的。

        我们之所以要使用这么多选项是因为我们自己实现的头文件和库没有在系统里,如果把我们的头文件和库拷贝到系统路径下,那么我们也就不需要带那些选项了:

动静态库的实现(打包动静态库供他人使用)_第19张图片

        我们先通过上面两个命令进行拷贝到系统路径下,拷贝完后再运行,发现不是说的头文件的问题了,还是没有定义 my_add ,这个是因为虽然不用指定库路径和头文件路径了,因为已经再系统路径下了,但是我们还需要指定库名字,也就是还要带 -l 。

        至于为什么C语言不需要,是因为我们编译的是C语言,编译器默认找的就是C库了,所以它知道C库是什么,就默认编译了,但是这里它不知道,所以就是要带 -l 。

动静态库的实现(打包动静态库供他人使用)_第20张图片

        所以我们刚才拷贝的过程其实就是安装库的过程

        比方说我们如果把静态库打包好了,如果想给别人的时候,然后再配上一个安装脚本,其实就是将文件拷贝到系统路径下就好了。


        当然,这样做还是很不好的,因为时间长了也不知道自己写的代码是什么样的,而且也不要污染别人的库,所以我们要将添加进去的删掉:

动静态库的实现(打包动静态库供他人使用)_第21张图片

        注意不要删错,其实我们删除的过程就是卸载库的过程


2 动态库

生成动态库

         生成动态库就不用 ar 了,直接就 gcc 或者 g++ 。

  • shared: 表示生成共享库格式
  • fPIC:产生位置无关码(position independent code)
  • 库名规则:libxxx.so

        这里给大家解释一下产生位置无关码

        静态库是直接将代码拷贝到可执行程序里,加载到内存里,然后就可以直接在进程中使用了。

        而动态库则需要你的程序和库的程序产生二次交互的过程。那么你的程序一定要能找到对应的库,但是库呢在内存中加载到哪里,映射到哪个区域都是不确定的,所以我们一定要让库的地址产生与位置无关的地址,这样就可以在任何地方映射还是可以产生关联。

打包给别人用

        接下来就来进行操作:

动静态库的实现(打包动静态库供他人使用)_第22张图片

        我们可以看到生成了对应的动态库。

        那么如果我们想打包动态库给别人用,也是需要给别人提供一组头文件和一个库文件,和静态库是一样的!

如何使用

        我们还是想静态库那样将头文件放到 include 里, libcal.so 放到 lib 里,然后再进行编译即可:

动静态库的实现(打包动静态库供他人使用)_第23张图片

动静态库的实现(打包动静态库供他人使用)_第24张图片

         我们可以发现,成功的形成了 mytest 这个可执行程序。

        但是动态库还是有点不一样,我们可以看到,如果我们直接 ./mytest 这个可执行程序,结果运行不起来。

        原因是:当加载这个动态库的时候,没有找到这个文件。

        因为我们前面的 -I -L -l 是在编译期间告诉编译器头文件和库在哪里以及是哪个库,但是当编译成功的时候可执行程序已经有了。

        当 ./mytest 的时候要将可执行变成进程,但是将可执行变成进程就一定要将当前代码加载到内存,但是加载到内存的时候,和它同步的关联的动态库找不到了,这次是操作系统找不到了。因为操作系统发现这个可执行依赖一个库,但是库不知道是哪个。

        当然也可以证明这个说法:

动静态库的实现(打包动静态库供他人使用)_第25张图片

        所以就是上面说的,虽然在编译的时候告诉编译器了,但是在程序运行的时候已经和编译器没有关系了,这个时候是系统找不到了。

这里有三种方法:

  1. 将这个 libcal.so 这个库拷贝到系统路径下(不推荐)
  2. 在系统中做配置(ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新)
  3. 导出一个环境变量 LD_LIBRARY_PATH ,它代表的是程序运行时,动态查找库时所要搜索的路径。

        接下来我就用方法三来操作:

        我们导入环境变量后就发现在 LD_LIBRARY_PATH 中就能找到动态库了!

动静态库的实现(打包动静态库供他人使用)_第26张图片

        此时的 ldd ,就可以发现 libcal.so 就能够找到了。

        找到之后就发现可以直接运行了!

3 总结

动静态库的实现(打包动静态库供他人使用)_第27张图片

        其实上面的方法二也是可以使用的。

        我们可以看到这个配置文件里其实就是一个路径。

接下来我就做一下配置:

动静态库的实现(打包动静态库供他人使用)_第28张图片

动静态库的实现(打包动静态库供他人使用)_第29张图片

        我们发现,最后也成功的运行了。

        但其实后面用的最多的还是将库拷贝到目录下。

4 使用外部库

        系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(ncurses库)

动静态库的实现(打包动静态库供他人使用)_第30张图片

        其实直接编译是可以跑的,没有出现要引入数学库这个概念,其原因是因为现在的头文件包含了编译器自动帮我们找到,所以没有问题。

        不过我们也可以自己找:

动静态库的实现(打包动静态库供他人使用)_第31张图片

        可以看到这个 m 出来了。然后我们 ./a.out 是可以运行的。

动静态库的实现(打包动静态库供他人使用)_第32张图片

         如上就是 动静态库 的所有知识,如果大家喜欢看此文章并且有收获,可以支持下 兔7 ,给 兔7 三连加关注,你的关注是对我最大的鼓励,也是我的创作动力~!

        再次感谢大家观看,感谢大家支持!

你可能感兴趣的:(零碎但重要着...,c++,linux,java,服务器)