静态库和动态库

静态库和动态库

1.什么是库

库可以简单地看成一种代码仓库,他提供给使用者一些可以直接拿来使用的变量,函数和类;它也是一种特殊的程序,但是库不可以单独运行;

库可分为两种,一种是静态库,一种是动态库,区别是静态库在程序的链接阶段会被复制到程序中,而动态库顾名思义,在运行时才调入内存。

库的好处:代码保密,方面部署和分发。

以下是它们各自的优缺点:
静态库的优缺点:
优点:

  • 可以在编译时就地将静态库链接到目标代码中,减少了程序运行时的资源占用。
  • 适用于多次编译和长期运行的系统,因为静态库不会被动态加载和卸载,因此不会带来额外的开销。
    缺点:
  • 静态库的代码和数据会被链接到目标代码中,因此会增加目标代码的大小和复杂度,影响程序的运行速度。
  • 如果需要修改静态库的代码或数据,则需要重新编译整个程序,不利于版本管理和更新。
    动态库的优缺点:
    优点:
  • 可以在程序运行时动态加载和卸载,因此可以更灵活地管理库文件的版本和更新。
  • 可以减少程序运行时的资源占用,因为动态库只有在需要时才会被加载。
    缺点:
  • 需要在编译时就将动态库链接到目标代码中,因此会增加程序编译时的复杂度和时间。
  • 如果需要修改动态库的代码或数据,则需要重新编译目标程序,不利于版本管理和更新。
    综上所述,静态库适用于需要长期运行和稳定的系统,动态库适用于需要更灵活和高效运行的程序。在选择使用静态库或动态库时,需要根据具体的需求和场景进行权衡和选择。

2.静态库

命名规则

​ Linux中:libxxx.a(lib是固定前缀,xxx是自己起的库名称,.a是固定后缀名)

​ Windows中:libxxx.lib

静态库的制作:

在这里插入图片描述

​ gcc获得.o文件,将.o文件打包,使用ar工具(archive),命令如下:

ar rcs libxxx.a xxx.o xxx.o 

其中:r是将文件插入备存文件中;c是建立备存文件;s是索引

具体操作

我们首先看一下项目结构,所使用的file文件目录如下所示:

.file
└── library
    ├── include
    │   └── head.h
    ├── lib
    ├── main.c
    └── src
        ├── add.c
        ├── div.c
        ├── mult.c
        └── sub.c

假设library是我们开发的一个项目,我们将封装以下文件为一个静态库

