静态库和动态库

19 篇文章 0 订阅
5 篇文章 0 订阅

静态库和动态库

程序编译的四个阶段

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

每个阶段所做的工作

1, 预处理

宏替换 , 文件包含 (头文件展开等) , 条件编译 , 去注释

预处理指令是以 # 开头的代码行

gcc -E test.c -o test.i

选项 -E 是让编译器在预处理过后停止编译

选项 -o 是指目标文件 , .i 文件是预处理过后的原始 C 程序

2, 编译 (生成汇编代码)

对预处理过后的程序进行一系列的

词法分析、语法分析、语义分析

以及优化后产生相应的汇编代码文件

编译过程分为 6 步 :

  1. 扫描(词法分析)
  2. 语法分析
  3. 语义分析
  4. 源代码优化
  5. 代码生成
  6. 目标代码优化

gcc -S test.i -o test.s

选项 -S 使编译器在编译过后停止 , 不进行汇编 , 生成 .s 汇编代码文件

3, 汇编 (生成机器可识别代码)

把编译阶段生成的 .s 文件转成二进制目标代码 .o 文件

gcc -c test.s -o test.o

4, 链接 (生成可执行文件)

成功编译之后 , 就进入了链接阶段

gcc test.o -o test

函数库

我们在写代码的时候 , 用到了很多的库函数 , 但是我们并没有实现这些函数 , 我们包含的头文件也只有库函数的声明 , 没有实现 , 那么函数的实现在哪呢 ?

这些库函数都被封装到了一个名为 libc.so.6 的标准库文件之中

链接的时候就是在库文件中去寻找库函数的实现 , 这样程序就能正常运行了

库文件其实就是可执行代码的二进制形式 , 可以被操作系统载入内存执行

库一般有两种

  • 静态库

  • 动态库 (共享库)

静态库 是指在编译链接时把库文件的代码全部加到可执行文件中 , 因此生成的文件比较大 ,

但是在运行时也就不需要库文件了 .

静态库的名称后缀在 Linux 下一般为 .a , 在 Windows 下为 .lib

动态库 与之相反 , 在程序执行期间才去加载库文件 , 可以节省系统开销 .

动态库的名称后缀在 Linux 下一般为 .so , 在 Windows 下为 .dll

Linux 下生成静态库和动态库

我们写一个 Math 类来测试动静态库的生成

math.h

class Math {
public:
    double add(double& t_x, double& t_y);
    double sub(double& t_x, double& t_y);
    double mul(double& t_x, double& t_y);
    double div(double& t_x, double& t_y);
};

math.cpp

#include "math.h"

double Math::add(double& t_x, double& t_y)
{
    return t_x + t_y;
}
double Math::sub(double& t_x, double& t_y)
{
    return t_x - t_y;
}
double Math::mul(double& t_x, double& t_y)
{
    return t_x * t_y;
}
double Math::div(double& t_x, double& t_y)
{
    return t_x / t_y;
}

测试代码

test_math.cpp

#include <iostream>
#include "math.h"
using namespace std;

int main()
{
    Math math;
    double x, y;  
    cin >> x >> y;

    cout << "x + y = " << math.add(x, y) << endl;
    cout << "x - y = " << math.sub(x, y) << endl;
    cout << "x * y = " << math.mul(x, y) << endl;
    cout << "x / y = " << math.div(x, y) << endl;
}

现在我们如果直接 g++ test_math.cpp

这里写图片描述

肯定会报错 , 说我们的函数没有定义

下面我们生成一个静态库

  1. g++ -c math.cpp 生成 math.o 文件

这里写图片描述

  1. ar -rc libmy_math.a math.o 生成 libmy_math.a 静态库文件

这里写图片描述

  1. 使用静态库

因为编译器默认动态链接 , 如果想要静态链接可以使用 - static 选项

g++ -static test_math.cpp -o s_main -L. -lmy_math

这里写图片描述

其中 -o 选项是指定生成的可执行文件的名称

-L. 表示指定库的路径为本目录

-lmy_math 表示指定库名称 my_math

一般库的搜索路径为

  1. 从左到右搜索 -L 指定的目录
  2. 由环境变量指定的目录 LIBRARY_PATH
  3. 由系统指定的目录 /usr/lib /usr/local/lib

我们发现静态链接生成的可执行文件非常大 , 因为它把库文件的代码全都加入了可执行文件中

这里写图片描述

可以用 file 命令查看可执行文件的属性 , 发现是 statically linked

此时即便我们删除 libmy_math.a 库文件 , 程序仍然可以运行

这里写图片描述

接下来生成一个动态库

  1. g++ -fPIC -c math.cpp 生成 math.o 文件

    其中 -fPIC 选项表示 产生与位置无关码

  2. g++ -shared -o libmy_math.so math.o 生成 libmy_math.so 动态库文件

    其中 -shared 选项表示生成共享库格式

  3. 使用动态库

    g++ test_math.cpp -o d_main -L. -lmy_math

    因为编译器默认是动态链接的 , 所以不用加别的选项 , 直接指定路径和库名称就可以

这里写图片描述

此时生成了可执行文件 d_main , 发现他比 s_main 小了很多

通过 file 命令查看

这里写图片描述

发现他是 dynamically linked 的 , 运行 d_main

这里写图片描述

也没问题 . 此时如果我们删除动态库文件 libmy_math.so , 发现程序就无法运行了

这里写图片描述

编写 Makefile 自动化执行

.PHONY: build test clean

build: libmy_math.so

libmy_math.so: math.o
    g++ -shared -o $@ $^

math.o: math.cpp
    g++ -c -fPIC $^

test: d_main

d_main: test_math.cpp libmy_math.so
    g++ $^ -o $@ -L. -lmy_math
    LD_LIBRARY_PATH=. ./d_main

clean:
    rm -f *.o *.so d_main

执行命令 make build 即可生成动态库文件

执行命令 make test 即可动态链接库文件生成可执行文件并运行

执行命令 make clean 即可清理生成的文件

这里写图片描述

总结

下面总结一下静态库和动态库的优缺点

静态库动态库
链接时将库文件加入可执行程序之中程序运行时才去加载库文件
生成的可执行文件很大生成的可执行文件较小
会产生多份冗余的副本多进程共享 , 节省空间
运行时不依赖库文件运行依赖库文件
全量更新增量更新
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值