1.4 编译库:静态库、动态库

一、静态库

1.1 编译静态库

静态库:是将多个.o文件打包成一个归档文件(.a)

源码下载地址:universe.zip

(1)编译源码

gcc -c -o zeus.o zeus.c
gcc -c -o sun.o sun.c
gcc -c -o moon.o moon.c
gcc -c -o earth.o earth.c

(2)将.o文件打成包静态库

ar -rcs libstar.a moon.o sun.o earth.o

# ar是Linux归档工具
# -r 如果库中有同名的.o文件,则替换
# -c 如果静态库不存在,则创建。
# -s 为静态库生成或更新索引

(3)使用静态库

poseidon.c 内容如下:

#include "sun.h"
#include "moon.h"
#include "earth.h"
#include <stdio.h>
int main()
{
    printf("poseidon do something \r\n");
    sun_rotate();
    moon_rotate();
    earth_rotate();
    return 0;
}
# 1.编译
gcc -c poseidon.c

# 2.链接静态库
gcc -o poseidon poseidon.o -static -L./ -lstar # 通过-L指定库路径

gcc -o poseidon poseidon.o libstar.a # 通过库完整名称来指定

1.2 封装静态库

封装静态库:在静态库基础上,增加一些功能代码,再重新打包静态库。

封装静态库流程

  • 将新增的.c文件编译成.o文件
  • 将静态库解压:ar -x
  • 将所有的.o文件重新打包成静态库

项目代码:handes.zip

(1)编译新增代码

gcc -c -o dog.o dog.c
gcc -c -o pig.o pig.c

(2)解压原静态库

# libstar.a会解压出:earth.o、moon.o、sun.o
ar -x libstar.a

(3)重新打包

ar -rcs libpower.a earth.o moon.o sun.o dig.o pig.o

注意

  • 不能直接把libstar.a和新.o文件一起打包,否则会有问题。
  • 静态库不是纯ELF文件,而是多个ELF文件的合并,所以必须先解压再打包

二、动态库

2.1 编译动态库

动态库:多个程序共用,共享节省磁盘空间。

源码下载地址:universe.zip

(1)编译源码

gcc -c -o sun.o sun.c
gcc -c -o moon.o moon.c
gcc -c -o earth.o earth.c 

(2)编译动态库

# 编译动态库 -fPIC 生成位置无关的代码,-shared生成动态库
gcc -fPIC -shared -o libstar.so sun.o moon.o earth.o

(3)使用动态库

# 链接动态库
gcc -c zeus.c
gcc -o zeus zeus.o libstar.so  

# 运行报错,找不到libstar.so,因为Linux默认不从当前目录加载动态库。
./zeus

# 可以设置环境变量 再运行
export LB_LIBRARY_PATH=./:$LB_LIBRARY_PATH
./zeus

2.2 显式使用动态库

问题:当静态库和动态库存在同名符号时,会导致符号冲突。
如:

  • 静态库A(依赖C标准库libc.a)和 动态库B(依赖C标准库libc.so)
  • 现在程序program需要同时链接A和B库。
  • A和B存在相同的符号(函数)就会导致符号冲突

解决方法正常链接静态库,动态库在代码中显式调用。

zeus.c 的代码:

  #include <stdio.h>
  #include<dlfcn.h>
  
  int main(int argc, char **argv)
  {
    // 动态库的句柄
    void *handle;
    
    // 动态库中函数的地址
    int (*sun_rotate)();
  
    // 打开动态库
    handle = dlopen("/home/oem/Desktop/dev/cpp/universe/libstar.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }
  
    // 获取函数的地址
    sun_rotate = dlsym(handle, "sun_rotate");
    if (!sun_rotate) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }
    
    // 调用函数
    printf("zeus2 do something\n");
    (*sun_rotate)();
  
    // 关闭动态库
    dlclose(handle);
  
    return 0;
  }

编译和链接

# 会提示一个警告,但不影响运行
gcc -c zeus.c
gcc -c zeus zeus.o -ld -static

2.3 封装静态库为动态库

封装静态库成动态库:在已有的静态库基础上,增加一些功能代码,并封装成新动态库。

封装动态库流程

  • 将新增的.c文件编译成.o文件
  • 将静态库解压:ar -x
  • 将所有.o文件重新链接成动态库。

项目代码:handes.zip

(1)编译新增代码

gcc -c -o dog.o dog.c
gcc -c -o pig.o pig.c

(2)解压原静态库

# libstar.a会解压出:earth.o、moon.o、sun.o
ar -x libstar.a

(3)重新打包

gcc -fPIC -shared -o libpower.so dog.o pig.o moon.o earth.o sun.o

2.4 混合使用静态库与动态库

编译器支持同时链接静态库和动态库:

  • -Wl,-Bstatic 指定后续库静态链接。
  • -Wl,-Bdynamic 指定后续库动态链接。

示例项目

  • 项目用到动态库libstar.so和静态库libcook.a
  • 动态库存储在:/usr/local/star/lib
  • 静态库存储在:/usr/lib
gcc -c theseus.c
gcc -o theseus theseus.o -Wl,-Bstatic -lcook -Wl,-Bdynamic -L/usr/local/star/lib -lstar
#  静态库不使用-L指定库路径的原因,是/usr/lib系统默认的库路径

注意:避免同一库同时静态和动态链接,防止冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值