首页 > 其他 > 详细

【CPP】静态库与动态库

时间:2020-12-17 15:34:04      阅读:36      评论:0      收藏:0      [点我收藏+]

静态库与动态库

静态库与动态库的比较

函数库本质是一组函数的集合,具有相近的功能或操作。根据链接时期的不同,函数库分为静态库和动态库。

静态库是指链接时,把库文件链接到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。静态库后缀名一般为.a。动态库与之相反,在链接时并没有把库文件链接到可执行文件中,而是仅简单地引用其函数声明,在程序执行时根据需要动态链接库文件,这样可以节省系统的开销。动态库后缀名一般为.so

静态库和动态库的最大区别在于,在静态情况下,把库直接加载到程序中;而动态库链接的时候,它只是保留接口声明,将动态库与程序代码独立,这样就可以提高代码的可复用度,和降低程序的耦合度。

  • 静态库可以降低运行依赖,但是会增大程序体积,并且耦合性高。例如,100 份程序都用到静态库 a,静态库 a 会被拷贝 100 份。并且一旦静态库需要修改,需要修改 100 份。
  • 动态库提高代码的复用度,减少程序体积,但是程序运行时需要安装相应的动态库。例如,100 份程序都用到动态库 a,它们会共用 1 份动态库 a,并且修改方便。但是需要运行程序时,系统必须安装有动态库 a。

gcc 介绍

Linux 系统下的 gcc 是 GNU 推出的功能强大、性能优越的多平台编译器。gcc 是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高 20%~30%。gcc 编译器能将 C、C++ 语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc 将生成一个名为 a.out 的可执行文件。

静态库是目标文件的一个集合,目标文件中通常仅解析了文件内部的变量和函数,对于引用的函数和变量还没有解析,这需要将其他已经编写好的目标文件引用进来,将没有解析的函数和变量进行解析,通常引用的目标是库。

gcc 编译流程

gcc 编译源代码时,会依次经过预处理-编译-汇编-链接这 4 个流程。

  • 预处理,对源代码中的文件包含、预编译语句等进行分析,生成预编译文件:gcc -E hello.c -o hello.i
  • 编译,生成汇编文件:gcc -S hello.i -o hello.s
  • 汇编,生成目标文件:gcc -c hello.s -o hello.o
  • 链接,生成可执行文件:gcc hello.o -o hello

gcc 参数表

参数 含义
-E 只预处理,不编译,生成编译代码.i
-S 只编译,不汇编,生成汇编代码.s
-c 只编译,不链接,生成目标文件.o
-o file 把输出文件输出到 file 里
-v 打印 gcc 的版本
-I dir 在头文件的搜索路径列表中加入 dir 目录
-L dir 在库文件的搜索路径列表中加入 dir 目录
-static 显式链接静态库(静态库也可以用动态库的方式链接)
-lxxx 链接名为 libxxx.so 的动态库

gcc 默认链接动态库。当有静态库与动态库同名时,默认链接动态库,如需指定静态库,需要加-static参数。

静态库

静态库的名字是libxxx.a,其中 xxx 是该 lib 的名称。

生成静态库

在 UNIX 中,使用ar -rc libxxx.a xxx1.o xxx2.o命令创建或者操作静态库。

参数 含义
-r 将目标文件插入静态库尾或者替换静态库中同名文件
-c 创建静态库文件

使用静态库的例子

// add.h
#ifndef _ADD_H
#define _ADD_H
int add(int a, int b);
#endif
// add.cpp
#include "add.h"
int add(int a, int b)
{
    return a + b;
}
// sub.h
#ifndef _SUB_H
#define _SUB_H
int sub(int a, int b);
#endif
// sub.cpp
#include "sub.h"
int sub(int a, int b)
{
    return a-b;
}
// main.cpp
#include "add.h"
#include "sub.h"
#include <iostream>

int main()
{
    std::cout << add(1, 2) << std::endl;
    std::cout << sub(1, 2) << std::endl;
}
  • g++ -c add.cpp, g++ -c sub.cpp,将源代码编译成.o文件。
  • ar -cr libmymath.a add.o sub.o,将.o文件生成静态库。
  • g++ main.cpp -L ./ -static -lmymath,说明是静态库,默认是加载动态库的
  • 或者g++ main.cpp libmymath.a,此时不用指定静态库。

动态库

动态库的名字是libxxx.so.major.minor,xxx 是该 lib 的名称,major 是主版本号,minor 是副版本号。版本号也可以没有,一般都会建立个没有版本号的软连接文件链接到全名的库文件。

动态链接库是目标文件的集合,目标文件在动态链接库中的组织方式是按照特殊方式形成的。库中函数和变量的地址是相对地址,不是绝对地址,其真实地址在调用动态库的程序加载时形成。

生成动态库

不同的 Linux 系统,链接动态库方法,实现细节不一样。编译 PIC 型.o中间文件的方法一般是采用 C 语言编译器的-KPIC或者-fPIC选项,有的 Linux 版本 C 语言编译器默认带上了 PIC 标准。创建最终动态库的方法一般采用 C 语言编译器的-G 或者-shared 选项,g++ -fPIC -shared -o libmymath.so add.cpp sub.cpp

参数 含义
-shared 指定生成动态连接库
-fPIC 编译为位置独立的代码,跨平台

使用动态库的例子

源代码同静态库。

  • g++ -fPIC -shared -o libmymath.so add.cpp sub.cpp,生成动态库
  • g++ main.cpp -L ./ -lmymath
  • 或者g++ main.cpp libmymath.so

库的查找与加载

系统默认的头文件的搜索路径一般为/usr/include, /usr/local/include, /usr/lib/gcc/i486-Linux-gun/4.1.2/include

系统默认的库文件的搜索路径一般为/lib, /usr/lib,具体定义在文件/etc/ld.so.conf或者环境变量LD_LIBRARY_PATH中。

链接时

  • 显式指定库的路径。gcc main.cpp ../lib/libxxx1.a ../lib/libxxx2.so
  • 或者通过参数指定库的路径。gcc main.cpp -L ../lib/ -lxx1 -lxx2
    • -L ../lib/:指定库的搜索路径。
    • -lxx1 -lxx2:指定库文件名。

运行时

由于静态库在链接时已经写入可执行程序,故运行时不需要额外操作了。

但是动态库需要运行时动态地按需调用,与链接时指定的方法不同,运行时则需要系统自行搜索,搜索的路径一般有两种方式指定:

  • 第一种方式。由文件/etc/ld.so.conf来指定,一般默认是/lib、/usr/lib,所以要想让动态库顺利加载,可以将库文件拷贝到上面的两个目录下或者追加自己的动态库目录到/etc/ld.so.conf文件后再ldconfig刷新即可。
  • 第二种方式。设置LD_LIBRARY_PATH环境变量,添加自己的动态库的目录后再source .bashrc刷新即可。

另外还有个文件需要了解/etc/ld.so.cache,里面保存了常用的动态函数库,且会先把他们加载到内存中,因为内存的访问速度远远大于硬盘的访问速度,这样可以提高软件加载动态函数库的速度了。

【CPP】静态库与动态库

原文:https://www.cnblogs.com/zghong/p/14149155.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!