二、C++语言进阶:动态库与静态库之函数篇

2 动态库与静态库之函数篇

  • 作用
    分离编译
    代码重用
  • 分类
分类作用后缀
静态库一个或多个.o目标文件归档在一个文件中.a
共享库没有main函数的可执行文件.so
动态加载库没有main函数的可执行文件,接口复合API.so

2.1 静态库的制作与使用

2.1.1 素材(文件)

  • Func.cpp
#include <iostream>
minclude "Func.h"
using namespace std;

void Func(int n){
    cout << __func__ << " " <<n << endl;
}
  • Func.h
#pragma once
void Func(int n);
  • main.cpp
#include <iostream>
#include "Func.h"
using namespace std;

int main(){
    Func(100);
}

2.1.2 创建

(1)编译源文件

g++ -c -o Func.o Func.cpp

(2)生成静态库

ar -rcs libFunc.a Func.o
  • ar选项
命令选项作用
r替换模块(replace)
c创建库(create)
s建立索引

注:
tar和ar都是归档工具
tar用于创建.tar归档文件。
ar用于创建归档文件,并且为归档的目标文件中的符号建立索引。

  • 查看目标文件的符号(symbol)信息
 nm 目标文件

目标文件可以是.o、.a,也可以是可执行文件。

2.1.3 使用

  • 链接静态库
g++ -o main main.cpp -L. -lFunc

或者

g++ -o main main.cpp ./libtest.a

注意:库一定要放在命令行的末尾

  • 测试
./main
  • 结果、
Func 100

2.2 动态库的制作与使用

2.2.1 创建

(1)编译目标文件

g++ -c -fPIC Func.cpp -o Func.o

(2)生成动态库

g++ -shared Func.o -o libFunc.so

注:以上两步可以合并为g++ -shared -fPIC -o libFunc.so test.cpp

选项说明

命令选项作用
shared创建动态库
fPIC代码都是与位置无关的

每个共享函数库都有个特殊的名字,称作sonamesoname名字命名必须以lib作为前缀,然后是函数库的名字,然后是.so,最后是版本号信息。不过有个特例,就是非常底层的C库函数都不是以lib开头这样命名的。

2.2.2 使用

(1)生成可执行文件

g++ -o main main.cpp -L. -lFunc

或者

g++ -o main main.cpp ./libFunc.so

注意:库一定要放在命令行的末尾

(2)测试
指定动态链接库位置

export LD_LIBRARY_PATH=动态链接库位置

执行

./main

结果

Func 100
  • 关于动态链接库的安装路径
    如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。
    如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:
    编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
    运行ldconfig ,该命令会重建/etc/ld.so.cache文件

注:当静态库和动态库同名时, gcc命令将优先使用动态库。

  • 查看执行文件链接的动态链接库:ldd 可执行文件
[root@localhost Func2]# ldd main
	linux-vdso.so.1 (0x00007fffb1da5000)
	./libFunc.so (0x00007fa2aeb6c000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fa2ae7d7000)
	libm.so.6 => /lib64/libm.so.6 (0x00007fa2ae455000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fa2ae23d000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fa2ade79000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa2aed6e000)

2.3 动态加载库

2.3.1 修改main.cpp

  • maindl.cpp
#include <iostream>
#include <dlfcn.h>
using namespace std;
int main(){
    void* handle = dlopen("./libFunc.so",RTLD_LAZY);
    if(nullptr == handle) return 0;

    typedef void (*Func_t)(int);
    Func_t pFunc = reinterpret_cast<Func_t>(dlsym(handle,"_Z4Funci")); //在动态库中找到Func函数,强转之后采用函数指针接收
    //Func_t pFunc = (Func_t)dlsym(handle,"Func"); //在动态库中找到Func函数,强转之后采用函数指针接收

    pFunc(100);

    dlclose(handle);
    handle = nullptr;
}

2.3.2 使用

(1)编译

g++ maindl.cpp -ldl

(2)执行

[root@localhost Func2]# ./a.out 

(3)结果

Func 100

2.4 动态库(共享库、动态加载库)与静态库的区别

2.4.1 区别

(1)静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
(2)动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
在这里插入图片描述
在这里插入图片描述

2.4.2 优缺点

  • 动态加载
    (1)灵活,可以在需要的时候进行加载,在不需要的时候进行卸载,这样可以不必占用内存。
    (2)可以在没有动态库时候发现,而不致程序报错。
    (3)加载程序中有条件才运行的库。
    (4)热更新,在不停止程序的前提下进行更新。
    (5)复杂一些,需要显示获得函数地址。
  • 静态加载
    (1)简单方便
    (2)没有找到动态库时,系统报错
    (3)加载运行很久的库

2.5 总结

  • 静态库、共享库与动态库编译链接使用比较
    在这里插入图片描述
  • 静态库与动态库Make file比较
    在这里插入图片描述

2.6 补充

使用动态库的方法还有如下几种

方法一:连接前,添加动态库目录到环境变量LD_RUN_PATH

export LD_RUN_PATH=动态库目录

方法二:编译链接时,添加链接选项-Wl,-rpath -Wl,动态库目录

方法三:执行前,添加动态库目录到环境变量LD_LIBRARY_PATH

export LD_LIBRARY_PATH=动态库目录

方法四:添加共享库目录/usr/local/lib到共享库配置文件

echo 动态库目录 >> /etc/ld.so.conf
ldconfig
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值