今天主要介绍动静态库的制作和使用,对于库来说,大家肯定都不陌生,因为我们写代码基本都要使用库。今天我们来进一步学习库的知识吧~
目录
库的介绍
库的分类
库文件的命名
库的真实名字
查看可执行程序依赖的库
思考
静态库
制作静态库
使用静态库
总结
动态库
制作动态库
使用动态库
运行方法一
运行方法二
运行方法三
总结
补充
一般库分为两种:动态库、静态库
在Linux中
如果是动态库:库文件是以.so作为后缀的!
如果是静态库:库文件是以.a作为后缀的!
在windows中
如果是动态库:库文件是以.dll作为后缀的!
如果是静态库:库文件是以.lib作为后缀的!
今天我们主要讨论Linux下的动静态库的制作和使用。
库文件的命名:libXXX.so 或者 libYYYY.a-..
其实也就是lib加库名字,再加后缀,后缀后面可能还跟一些东西。
比如我们看一下系统库文件的命名:
[cyq@VM-0-7-centos test]$ ls /lib64/libc.so.6 -l
[cyq@VM-0-7-centos test]$ ls /lib64/libc-2.17.so -l
我们发现库文件名:libc-2.17.so
去掉lib前缀,去掉.a- 或 .so-(包含后缀)之后的部分,剩下的就是库名称。
举个栗子:
libc-2.17.so:去掉前缀lib,去掉后缀.so,剩下的c-2.17就是库的真实名字。
[cyq@VM-0-7-centos 库]$ ldd test
我们来查看一个可执行程序依赖的动态库:
我们可以发现这个这个可执行程序依赖的是动态库(直接去编译,linux默认是动态链接)。
如果我们静态链接方式去编译再来查看这个可执行程序依赖的库:
[cyq@VM-0-7-centos 库]$ gcc -o test test.c -static
[cyq@VM-0-7-centos 库]$ file test
接下来才是今天的主要内容~
库本身就是二进制文件,我们如何得知,一个库给我们提供了什么方法?
一套完整的库:库文件本身、头文件、说明文档
头文件是文本形式的,会说明库中暴露出来的方法的基本使用!
我们在C/C++中,为什么有时候写代码的时候,.h里面放上声明,.c/.cpp里面放入函数的定义?为什么要这么实现?
因为我们要制作库!
函数定义和函数实现进行分离,一是方便维护。还有一个原因是为了私密。因为有时候别人想使用我们的代码,但是我们又不能把源码发给别人,所以我们可以制作成库(库是二进制文件),然后给别人使用,这样就保证了私密性。
我们在此之前,先创建add.h、add.c、sub.h、sub.c四个文件,.h文件里面写函数声明,.c文件里面写函数定义。
如下:
add.h
#pragma once
#include
int my_add(int x, int y);
add.c
#include "add.h"
int my_add(int x, int y)
{
return x + y;
}
sub.h
#pragma once
#include
int my_sub(int x, int y);
sub.c
#include "sub.h"
int my_sub(int x, int y)
{
return x - y;
}
Makefile
libmymath.a:add.o sub.o
ar -rc $@ $^
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm -f libmymath.a *.o
.PHONY:output
output:
mkdir lib
cp libmymath.a lib
cp *.h lib
实际上Makefile动作:
第一步就是把对应的.c文件生成对应的.o文件。
注意:我们不用写main函数,因为在链接之前每个文件预处理、编译、汇编(生成可重定向位的二进制文件)动作是独立的,只有到链接的时候才会相互交叉。补充:在vs环境下运行没有写main实际上报错就是链接错误~
第二步就是把生成的.o文件打包成库文件。
第三步,把库文件和.h头文件进一步打包,放在一起,就可以发布了。
第一步&第二步:
libmymath.a:add.o sub.o //我们生成libmymath.a库文件时,依赖目标是add.o和sub.o,如果在指定路径下(默认是当前路径)没有找到,就先会先找到add.o的生成方法。
ar -rc $@ $^ //ar是gnu归档工具,rc表示(replace and create),意思就是,没有就创建,有的话就替代。这是打包静态库的工具。
%.o:%.c //当前路径下所有要生成的.o文件都要依赖%.c文件
gcc -c $< //把每个.c文件编译生成对应的.o文件。
第三步:
.PHONY:output //伪目标,总是可以被执行
output:
mkdir lib //创建一个目录
cp libmymath.a lib //把打包的静态库文件拷贝到该目录下
cp *.h lib //把当前目录下的所有.h文件拷贝到该目录下
最后:
.PHONY:clean
clean:
rm -f libmymath.a *.o //删除库文件和当前目录下的所有.o文件
接下来我们就来看一下这个过程:
这样,我们就生成好了我们的静态库lib,这样我们就可以发给别人了。
我们假设把制作好的lib库发送给friend_lib,然后friend_lib使用我们的库写代码时:
我们看到,在friend_lib目录下有我们给他的lib库,同时在friend_lib目录下写了一个test.c代码,使用了lib库。
test.c
#include "./lib/add.h"
#include "./lib/sub.h"
int main()
{
int x = 10;
int y = 20;
int r1 = my_add(x, y);
int r2 = my_sub(x, y);
printf("+: %d\n",r1);
printf("-: %d\n",r2);
return 0;
}
运行指令:
[cyq@VM-0-7-centos friend_lib]$ gcc -o test test.c -I./lib -L./lib -lmymath
[cyq@VM-0-7-centos friend_lib]$ gcc -o test test.c -I./lib -L./lib -lmymath
-I./lib:告诉编译器,编译时到lib目录下找的头文件。(如果头文件指明了路径就可以不写)-L./lib:指定库路径。
-lmymath:告诉编译器,在指定的路径下,找哪个库,-l后面加库的真实名字。
实验如下:
1、我们把自己写好的.c文件编译生成对应的.o文件。
2、使用归档工具ar -rc把生成的.o文件打包成静态库。
3、然后把打包的库和自己写的.h头文件放在一个目录下就可以给别人了。
我们给别人交付的其实就是一个库文件+一套头文件。
我们制作动态库方法和静态库类似,只是Makefile里面的细节不一样:
add.h
#pragma once
#include
int my_add(int x, int y);
sub.h
#pragma once
#include
int my_sub(int x, int y);
add.c
#include "add.h"
int my_add(int x, int y)
{
return x + y;
}
sub.c
#include "sub.h"
int my_sub(int x, int y)
{
return x - y;
}
Makefile
libmymath.so:add.o sub.o
gcc -shared -o $@ $^
%.o:%.c
gcc -fPIC -c $<
.PHONY:clean
clean:
rm -f libmymath.so *.o
.PHONY:output
output:
mkdir so
cp libmymath.so so
cp *.h so
第一步就是把所有的.c文件生成对应的.o文件,同时借助-fPIC,生成位置无关码。
第二步将生成的.o文件打包成动态库,同时借助-shared,表示生成共享库格式。
第三步把生成的动态库和.h头文件放到一个目录下,就可以交付给别人了。
第一步&第二步:
libmymath.so:add.o sub.o //生成目标文件libmymath.so依赖add.o和sub.o,如果依赖的文件找不要的话,就向下执行依赖文件的生成方法。
gcc -shared -o $@ $^ //将生成的.o文件打包成动态库,-shared表示共享库格式
%.o:%.c //所有将生成的.o文件依赖对应的.c文件
gcc -fPIC -c $< //生成方法,-c就是生成.o文件,$<对应上面的%.o:%.c。
第三步:
.PHONY:output
output:
mkdir so //创建so目录
cp libmymath.so so //把libmymath.so动态库文件拷贝到so目录中。
cp *.h so //把当前目录下的所有.h文件拷贝到so目录中
实验效果:
这样生成的动态库交给别人了~
如果我们把上面打包好的动态库,给friend_dll。然后再friend里面写一个调用so动态库的函数,关系如下:
test.c
#include "./so/add.h"
#include "./so/sub.h"
int main()
{
int x = 10;
int y = 20;
int r1 = my_add(x, y);
int r2 = my_sub(x, y);
printf("+: %d\n",r1);
printf("-: %d\n",r2);
return 0;
}
但可惜,我们如果直接编译,是没问题,但是我们运行就不对了:
那我可能就好奇了,我不是指明头文件路径了吗?我不是指明库路径了吗?我们也指明了是哪个库啊?为什么还是不行?
实际上我们那是在编译的时候指明了,等到运行可执行文件的时候还是要去库,这时候编译器就不知道了,因为没有人告诉它,这时候我们需要去再多做一步,在运行时"告诉编译器"库在哪。
静态库不存在这样的问题,因为他在编译的时候就已经把库拷贝进可执行程序里面了,运行时立马就能找到对应的库。
我们先用在上面讲过的ldd命令看看:
这时候我们就发现,我们的代码在运行时找不到我们的库!
我们曾经使用-I和-L以及-l选项是在编译期间告诉编译器到哪里去找,这时候已经生成了.o目标文件了,但是在运行时,却没有告诉编译器去哪里找我们要使用的库,换句话说,也就是环境变量里没有对应的搜索路径!
注意:这里说的环境变量不是PATH,PATH是保存搜索指令的路径的环境变量。这里的环境变量保存的是库文件的搜索路径。
之前的库,在系统默认路径下:/lib64、/usr/lib、/usr/include等,编译器是能够识别这些路径的。
在这里我们就知道了,安装软件其实就是把对应的库和头文件下载然后拷贝到默认目录下。
把我们的库路径拷贝到LD_LIBRARY_PATH环境路径下。
LD_LIBRARY_PATH
这是可以用来存放搜索库文件位置的路径的环境变量。
使用方法和PATH一致。
[cyq@VM-0-7-centos friend_dll]$ pwd
/home/cyq/practice/test/库/friend_dll
[cyq@VM-0-7-centos friend_dll]$ echo $LD_LIBRARY_PATH
:/home/cyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
[cyq@VM-0-7-centos friend_dll]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cyq/practice/test/库/friend_dll/so
[cyq@VM-0-7-centos friend_dll]$ echo $LD_LIBRARY_PATH
:/home/cyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/cyq/practice/test/库/friend_dll/so
注意:路径精确到这个so动态库!!
这时候我们编译再ldd查看生成的可执行程序:
[cyq@VM-0-7-centos friend_dll]$ gcc -o test test.c -I./so -L./so -lmymath
我们发现test.c就可以找到对应的so库了:
这时候我们就可以运行了:
可以修改系统默认的库路径搜索路径。下面两个也是博主曾经介绍过的~
vim ~/.bash_profile
vim ~/.bashrc
这样做可以永久生效,但是严重不推荐,这样做会污染人家的库文件池。
配置全局的搜索路径。
需要在root用户下进行配置。
我们首先进入到/etc/ld.so.conf.d/目录下:
ld.so.conf.d:系统在搜索动态库时搜索路径。
[root@VM-0-7-centos ld.so.conf.d]# cd /etc/ld.so.conf.d/
我们看到当前目录下有很多.conf的文件,我们在这里也可以创建自己的文件,然后把对应的动态库路径保存进去:
[root@VM-0-7-centos ld.so.conf.d]# vim cyq.conf
然后使用ldconfig让路径缓存更新一下:
[root@VM-0-7-centos ld.so.conf.d]# ldconfig
接着我们就可以在别的终端运行我们的代码了:
如何制作库?
1、所有的源代码都要被编译成.o(可重定向目标文件)
2、将.o打包,使用ar -rc或者gcc -shared -fPIC进行打包
3、交付include和.a或者.so文件
如何使用?
具体看上面介绍。
编译器动态链接优先、静态链接优先?
如果只提供静态库,我们只能将我们的库,静态链接到我们的程序中。
如果只提供动态库,我们只能将我们的库,动态链接到我们的程序中。
如果想既能动态链接,又能静态链接,这时候就应该提供两种库文件!
gcc、g++默认的是动态链接优先、release优先。
如果我们想静态链接就首先要安装静态库,因为,一般服务器可能没有内置语言的静态库,只有动态库。
静态库安装好之后,在编译的时候,在后面加 -static选项就可以静态链接了。
看到这里,给博主点个赞吧~