热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C/C++静态库与动态库

CC++静态库与动态库什么是库简单实例静态库动态库Reference什么是库库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从




C/C++ 静态库与动态库


  • 什么是库
    • 简单实例

  • 静态库
  • 动态库
  • Reference


什么是库

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

本质上来说库是一种可执行代码的二进制形式,可以操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。

库文件常常按照特定格式来组织,在linux下,它是ELF格式(Executable Linkable Format,可执行可链接格式),而在windows下是PE(Portable Executable,可移植可执行)。

而通常目标文件有三种形式:


  • 可执行目标文件:即我们通常所认识的,可直接运行的二进制文件。
  • 可重定位目标文件:包含了二进制的代码和数据,可以与其他可重定位目标文件合并,并创建一个可执行目标文件。
  • 共享目标文件:它是一种在加载或者运行时进行链接的特殊可重定位目标文件。

程序编译成可执行程序的步骤:
在这里插入图片描述


简单实例

编写.c文件

//main.c
#include
#include
int main(int argc,char *argv[])
{
printf("hello 编程珠玑\n");
int b = 2;
double a = exp(b);
printf("%lf\n",a);
return 0;
}

代码计算e的2次方并打印结果。由于代码中用到了exp函数,它位于数学库libm.so或者libm.a中,因此编译时需要加上-lm。

生成可重定位目标文件main.o:

$ gcc -c main.c #生成可重定位目标文件
$ readelf -h main.o #查看elf文件头部信息
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
(省略其他内容)

通过上面的命令将main.c生成为可重定位目标文件。通过readelf命令也可以看出来:REL (Relocatable file)。

观察共享目标文件libm.so:

$ readelf -h /lib/x86_64-linux-gnu/libm.so.6
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Shared object file)
(省略其他内容)

查看可执行目标文件main:

$ gcc -o main main.o -lm #编译成最终的可执行文件
$ readelf -h main #查看ELF文件头
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
(省略其他内容)

Note: 如果使用到的函数没有在libc库中,那么你就需要指定要链接的库,本文中需要链接libm.so或libm.a。可以看到,最终生成的main类型是Executable file,即可执行目标文件。


静态库

之所以称为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。

静态库特点总结:


  1. 静态库对函数库的链接是放在编译时期完成的。
  2. 程序在运行时与函数库再无瓜葛,移植方便。
  3. 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
    在这里插入图片描述
    静态链接构建我们的可执行文件:

$ gcc -c main.c
$ gcc -static -o main main.o -lm

在这个过程中,就会用到系统中的静态库libm.a。这个过程做了什么呢?首先第一条命令会将main.c编译成可重定位目标文件main.o,第二条命令的static参数,告诉链接器应该使用静态链接,-lm参数表明链接libm.a这个库(类似的,如果要链接libxxx.a,使用-lxxx即可)。由于main.c中使用了libm.a中的exp函数,因此链接时,会将libm.a中需要的代码“拷贝”到最终的可执行文件main中。

Note: 必须把-lm放在后面。

放在最后时它是这样的一个解析过程:


  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描main.o时,发现一个未解析的符号exp,记住这个未解析的符号
  • 扫描libm.a,找到了前面未解析的符号,因此提取相关代码
  • 最终没有任何未解析的符号,编译链接完成

如果将-lm放在前面


  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描libm.a,由于前面没有任何未解析的符号,因此不会提取任何代码
  • 扫描main.o,发现未解析的符号exp
  • 扫描结束,还有一个未解析的符号,因此编译链接报错

如果把-lm放在前面,编译结果如下:

$ gcc -static -lm -o main main.o
main.o: In function `main':
main.c:(.text+0x2f): undefined reference to `exp'
collect2: error: ld returned 1 exit status

动态库

为什么需要动态库,其实也是静态库的特点导致。


  • 空间浪费是静态库的一个问题。
    在这里插入图片描述
  • 另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
在这里插入图片描述
动态库特点总结:


  1. 动态库把对一些库函数的链接载入推迟到程序运行的时期。

  2. 可以实现进程之间的资源共享。(因此动态库也称为共享库)

  3. 将一些程序升级变得简单。

  4. 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

通常我们编译的程序默认就是实用动态链接:

$ gcc -o main main.c -lm #默认使用的是动态链接

通过ldd命令来观察可执行文件链接了哪些动态库:

$ ldd main
linux-vdso.so.1 => (0x00007ffc7b5a2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe9642bf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe963ef5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe9645c8000)

Reference

https://blog.nowcoder.net/n/8e07e78a703c413c916d0f830b8ceda7
https://zhuanlan.zhihu.com/p/71372182



推荐阅读
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文介绍了在Linux中执行.sh脚本时出现/bin/sh^M: bad interpreter: No such file or directory异常的原因分析,并提供了两种解决方法:在Windows下进行编码格式转换,或在Linux中修改文件格式和执行权限。具体操作步骤也在摘要中给出。 ... [详细]
  • 目录1、将mysql数据导出到SQL文件中(数据库存在的情况)2、将现有的sql文件数据导入到数据库中(前提数据库存在) 3、利用Navicat导出SQL文件和导入SQL文件1)从 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 通过Anaconda安装tensorflow,并安装运行spyder编译器的完整教程
    本文提供了一个完整的教程,介绍了如何通过Anaconda安装tensorflow,并安装运行spyder编译器。文章详细介绍了安装Anaconda、创建tensorflow环境、安装GPU版本tensorflow、安装和运行Spyder编译器以及安装OpenCV等步骤。该教程适用于Windows 8操作系统,并提供了相关的网址供参考。通过本教程,读者可以轻松地安装和配置tensorflow环境,以及运行spyder编译器进行开发。 ... [详细]
author-avatar
许心怡917
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有