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

gcc的静态链接编译和动态链接编译

首先我们得知道:
gcc 默认编译是动态链接;
gcc使用静态链接编译需要加参数static;


准备源文件:mytest.c
在这里插入图片描述


使用gcc 默认编译后,得到 mytest 可执行文件:
使用 file 命令查看可执行程序mytest:发现使用的是动态链接:
在这里插入图片描述


使用 ldd 命令可以查看可执行程序 ,mytest的动态链接库:
在这里插入图片描述


假如gcc -static静态编译 源文件:mytest.c:那么就是静态编译:

gcc mytest.c -o mytest_static -static # 静态编译 mytest.c为mytest_static

使用 file 命令查看可执行程序mytest_static:发现使用的是静态链接:
在这里插入图片描述


使用 ldd 命令可以查看可执行程序 ,mytest_static的动态链接库:发现没有使用动态链接库:
在这里插入图片描述


对比:动态链接编译和静态链接编译的可执行程序大小:发现静态链接的比动态链接的大:
在这里插入图片描述
原因:静态链接时把库文件加载到和源文件一起编译形成呢可执行文件;所以很大;
而动态链接时,编译源文件为可执行程序,需要用到动态库的哪个函数,就会跳转到该动态库的那个函数去执行,并不会把动态库所有文件和源文件一起编译;


我们的云服务器的静态库和动态库的位置在:

在这里插入图片描述


注意默认情况:云服务器没有静态库,只有动态库;没有静态库,也就是无法使用 gcc -static的选项;
所以我们假如没有静态库:那么安装:sudo yum -install -y glibc-static;


静态库的制作

不管时静态库还是动态库,本质都是二进制文件,该库就无法查看里面有什么函数功能方法的实现;

那么我们如何查看该库有什么函数呢?

那就是通过提供该库的头文件,头文件就是为了暴露该库的接口的,由于头文件时二进制文件,所以我们可以查看到该库有什么函数接口;


对于一个完整的库来说:包括三个文件

  1. 库文件
  2. 头文件
  3. 库使用说明文档

为什么C/C++要把声明放在.h中,实现放在.c或者.cpp 或者.cc中呢?
原因1:方便维护;
原因2:就是为了制作库!制作库是想把库给别人用,但是不想给源代码别人用,所以用.h来暴露接口;


静态库的制作:
准备文件:

  1. add.h sub.h add.c sub.cadd.csub.c制作成静态库;
/add.h/
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // __ADD_H__
/add.c/
#include "add.h"
int add(int a, int b)
{
    
    
return a + b;
}
/sub.h/
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif // __SUB_H__
/add.c/
#include "sub.h"
int sub(int a, int b)
{
    
    
return a - b;
}


制作静态库:

生成静态库,需要先对源文件进行汇编操作 (使用参数 -c) 得到二进制格式的目标文件 (.o 格式),
然后在通过 ar 工具将目标文件打包就可以得到静态库文件了 (libxxx.a)。
使用ar 工具创建静态库的时候需要三个参数:
参数c:创建一个库,不管库是否存在,都将创建。
参数s:创建目标文件索引,这在创建较大的库时能加快时间。
参数r:在库中插入模块 (替换)。默认新的成员添加在库的结尾处,如果模块名已经在库中存在,则替换同名的模块。


制作做静态库的步骤:

  1. 将源文件生成.o文件
  2. 用ar命令打包.o文件为静态库;
  3. 将静态库和头文件还有文档说明发布出去(就是放在一个目录下就行);

在这里插入图片描述


制作静态库的makefile文件:

在这里插入图片描述


查看以下当前的目录文件:
在这里插入图片描述


当我们make后:就会多出 .o文件和 静态库:ibmath.a,这个就是静态库;

在这里插入图片描述


然后我们在 make output就可以发布了静态库:
在这里插入图片描述


查看output的内容:就是我们的头文件和静态库;
在这里插入图片描述


制作成功,我们就可以make clean 情况不必要的资源了:
然后你的把output打包就可以直接给别人使用了你的静态库了;


静态库的使用

在搞一个源文件出来mypro.c它要使用静态库:
在这里插入图片描述


首先把output文件改名为lib,因为这样更加明确名字:
查看目录结构:
在这里插入图片描述


我们开始编译:

gcc mypro.c -o mypro  -L./lib -lmath

解释参数:gcc
在这里插入图片描述


编译成功:执行结果为:
在这里插入图片描述


在这里插入图片描述


制作动态库

生成动态链接库是直接使用 gcc 命令并且需要添加 -fPIC(-fpic) 以及 -shared 参数。

-fPIC 或 -fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
-shared参数的作用是告诉编译器生成一个动态链接库。

在这里插入图片描述


生成动态链接库的具体步骤如下:

  1. 将源文件进行汇编操作,需要使用参数-c, 还需要添加额外参数-fPIC,
  2. 将得到的.o 文件打包成动态库,还是使用gcc, 使用参数 -shared指定生成动态库 (位置没有要求);
  3. 发布动态库和头文件;

制作动态库的makefile文件:
在这里插入图片描述


只要我们make,就可以得到动态库:libmath.so;
make output ,就可以得到动态库和头文件的打包文件output;
make clean 清理资源;
再把得到的 output给别人就可以使用了l


动态库的使用

当我们试着像使用静态库那样使用动态库:发现编译没错,执行时候没有链接动态库;
在这里插入图片描述


原因是:当你编译源文件时候,只是告诉了编译器,库文件和头文件所在的路径而已;
当你执行了可执行程序,这个可执行程序就和加载器有关了,和编译器无关了;


库的工作原理

静态库如何被加载

在程序编译的最后一个阶段也就是链接阶段,提供的静态库会被打包到可执行程序中。当可执行程序被执行,静态库中的代码也会一并被加载到内存中,因此不会出现静态库找不到无法被加载的问题。

动态库如何被加载

在程序编译的最后一个阶段也就是链接阶段:

在 gcc 命令中虽然指定了库路径 (使用参数 -L ), 但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。
同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。
可执行程序被执行起来之后:
程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息;
当动态库中的函数在程序中被调用了,这个时候动态库才加载到内存,如果不被调用就不加载;
动态库的检测和内存加载操作都是由动态连接器来完成的;


动态链接器是一个独立于应用程序的进程,属于操作系统,当用户的程序需要加载动态库的时候动态连接器就开始工作了,很显然动态连接器根本就不知道用户通过 gcc 编译程序的时候通过参数 -L 指定的路径;


所以当我们使用动态库时候,需要告知加载器,去哪找到我们的动态库:
在系统中有一个环境变量:LIB_LIBRARY_PATH,只要我们给该变量赋值:动态库所在的路径即可;

export LD_LIBARAY_PATH=动态库所在的路径

在这里插入图片描述
在这里插入图片描述


这样就行了;但是每次重新启动一个新的终端,就会失效;这种方式


另一种:永久生效方式:添加配置文件

cd /etc/ld.so.conf.d/
touch myfile.conf #创建一个自己的.conf文件
vim myfile.conf #打开后复制动态链接库的路径进去,wq保存退出即可
ldconfig #更新链接库

上面操作没有权限就用sudo执行


假如要删除该文件直接

rm myfile.conf# 删除动态链接库的文件
ldconfig #更新

上面操作没有权限就用sudo执行

猜你喜欢

转载自blog.csdn.net/m0_46606290/article/details/124243421