文章目录
- 动态库和静态库
- 1.静态库和动态库的介绍
- 2.静态库的打包和使用
- 2.1生成静态库
- 2.2使用静态库的三种方式
- 2.2.1gcc编译
- 2.2.2拷贝到系统默认的路径
- 2.2.3建立软连接
- 3.动态库的打包和使用
- 3.1生成动态库
- 3.2使用动态库
- 3.3解决加载不到动态库的方法
动态库和静态库
1.静态库和动态库的介绍
静态库和动态库是两种不同的程序库,它们在编译和链接阶段有不同的应用方式和特点。
静态库(Static Library):
静态库是在编译时被全部链接到目标程序中,一同生成可执行文件,所以生成的可执行文件较大,但运行时不需要链接其他库。静态库的后缀通常为.a或.lib。在程序发布时,通常只需要提供静态库和可执行文件,而不需要源代码。
动态库(Dynamic Library):
动态库在程序运行时才被加载和链接,所以多个程序可以共享相同的动态库代码,从而节省内存。动态库的后缀通常为.so(Linux)或.dll(Windows)。动态库的代码需要满足能够被加载到不同进程的不同地址,因此需要进行特别的编译处理。动态库在程序运行时由操作系统负责加载和链接,因此如果程序需要更新某个模块,只需要更新相应的动态库即可,而不需要重新编译整个程序。
静态库和动态库的主要区别在于链接时间和使用方式。静态库在编译时链接到目标程序中,而动态库在程序运行时才被加载和链接。此外,静态库和动态库的打包和分发方式也不同,静态库需要和可执行文件一起发布,而动态库只需要提供动态库文件即可。
静态库(后缀为 .a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
动态库(后缀为 .so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
2.静态库的打包和使用
静态库的打包原理基于链接器的工作原理,将各个目标文件中的代码和符号合并到一起,以便在其他项目中进行使用。先将所有目标文件(.c文件)编译为(.o文件),然后把目标文件(.o文件)打包在一起,生成一个或多个静态库文件。 这个过程通常使用ar命令
(在Linux和类Unix系统中)或lib命令
(在Windows系统中)完成。
打包过程中,需要将所有的目标文件组织到一个归档文件中,形成静态库。这个归档文件是一个持久的数据库,包含了目标文件的名称、创建时间和修改时间等信息。在链接阶段,链接器会从静态库中提取需要的目标文件,将其链接到最终的可执行文件中。
2.1生成静态库
我们假设使用mymath.c和mymath.h模拟为静态库。下面是制作和打包静态库的过程。
假设我们的main.c想要编译外部的两个源文件和头文件(mymath.c和mymath.h)。
mymath.h
#pragma once#include <stdio.h>extern int myerrno;int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
mymath.c
#include "mymath.h"int myerrno = 0;int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{if(y == 0){myerrno = 1;return -1;}return x / y;
}
main.c
#include "mymath.h"
//#include "myinc/mymath.h"int main()
{extern int myerrno;//printf("1+1=%d\n", add(1,1));int n=div(10,0);//C语言实例化是从右向左,所以myerrno输出的是0printf("10/0=%d, errno=%d\n", n, myerrno);//gcc默认的链接方式是动态链接//没有动态库就默认使用静态库链接return 0;
}
经过下面的make操作,生成.a静态库文件。
static-lib=libmymath.a//将我们静态库的名称命名为static-lib$(static-lib):mymath.o//如何使用mymath.o构建static-libar -rc $@ $^//构建静态库 目标文件 依赖文件
mymath.o:mymath.c//如何使用mymath.c构建mymath.ogcc -c $^//编译到.o文件.PHONY:clean//伪目标
clean: //清除所有的.o .a 和static-lib文件rm -rf *.o *.a static-lib.PHONY:output//打包文件
output:mkdir -p static-lib/include//创建目录includemkdir -p static-lib/my-static-lib//创建目录my-static-libcp *.h static-lib/include//拷贝所有.h文件到includecp *.a static-lib/my-static-lib//拷贝所有.a文件到my-static-lib
此时的文件为,我们进行make操作:
我们可以看到生成了mymath.o文件和我们需要的打包好的静态库libmymath.a文件。此时我们就需要使用这个静态库libmymath.a了。
顺带着打包一下,将.h和.a文件放入一个static-lib文件中。
2.2使用静态库的三种方式
2.2.1gcc编译
当前的文件下输入 gcc main.c -I ./头文件的路径 -L ./库文件的路径 -l 链接库的名称
即可生成我们的可编译程序。
注意上面的代码所含的内容缺一不可:
缺少头文件和库文件,链接出错。
缺少库文件,链接出错。
找不到链接库的名称,链接出错。
虽然链接出错,但是仍然可以汇编为.o文件。
2.2.2拷贝到系统默认的路径
拷贝文件到系统路径同样可以实现静态库的使用:
sudo cp static-lib/include/mymath.h /usr/include/
sudo cp static-lib/my-static-lib/libmymath.a /lib64/libmymath.a
gcc 无法直接编译我们的main.c文件还是需要我们告诉编译器其中的静态库的名字才可以,-l mymath
。
但是一般不推荐,这样会对我们系统的路径造成污染,删除:
2.2.3建立软连接
软链接应用广泛,可以快速找到.h和.c文件。
使用时,main函数的头文件要修改为文件的路径。
//#include "mymath.h"
#include "myinc/mymath.h"//使用软链接时编译
软链接includesudo ln -s /home/wu1/study_liunx/2024_1_23动静态库测试/static-lib/include /usr/include/myinc
软链接.a静态库sudo ln -s /home/wu1/study_liunx/2024_1_23动静态库测试/static-lib/my-static-lib/libmymath.a /lib64/libmymath.a
解除链接:sudo unlink/usr/include/myinc
sudo unlink/lib64/libmymath.a
3.动态库的打包和使用
动态库的打包原理是将多个相对独立的部分按照模块化的方式拆分成不同的文件,并在程序运行时才将这些模块链接在一起形成一个完整的程序。与静态库不同,动态库不会将所有代码和数据都包含在最终的可执行文件中,而是在程序运行时由操作系统动态加载到内存中。
打包动态库时,需要将各个目标文件(.o文件)编译为动态库文件(.so文件),以便在程序运行时被加载和链接。 这个过程通常使用gcc命令,并指定-fPIC
和-shared
选项,以便生成位置无关代码和共享库。
3.1生成动态库
和上面生成的.a类似,动态库是后缀为.so的文件,我们下面使用mylog.h mylog.c myprint.h myprint.c
进行动态库的打包实现。
main.c
#include "mylog.h"
#include "myprint.h"int main()
{Print();Log("这是一个动态库打包的测试");return 0;
}
mylog.h
#pragma once#include <stdio.h>void Log(const char*);
mylog.c
#include "mylog.h"void Log(const char*info)
{printf("Warning: %s\n", info);
}
myprint.h
#pragma once#include <stdio.h>void Print();
myprint.c
#include "myprint.h"void Print()
{printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");
}
经过下面的make操作,生成.so静态库文件。
dy-lib=libmymethod.so//将我们动态库的名称命名为dy-lib.PHONY:all//伪目标为dy-lib文件
all: $(dy-lib)$(dy-lib):mylog.o myprint.o//将mylog.o和myprint.o文件打包为动态库文件gcc -shared -o $@ $^//形成共享库(可执行程序加载内存)mylog.o:mylog.c//将.c文件编译为.o文件gcc -fPIC -c $^//-fPIC产生与位置无关码
myprint.o:myprint.cgcc -fPIC -c $^.PHONY:clean//伪目标删除操作
clean:rm -rf *.o *.so dy-lib.PHONY:output//打包动态库
output:mkdir -p dy-lib/includemkdir -p dy-lib/my-dy-libcp *.h dy-lib/includecp *.so dy-lib/my-dy-lib
此时的文件为,我们进行make操作:
我们将我们的头文件和.so文件打包为了dy-lib文件。
3.2使用动态库
和上面使用静态库一样,我们链接头文件和库文件,而且找到链接库的名称即可。
gcc main.c -I ./dy-lib/include/ -L ./dy-lib/my-dy-lib -l mymethod
但是在链接的时候,会报错。因为动态库在哪里也要告诉系统——加载器,加载同样也需要过程。
进行动态库的软链接,ldd成功找到链接。
运行成功。
3.3解决加载不到动态库的方法
1.拷贝到系统默认的库路径 /lib64 /usr/lib64/
2.在系统默认的库路径 /ib64 /usr/lib64/下建立软连接
3.将自己的库所在的路径,添加到系统的环境变量LD LIBRARY PATH中
4. /etc/ld.so.conf.d 建立自己的动态库路径的配置文件,然后重新ldconfiq即可
实际情况,我们用的库都是别人的成熟的库,都采用直接安装到系统的方式。