【动静态库的制作和使用】

​ 库(Library)说白了就是一段编译好的二进制代码(.o二进制文件),加上头文件就可以供别人使用。
​ 什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件。另外一种情况是,对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的二进制了,编译的​ 时候只需要 Link 一下,不会浪费编译时间。

​ 上面提到库在使用的时候需要 Link,Link 的方式有两种,静态链接和动态链接,于是便产生了静态库和动态库

一、静态库

1.1 静态库概念

​ 静态库即静态链接库(Windows 下的 .libLinux 和 Mac 下的 .a)。之所以叫做静态,是因为静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里 就不会再改变了。静态库的好处很明显,编译完成之后,库文件实际上就没有作用了。目标程序没有外部依赖,直接就可以运行。当然其缺点也很明显,就是会使用目标程序的体积增大。

1.2 静态库的制作

mymath.h文件

#pragma once
#include<stdio.h>
#include<assert.h>
extern int AddtoVal(int strat, int end);

mymath.c文件

#include"mymath.h"
int AddtoVal(int strat, int end)
{
    assert(end >= strat);
    int result = 0;
    for(int i = strat; i <= end; ++i)
    {
        result += i;
    }
    
    return result;
}

myprint.h文件

#pragma once
#include<stdio.h>
#include<time.h>
extern void Print(const char* msg);

myprint.c文件

#include"myprint.h"
void Print(const cahr *msg)
{
    printf("%s, %lld",msg, (long long)time(NULL))}

Makefile文件(重点)

libmylib.a:mymath_s.o myprint_s.o
	ar -rc libmylib.a mymath_s.o myprint_s.o
mymath_s.o:mymath.c
	gcc -c mymath.c -o mymath_s.o -std=c99
myprint_s.o:myprint.c
	gcc -c myprint.c -o myprint_s.o -std=c99
.PHONY:S_lib
S_lib:
	mkdir -p lib-static/lib
	mkdir -p lib-static/include
	cp *.a lib-static/lib
	cp *.h lib-static/include
.PHONY:clean
clean:
	rm -rf *.a *.o lib-static

解读:

ar是gnu归档工具,它就是用来把*.o文件打包为库文件( *.a 文件),-rc(rc为replace and create)

我们在使用C语言的库函数时,除了要用到相应的库文件,还用包含库函数的头文件,所以我们要把库文件和头文件指定的放在相应的目录当中。如上面的cp *.a lib-static/lib和*cp .h lib-static/include

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLSNsjuP-1667358511606)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102104401408.png)]

总结:

静态库的制作就是把函数的声明(*.h)与定义 ( *.c 或者 *.cpp)分离,然后把 *.c或 *.cpp编译为 *.o文件,紧接着把 *.o 打包为 *.a文件,最后分别把 *.a文件 与 *.h文件指定放在相应的目录当中即可。

