💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
文章目录
- 前言
- 一、再谈静态库
- (1)站在库的制作者角度
- (2)站在库的使用者角度
- 二、再谈动态库
- (1)站在库的制作者角度
- (2)站在库的使用者角度
- 三、总结
前言
我们上篇讲解完文件系统和软硬件链接,今天我们在此来谈一个熟悉的话题–动静态库,我们之前也说过这个话题,但是讲解了一个网吧的例子,让大家看到我们的动静态库的区别,以及程序是怎么去进行动静态库的调用,就相当于我们还停留在了解,今天博主带大家写一个动静态库,然后写程序使用我们自己的动静态库去操作,这样大家就可以更加的理解动静态库了。话不多说,我们开始进入正文的讲解。
讲解思路:
- 站在库的制作者角度
- 站在库的使用者角度
- 动态库是怎么被加载的
一、再谈静态库
(1)站在库的制作者角度
我们在之前说过,我们的库是函数的定义,还需要一个.h文件,他是库的声明,因为我们已经简单的使用了多文件开发,显然两个源文件是编译不起来的,所以必须要有头文件。静态库打包后的后缀是.a
我们需要写一个源文件定义,还有一个头文件进行声明:
mymath.h
#include<stdio.h>
extern int myerror;
int add(int,int);
int sub(int,int);
int mul(int,int);
int div(int,int);
mymath.c
#include "mymath.h"
int myerror=0;
int add(int a,int b)
{
return a+b;
}
int sub(int a,int b)
{
return a-b;
}
int mul(int a,int b)
{
return a*b;
}
int div(int a,int b)
{
if(b==0)//出现错误讲错误码设置为1
{
myerror=1;
return -1;
}
return a+b;
}
main.c
#include "mymath.h"
int main()
{
printf("1+1=%d",add(1,1));//调用一个这个头文件里面的函数,就好比我们printf是stdio.h里面的函数。
return 0;
}
我们可以直接把我们的库文件(mymath.c)和头文件(mymath.h)直接给用户,让main.c去使用,这就和我们平时进行多文件开发一下的。但是我们今天使用的是静态库链接,我们是看不到库文件里面的具体实现的,所以我们需要将这个库文件进行打包就行了。
我们包含头文件的时候,将程序进行编译链接在形成可执行程序,所以我们自己的源文件(main.c)和库文件在链接的时候才会发生,此时前面两个文件都经过了编译,都变成了.o文件,只需要把.o文件打包给源文件(main.c)就行了。
makefile:
lib=libmymath.a //静态库文件一般都是.a的后缀
$(lib):mymath.o
ar -rc $@ $^ //通过这个命令打包成.a文件
mymath.o:mymath.c //先编译成.o文件
gcc -c $^
.PHONY:clean
clean:
rm -rf *.o *.a lib
.PHONY:output
output://创建一个目录将属于静态库的内容放在一个目录里面,别人就可以通过这个lib文件,就可以把所有的静态库相关的文件都下载下来
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
此时我们就自己制作了一个静态库。
(2)站在库的使用者角度
我们怎么去使用这个静态库呢??我们虽然生成了静态库,但是我们调用自己写的静态库,使用gcc编译的时候会报错
造成错误的原因是因为,我们的gcc是编译器,会去系统默认的路径下找对应的库文件和头文件,我们自己写的gcc并不知道,所以我们需要手动告诉gcc库文件和头文件在哪,所以需要使用下面的指令
gcc main.c -I ./lib/include -L ./lib/mymathlib
-I:是找到对应的头文件,-L:是找到对应的库文件
居然还不可以,原因是我们找到了对应的库文件所在的路径,但是必须要指定的链接的是哪个文件,即使里面只有一个静态库,也需要指定,所有需要在加一个选项
-lmymath
静态库的名称是去前缀和后缀,才是他真正的名字。选项和名字直接最好不要加空格。
再来看一个问题
我们查看可执行程序可以使用使用ldd/file查看是什么链接方式
我们没有看到mymath这个静态库啊,原因是我们默认的使用的是动态链接方式,但是你没有动态库,有静态库的文件,gcc不是阻挡编译,没有动态库,就相当于使用动态链接,用的是静态库文件,因为使用什么链接方式就展示什么类型的库文件,所以这里面看不到我们的静态类型的库文件,也希望大家遇到这个问题不要困惑。
我们发现这么一串编译太不好了,有两种方式解决这个问题
- 放到系统默认路径下
系统默认的头文件在 /usr/include
系统默认的库文件在/usr/lib64
我们可以把自己制作的头文件和库文件放在这些默认的搜索路径下:
我们发现-l还是要加上的。
- 软连接的方式
其实我们不推荐将自己制作的库放到默认的系统搜索路径下,万一对原路径下的库文件造成污染就麻烦了,所以我们不需要将自己的库文件放到默认搜索路径下,使用软连接
来看操作:
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/include /usr/include/myinc
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/mymathlib/libmymath.a /usr/lib64/libmymath.a
//大家一定要看好路径,如果按照我这个创建的路径,就可以直接使用这个指令了
我们看到使用软连接也可以将我们的问题解决掉,但是也需要加-l,大家应该会发现我们将路径复制到默认路径下,实际就是在安装的过程,我们把对应的文件下载下来,为什么要安装,就是放在默认的搜索路径下,方便去执行。
结论就是第三方库在使用gcc的时候必须加上-l选项。第一方和第二方可以理解为系统和语言
静态库我们就讲解完毕了,我们来看动态库
二、再谈动态库
按照老思路去讲解
(1)站在库的制作者角度
博主将多写几个库文件,一起打包,让大家也看看。我峨嵋你动态库打包后的默认后缀是.so
还是需要写一个头文件和两个库文件
myprint.h
#pragma once
#include<stdio.h>
void print();
myprint.c
#include "myprint.h"
void print()
{
printf("hello dy\n");
printf("hello dy\n");
printf("hello dy\n");
printf("hello dy\n");
}
mylog.h
#pragma once
#include<stdio.h>
void Log(const char*);
mylog.c
#include "mylog.h"
void Log(const char* str)
{
printf("warning:%s\n",str);
}
我们已经有了两个库文件,那我们应该怎么什么对应的动态库文件呢?首先动态库也是和静态库一样在链接的时候才会和原文件产生关系,所以我们将我们自己写的两个库文件需要先编译生成对应的.o文件,在进行打包成动态库文件。
肯定是有点区别的
gcc -fPIC -c mylog.c myprint.c
生成.o文件,难道是和静态库一样使用ar就可以了吗,答案肯定不是的,我们可以直接只有下面的指令去操作
gcc -shared -o libmymethod.so mylog.o myprint.o
重点:
- 我们发现动态库可以直接使用gcc进行打包,而静态库不行,我们可以理解为动态库是gcc的亲儿子,而静态库是干儿子,我们的gcc已经内置编码可以直接打包成动态库,这也可以简单理解为我们的链接方式默认就是动态库,但是用户最大,使用- static的时候还是静态链接,使用静态库,只有静态库的时候也是使用静态库。
- 我们发现.so是可执行文件,而.a文件是不可执行文件,这是因为,我们动态库是所有程序共享的,但我们自己写的源文件编译好,准备去执行的时候,还需要将动态库文件一起加载到内存进行执行,而静态库在形成可执行程序之前,就讲自己对应的代码复制到源文件里面了,执行的时候,那源文件加载内存就行了,和静态库文件就没有关系了
- 我们的打包成动态库文件去掉- shared,是不是就是我们一开始学的多个文件生成可执行程序,可我不想生成可执行文件,所有加了一个- shared,说这个的目的就是让大家更好的理解。
上面先不给大家演示效果,我先将下面的操作讲解完毕,在演示,我想将动静态库一起通过一个makefile同时编译,相信在进程程序替换的时候,已经说过怎么同时生成量恶搞可执行程序,默认值生成先出现的那一个,所以我们要设置为目标,接下来我们来看具体操作
makefile:
static-lib=libmymath.a
dy-lib=libmymethod.so
.PHONY:all
all:$(static-lib) $(dy-lib)
$(static-lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
$(dy-lib):mylog.o myprint.o
gcc -shared -o $@ $^
mylog.o:mylog.c
gcc -fPIC -c $^
myprint.o:myprint.c
gcc -fPIC -c $^
.PHONY:clean
clean:
rm -rf *.o *.a *.so mylib
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
我们使用这个makefile先来测试一下之前静态库的代码,看看我们写的对不对,博主就直接在gcc下指定具体路径了去测试了。
重点:不管是那种类型的库文件,前缀都一定要是lib,不让就会出现问题
结果也同时可以使用自己制作的静态库,但是这个结果我们发现不对,错误码应该是1,这个和我们测试没有关系,就是printf自己的特性,他是从右往左进行赋值的,在没有调用函数的时候,错误码就已经被赋值了,提前算好就行了。
这样的问题就解决了。
(2)站在库的使用者角度
我们通过上面方法,制作出属于我们自己的动态库,现在要来看看怎么去使用它。
我们使用make一键生成动态库,看效果
mylib里面就是包含我们自己制作的动静库文件,大家可以选择将两者分开,这里博主就没有分开了,因为不影响演示,接下来我们就来测试。
我们来写一个程序去使用我们的动态库
mytest.c
#include"mylog.h"
#include"myprint.h"
int main()
{
print();
Log("hello log function");
return 0;
}
gcc mytest -o mytest
显然我们直接编译成可执行程序是不行的。这个问题不用多说,找不到路径,我们使用下面的命令,指定路径试试看
gcc mytest.c -I ../mylib/include -L ../mylib/lib -lmymethod -o mytest
我们看到可以形成可执行文件了,但是我们执行的时候去还是报错,这是为什么??
我都已经把指定路径告诉你了,让你去这个路径下面去查找,你怎么还是不行呢??
首先我们要搞懂一个问题,这个告诉你,你指的是谁(gcc)gcc只管编译,形成可执行程序,大家还记得我最上面说的重点吗,但可执行程序开始运行的时候,动态库也是要加载到内存的,刚才指定路径值告诉了gcc,没有告诉我系统你的动态库在哪里,所以才会导致我们可以形成可知心个程序,去执行不了,既然找到问题的答案了,那我们就解决这个问题,让系统知道就好了。
为什么静态库没有这个问题呢,原因就是我们在使用静态库链接的时候,代码是拷贝到可执行代码中的,形成可执行程序之后,加载到内存就和静态库没有关系了。只要编译时能找到就行了。
解决问题:
我们有四种结果办法,博主都会一一介绍的。
- 将动态库放到默认系统默认路径下
sudo cp ./mylib/include/myprint.h /usr/include/myprint.h
sudo cp ./mylib/include/mylog.h /usr/include/mylog.h
sudo cp ./mylib/lib/libmymethod.so /usr/lib64/libmymethod.so
- 使用软链接的方式
sudo ln -s /home/xdh/gitcode/linux/动静态库/mylib/lib/libmymethod.so /usr/lib64/libmymethod.so
我直接将动态库和系统默认路径进行软链接,我程序都没有重新生成,结果就可以直接跑了。大家应该可以理解这个道理。这也侧面体现出来软链接的作用以及重要性
- 使用环境变量去操作
我们为什么可以这么自然而然的使用我们的系统默认路径,原因是我们有环境变量,那我们把我们对应的动态库文件放到环境变量里面就可以了
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xdh/gitcode/linux/动静态库/mylib/lib
所以环境变量还是非常重要的
- 添加到系统的配置文件里面
第三种在下次打开shell的时候,就又不行了,所以放到配置文件里面就可以了
我们自己添加一个配置文件,把我们对应的动态库文件的路径放进去
我们在使用配置文件的时候一定要注意权限,还有这个配置文件要更新一下才可以使用,下次在重启的时候,还是可以对应的动态库的。
解决疑惑:
- 我们发现我们把对应的库文件放系统知道就行了,为什么头文件不需要让系统知道呢?原因是我们在编译的时候就标识过了我们自己写的可执行程序是在.o文件的时候进行链接的,而头文件在编译的时候就展开到库文件里面了然后形成了.o文件,所以不需要.
- 那这么多方法我们使用哪一个呢??答案是最多还是第一种,虽然第一种不建议,为什么不建议,我在一开始就标注了,我们自己写的不建议,但是我们以后几乎使用的都是别人成熟的库,所以不用担心对自己系统有啥影响,所以可以直接放到系统默认的搜索路径下,所有就有了安装。
三、总结
对于动静态库大家应该都不陌生了,我们以后去公司有很大可能使用公司自己写的库去开发,这时候就需要我们自己去将公司的库配置到自己的电脑,这也是你刚开始入职的时候,要频繁的安装软甲的原因,所以大家了解原理了,到时候也不用担心了,也希望读者可以学到更多知识,我们下篇再见。
😃