(二)静态库和动态库

1 什么是库

  • 库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
  • 库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行。
  • 库文件有两种,静态库和动态库(共享库),区别是:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
  • 库的好处:1.代码保密;2.方便部署和分发。

2 静态库

2.1 命名规则

  • Linux:libxxx.a
    lib:前缀(固定)
    xxx:库名,自定义
    .a:后缀(固定)
  • Windows:libxxx.lib

2.2 静态库的制作

  • gcc 获得 .o 文件
  • 将 .o 文件打包,使用 ar 工具(archive)
    ar rcs libxxx.a xxx.o xxx.o

r – 将文件插入备存文件中
c – 建立备存文件
s – 索引

2.3 示例

当前目录文件如下:

.
├── add.c
├── div.c
├── head.h
├── mult.c
└── sub.c

head.h

#ifndef _HEAD_H
#define _HEAD_H

// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);

#endif

add.c

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

int add(int a, int b)
{
    return a+b;
}

div.c

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

double divide(int a, int b)
{
    return (double)a/b;
}

mult.c

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

int multiply(int a, int b)
{
    return a*b;
}

sub.c

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

int subtract(int a, int b)
{
    return a-b;
}

执行入如下命令:
gcc -c add.c div.c mult.c sub.c
ar rcs libcalc.a add.o div.o mult.o sub.o
最终生成 libcalc.a 文件。

2.4 静态库的使用

当前目录如下:

.
├── include
│   └── head.h
├── lib
│   └── libcalc.a
├── main.c

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;
}

执行编译编译链接命令:
gcc main.c -o app -I ./include -l calc -L ./lib

注意要以 -I 指定头文件位置,-l 指定库文件命名,-L 指定库文件地址,可以不加空格。

运行可执行文件,输出如下:

root@LDY:~/cpp-study/lesson05/library# ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

3 动态库

3.1 命名规则

  • Linux:libxxx.so
    lib:前缀(固定)
    xxx:库名,自定义
    .so:后缀(固定)
  • Windows:libxxx.dll

3.2 动态库的制作

  • gcc 得到 .o 文件,得到和位置无关的代码
    gcc -c –fpic/-fPIC a.c b.c
  • gcc 得到动态库
    gcc -shared a.o b.o -o libcalc.so

3.3 示例

执行入下命令:
gcc -c -fpic add.c div.c mult.c sub.c
gcc -shared add.o div.o mult.o sub.o -o libcalc.so
最终生成 libcalc.so 文件

3.4 动态库的使用

当前目录入下:

.
├── include
│   └── head.h
├── lib
│   └── libcalc.so
├── main.c

执行编译连接命令:
gcc main.c -o app -I include -L lib -l calc
运行可执行文件,会报以下错误:

./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

3.5 动态库加载失败原因

  • 静态库:GCC 进行链接时,会把静态库中代码打包到可执行程序中。
  • 动态库:GCC 进行链接时,动态库的代码不会被打包到可执行程序中。
  • 程序启动之后,动态库会被动态加载到内存中,通过 ldd(list dynamic dependencies)命令检查动态库依赖关系。
  • 如何定位共享库文件呢?
    当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是由ld-linux.so来完成的,它先后搜索elf文件的DT_RPATH段 ——> 环境变量LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——> /lib//usr/lib目录找到库文件后将其载入内存。

使用 ldd 命令分析依赖可以发现,没有找到生成的动态库。

root@LDY:~/cpp-study/lesson06/library# ldd app
        linux-vdso.so.1 (0x00007ffef8ad7000)
        libcalc.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f223a012000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f223a215000)

3.6 解决动态库加载失败的方法

  1. 将库路径添加到环境变量LD_LIBRARY_PATH
  • 会话级别
    执行以下命令
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/cpp-study/lesson06/library/lib

    打开新终端后失效

  • 用户级别
    在home目录下的.bashrc文件中添加以上命令
    刷新source .bashrc. .bashrc
  • 系统级别
    /etc/profil文件中添加以上命令
  1. 更新/etc/ld.so.cache文件
    该文件为二进制文件,不能直接编辑,将动态库路径复制到/etc/ld.so.conf中间接加入

  2. 将动态库复制到/lib/,/usr/lib路径下 (不推荐)
    再次执行 ldd 命令,找到了动态库

root@LDY:~/cpp-study/lesson06/library# ldd app
        linux-vdso.so.1 (0x00007ffd907de000)
        libcalc.so => /root/cpp-study/lesson06/library/lib/libcalc.so (0x00007f1099925000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1099729000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1099931000)

4 静态库的动态库的对比

静态库、动态库区别来自链接阶段如何处理,链接成可执行程序。分别称为静态链接方式和动态链接方式。

  • 静态库的优缺点
    • 优点
      • 静态库被打包到应用程序中加载速度快
      • 发布程序无需提供静态库,移植方便
    • 缺点
      • 消耗系统资源,浪费内存
      • 更新、部署、发布麻烦
  • 动态库的优缺点
    • 优点
      • 可以实现进程间资源共享(共享库)
      • 更新、部署、发布简单
      • 可以控制何时加载动态库
    • 缺点
      • 加载速度比静态库慢
      • 发布程序时需要提供依赖的动态库

参考链接
https://www.nowcoder.com/study/live/504

posted @   DaleLee  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示