1.3 静态库的使用

  1. 将自己的头文件和库文件拷贝到系统的头文件路径(/usr/include/)和库文件路径(/usr/lib64/)下!!!(相当于安装库),然后指定要链接的库文件

    sudo cp ./*.h /usr/include/
    sudo cp ./*.a /usr/lib64/
        
    gcc mytest.c -o mytest -l mymath
    //gcc mytest.c -o mytest -lmymath 也可以
    

    其中的-l选项就是指定要链接的库文件,后面跟着的是库文件名。(去掉前缀lib后缀.a就是库文件名)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PiNHbs7D-1667358511607)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101170212094.png)]

    (不推荐)

  2. 在编译的时候告诉编译器头文件和库文件的查找路径,并且指定要链接的库文件

    gcc mytest.c -o mytest -I ./lib-static/include/ -L ./lib-static/lib/ -l mylib
    //gcc mytest.c -o mytest -I./lib-static/include/ -L./lib-static/lib/ -lmylib 也可以
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S59u5xFM-1667358511608)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101170911077.png)]

二、动态库

2.1 动态库的概念

动态库

​ 动态库即动态链接库(Windows 下的 .dllLinux 下的 .soMac 下的 .dylib)。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行(Linux 下喜闻乐见的 lib not found 错误)。

2.2 动态库的制作

mymath.h文件

#pragma once
#include<stdio.h>
#include<assert.h>
extern int AddtoVal(int strat, int end);

mymath.c文件

#include"mymath.h"
int AddtoVal(int strat, int end)
{
    assert(end >= strat);
    int result = 0;
    for(int i = strat; i <= end; ++i)
    {
        result += i;
    }
    
    return result;
}

myprint.h文件

#pragma once
#include<stdio.h>
#include<time.h>
extern void Print(const char* msg);

myprint.c文件

#include"myprint.h"
void Print(const cahr *msg)
{
    printf("%s, %lld",msg, (long long)time(NULL))}

Makefile文件

libmylib.so:mymath_d.o myprint_d.o
	gcc shared mymath_d.o myprint_d.o -o libmylib.so
mymath_s.o:mymath.c
	gcc -fPIC -c mymath.c -o mymath_d.o
myprint_d.o:myprint.c
	gcc -fPIC -c myprint.c -o myprint_d.o
.PHONY:D_lib
D_lib:
	mikdir -p lib-dynamic/lib
	mikdir -p lib-dynamic/include
	cp ./*.so ./lib-dynamic/lib
	cp ./*.h ./lib-dynamic/include
.PHONY:clean
	rm -rf *.o *.so lib-dynamic

解读:

shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)

我们在使用C语言的库函数时,除了要用到相应的库文件,还用包含库函数的头文件,所以我们要把库文件和头文件指定的放在相应的目录当中。如上面的cp *.so lib-dynamic/lib和*cp .h lib-dynamic/include

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4OranSuC-1667358511608)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101194610878.png)]

总结:

**动态库的制作就是把函数的声明(*.h)与定义 ( *.c 或者 *.cpp)分离,然后把 *.c或 *.cpp编译为 *.o文件(需要使用-fPIC),紧接着把 *.o 打包为 *.a文件(使用的是gcc -shared),最后分别把 .a文件 与 .o文件指定放在相应的目录当中即可

2.3 动态库的使用

  1. 将自己的头文件和库文件拷贝到系统的头文件路径(/usr/include/)和库文件路径(/usr/lib64/)下!!!(相当于安装库),然后指定要链接的库文件

    sudo cp ./*.h /usr/include/
    sudo cp ./*.so /usr/lib64/
        
    gcc mytest.c -o mytest -l mymath
    //gcc mytest.c -o mytest -lmymath 也可以
    

    其中的-l选项就是指定要链接的库文件,后面跟着的是库文件名。(去掉前缀lib后缀.so就是库文件名)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biKU6aTW-1667358511609)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101195451829.png)]

    (不推荐)

  2. 在编译的时候告诉编译器头文件和库文件的查找路径,并且指定要链接的库文件

    gcc mytest.c -o mytest -I ./lib-dynamic/include/ -L ./lib-dynamic/lib/ -l mylib
    //gcc mytest.c -o mytest -I./lib-dynamic/include/ -L./lib-dynamic/lib/ -lmylib 也可以
    
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyLoXgWr-1667358511610)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101201009623.png)]

报错的大概意思是说mytest可执行文件在执行的时候找不到它所依赖的动态库,又有人说不是在编译的时候指明了动态库的路径了吗???怎么会找不到呢???因为在编译的时候,他只是告诉了gcc动态库的路径,运行的时候并没有告诉进程动态库的路径,所以当进程需要使用动态库的代码时,会由于找不到动态库而崩掉。

那么又有人会有问题,为什么静态库不会存在进程找不到静态库呢???因为它在形成可执行程序后,已经把它所需要的代码加载到了可执行程序中,它在执行的时候不会依赖我们写的库。

2.3.1 那么如何让进程知道动态库的路径呢???

  1. 将动态库拷贝到系统路径 **/usr/lib64/**下(不推荐)

  2. 通过导入环境变量的方式 — LD_LIBRARY_PATH

    程序在运行的时候通过环境变量来查找自己所需要的动态库路径

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oL03iM48-1667358511610)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221101211719697.png)]

​ 把动态库的绝对路径添加到环境变量LD_LIBRARY_PATH中,在执行mytest就可以运行了。但是这个方法有一个问题,就是在下次登陆的时候,刚刚添加的环境变量就没有了。还得重新导入环境变量。

  1. 通过配置文件 /etc/ld.so.conf.d/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dvNWrsKj-1667358511611)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102085632349.png)]

在进程运行的时候,它不仅会去系统的库路径下去找自己需要依赖的动态库文件,还会去 /etc/ld.so.conf.d/ 路径下依次扫描该路径下的文件,通过这些文件里的信息(一般为绝对路径)找到依赖动态库文件。

sudo touch /etc/ld.so.conf.d/my-104.conf

然后在 my-104.conf中写入依赖动态库的绝对路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7R2KjMEd-1667358511612)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102092941818.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x99asAqb-1667358511612)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102093144916.png)]

我们发现添加了配置文件以后,进程还是找不到自己需要的动态库,这是因为配置文件还没有生效,需要通过 ldconfig指令:让配置文件生效。

这样我们的进程就可以运行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6Gm4u5Y-1667358511613)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102093830860.png)]

  1. 在系统路径下建立软连接

    sudo ln -s /home/lk/Linux/动静态库/uslib/lib-dynamic/lib/libmylib.so /usr/lib64/libmylib.so
    

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vItEM0op-1667358511614)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102101921437.png)]

unlink取消软连接:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3OfhVn0Y-1667358511614)(C:\Users\13916\AppData\Roaming\Typora\typora-user-images\image-20221102102303482.png)]

三、动静态库的对比(优缺点)

1、静态库
优点:

①静态库被打包到应用程序中加载速度快
②发布程序无需提供静态库,移植方便

缺点:

①相同的库文件数据可能在内存中被加载多份,消耗系统资源,浪费内存
②库文件更新需要重新编译项目文件,生成新的可执行程序,浪费时间。

2、动态库
优点:

①可实现不同进程间的资源共享
②动态库升级简单,只需要替换库文件,无需重新编译应用程序
③可以控制何时加载动态库,不调用库函数动态库不会被加载

缺点:

①加载速度比静态库慢
②发布程序需要提供依赖的动态库

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桑榆非晚ᴷ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值