#include <stdio.h>
#include "head.h
int add(int a, int b)
{
    return a+b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
double divide(int a, int b)
{
    return (double)a/b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
int multiply(int a, int b)
{
    return a*b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
int subtract(int a, int b)
{
    return a-b;
}

通过调用封装好的库来实现对main.c文件的编译

#include <stdio.h>
#include "head.h"

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

封装流程如下:

首先将.c文件编译汇编为.o文件,在src目录下执行

gcc -c add.c div.c mult.c sub.c -I ../include/

得到如下文件,在src目录下执行:

add.c  add.o  div.c  div.o  head.h  main.c  mult.c  mult.o  sub.c  sub.o

输入命令:

ar rcs libcalc.a div.o sub.o mult.o add.o

将得到的libcalc.a移动到lib目录下,在src目录下执行

mv libcalc.a ../lib/

得到最终项目结构如下:

├── include
│   └── head.h
├── lib
│   └── libcalc.a
├── main.c
└── src
    ├── add.c
    ├── div.c
    ├── mult.c
    └── sub.c
    //这里将src文件下的四个文件编译汇编成libcalc.a文件并且转移到lib中

此时,项目的静态库已经准备好了,试试来编译main.c文件

library目录下输入命令

gcc main.c -o app

报错:

lin@lin-virtual-machine:~/Linux/lesson04/library$ gcc main.c -o app
main.c:2:10: fatal error: head.h: No such file or directory
    2 | #include "head.h"
      |          ^~~~~~~~
compilation terminated.

因为在main.c的当前目录下,找不到include文件下的头文件

library目录下输入命令

gcc main.c -o app -I ./include/

报错

lin@lin-virtual-machine:~/Linux/lesson04/library$ gcc main.c -o app -I ./include/
/usr/bin/ld: /tmp/ccs9W4v1.o: in function `main':
main.c:(.text+0x41): undefined reference to `add'
/usr/bin/ld: main.c:(.text+0x66): undefined reference to `subtract'
/usr/bin/ld: main.c:(.text+0x8b): undefined reference to `multiply'
/usr/bin/ld: main.c:(.text+0xb0): undefined reference to `divide'
collect2: error: ld returned 1 exit status

在main文件中,具体的静态库的定义是在lib文件下的库文件中,所以除了加载头文件,还需要链接库文件。

故,library目录下输入命令

lin@lin-virtual-machine:~/Linux/lesson04/library$ gcc main.c -o app -I ./include/ -l calc -L ./lib

lin@lin-virtual-machine:~/Linux/lesson04/library$ ls
app  include  lib  main.c  src

-l指示静态库的名称clac,不是库文件的名字libcalc,-L表示库的地址

library目录下执行app文件

lin@lin-virtual-machine:~/Linux/lesson04/library$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

至此,静态库的制作和使用就已经介绍完了


3.动态库

命名规则

​ Linux中:libxxx.so(lib是固定前缀,xxx是自己起的库名称,.so是固定后缀名)

​ Windows中:libxxx.dll

静态库的制作:

在这里插入图片描述

gcc获得.o文件,将.o文件打包,-fpic得到和位置无关的代码,命令如下:

gcc -c -fpic a.c b.c

gcc得到动态库

gcc -shared a.o b.o -o libcalc.so

具体操作

我们首先看一下项目结构,所使用的file文件目录如下所示:

.file
└── library
    ├── include
    │   └── head.h
    ├── lib
    ├── main.c
    └── src
        ├── add.c
        ├── div.c
        ├── mult.c
        └── sub.c

假设library是我们开发的一个项目,我们将封装以下文件为一个动态库

#include <stdio.h>
#include "head.h
int add(int a, int b)
{
    return a+b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
double divide(int a, int b)
{
    return (double)a/b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
int multiply(int a, int b)
{
    return a*b;
}
//----------------------------//
#include <stdio.h>
#include "head.h"
int subtract(int a, int b)
{
    return a-b;
}

通过调用封装好的库来实现对main.c文件的编译

#include <stdio.h>
#include "head.h"

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

封装流程如下:

首先将.c文件编译汇编为.o文件,在src目录下执行

gcc -c add.c div.c mult.c sub.c -I ../include/

得到如下文件,在src目录下执行:

add.c  add.o  div.c  div.o  head.h  main.c  mult.c  mult.o  sub.c  sub.o

输入命令:

gcc -shared div.o sub.o mult.o add.o -o libcalc.so

将得到的libcalc.so移动到lib目录下,在src目录下执行

mv libcalc.so ../lib/

得到最终项目结构如下:

├── include
│   └── head.h
├── lib
│   └── libcalc.so
├── main.c
└── src
    ├── add.c
    ├── div.c
    ├── mult.c
    └── sub.c
    //这里将src文件下的四个文件编译汇编成libcalc.so文件并且转移到lib中

此时,项目的静态库已经准备好了,试试来编译main.c文件

library目录下输入命令

gcc main.c -o app

报错:

lin@lin-virtual-machine:~/Linux/lesson04/library$ gcc main.c -o app
main.c:2:10: fatal error: head.h: No such file or directory
    2 | #include "head.h"
      |          ^~~~~~~~
compilation terminated.

因为在main.c的当前目录下,找不到include文件下的头文件

library目录下输入命令

gcc main.c -o app -I ./include/

报错

lin@lin-virtual-machine:~/Linux/lesson04/library$ gcc main.c -o app -I ./include/
/usr/bin/ld: /tmp/ccs9W4v1.o: in function `main':
main.c:(.text+0x41): undefined reference to `add'
/usr/bin/ld: main.c:(.text+0x66): undefined reference to `subtract'
/usr/bin/ld: main.c:(.text+0x8b): undefined reference to `multiply'
/usr/bin/ld: main.c:(.text+0xb0): undefined reference to `divide'
collect2: error: ld returned 1 exit status

在main文件中,具体的动态库的定义是在lib文件下的库文件中,所以除了加载头文件,还需要链接库文件。

在library目录下输入命令

gcc main.c -o app -I include/ -L lib/ -l calc

但是依旧在运行时报错

lin@lin-virtual-machine:~/Linux/lesson04/library$ ./app
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

这个错误表示动态库加载失败

原因如下

ldd查找动态库依赖关系:

lin@lin-virtual-machine:~/Linux/lesson04/library$ ldd app
	linux-vdso.so.1 (0x00007ffdab9af000)
	libcalc.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000

由于动态库是运行时加载入内存的,所以我们需要在运行时,让系统找到动态库的位置,报错就是系统无法找到动态库的具体位置,导致加载失败。

如何找到动态库的位置呢?

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是由ld-linux.so来完成的,它先后搜索elf文件的 DT_RPATH段—>环境变量LD_LIBRARY_PATH—> /etc/ld.so.cache文件列表—>/lib/,/usr/lib目录找到库文件后将其载入内存。

解决动态库加载失败

DT_PATH段我们是无法修改的,所以我们需要在环境变量LD_LIBRARY_PATH中加入所需要的动态库绝对路径。

故,lib目录下输入命令

lin@lin-virtual-machine:~/Linux/lesson04/library/lib$ pwd
/home/lin/Linux/lesson04/library/lib

拿到了动态库绝对路径后保存到环境变量中:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lin/Linux/lesson04/library/lib

确认路径保存成功

lin@lin-virtual-machine:~/Linux/lesson04/library$ echo $LD_LIBRARY_PATH
:/home/lin/Linux/lesson04/library/lib

现在再查看动态库依赖度关系

lin@lin-virtual-machine:~/Linux/lesson04/library$ ldd app
	linux-vdso.so.1 (0x00007ffd2e185000)
	libcalc.so => /home/lin/Linux/lesson04/library/lib/libcalc.so (0x00007f0524961000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0524600000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f052496d000)

library目录下执行app文件

lin@lin-virtual-machine:~/Linux/lesson04/library$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

注意

这种配置只在当前终端中有用,该终端页面关闭后环境变量便消失了

要永久配置该环境变量,有两种方式,一种是用户级别,一种是系统级别

用户级别

in@lin-virtual-machine:~$ ll
total 100
drwxr-x--- 19 lin  lin  4096  6月  2 16:47 ./
drwxr-xr-x  3 root root 4096  5月 28 09:56 ../
-rw-------  1 lin  lin    37  6月  2 16:47 .bash_history
-rw-r--r--  1 lin  lin   220  5月 28 09:56 .bash_logout
-rw-r--r--  1 lin  lin  3771  5月 28 09:56 .bashrc
//需要修改 .bashrc

输入命令

vi .bashrc

在该文件最后一行插入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lin/Linux/lesson04/library/lib

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lin/Linux/lesson04/library/lib

并输入以下命令使得命令生效

. .bashrc

即可配置完成

系统配置就是在 /etc/profile文件下,依旧是按上面的格式插入绝对路径即可

再激活更改

source  /etc/profile

至此,动态库的制作和使用就已经介绍完了

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值