Linux 库文件浅谈(C语言)

前言:

在讲解Linux下库文件的知识之前,请先来简单了解一下C语言程序的编译过程。

预处理
编译
汇编
链接`lib库文件`
a.c / b.c / ...
a.i / b.i / ...
a.s / b.s / ...
a.o / b.o / ...
example 可执行文件

在Linux系统gcc编译器下,C语言程序的编译过程基本可分为预处理编译汇编链接四个过程。

其中,最后一步的链接,就是将系统的库文件(或是程序员自己编写的库文件)与经过汇编步骤生成的所有.o文件(可重定位文件1)合并生成最后的example可执行文件。库文件的使用也主要与此部分有关。


Linux 下库文件简介

1. 库文件是什么?

由以上C语言编译过程可以知道,编译最后要生成可执行文件example,必须去链接一些所谓的“库文件”,那这些神秘的库文件又是什么呢?

其实,库文件就是包含一系列子程序的集合体,也可以简单理解为一堆.o文件的打包集合。在库文件中封装了一系列函数的实现,而调用者通过加载库文件就可以实现对库中的函数的调用。(注:根据实现和使用方式的不同又可分为静态库和动态库,后面会详细介绍;编译过程默认链接系统标准C库和gcc内置库,外部库需手动链接

一方面,对库文件的灵活使用可以使得编程更加便捷、高效。另一方面,对库的使用也更好的保证了部分程序源码的私密性(如某个函数中包含了重要的算法,而你又不想公开这个算法,那么便可以将其编入库中)。同时,使用库文件还可以减少重复编译的时间,增强程序的模块化。

2. 静态库与动态库的区别

静态库与动态库二者之间最大的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。而动态库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

静态库动态库
在程序编译阶段载入程序在程序运行阶段动态加载
使得可运行程序体积较大可运行程序体积基本不变
程序的运行不依赖于库,一次编译,永久有效,运行效率较高程序的运行依赖于库,需动态加载,运行效率较低
浪费大量储存空间节省大量储存空间
程序编译阶段载入程序,无法进行后期的维护和升级程序运行阶段动态加载,与程序耦合性较低,方便进行后期的维护和升级

静态库与动态库各有利弊,但由于动态库更加方便进行后期的维护和升级,动态库在实际项目中更加常用。


Linux 下库文件制作

假设有两个文件 a.cb.c,下面我们分别将它们制作成静态库libxxx.a和动态库libxxx.soxxx为库的随意名字,静态库和动态库名称不一定要相同)。

1. 制作.o文件:

gcc a.c -o a.o -c -fPIC
gcc b.c -o b.o -c -fPIC

由此得到文件 a.ob.o

2. 制作静态库:

ar rcs libxxx.a a.o b.o

由此得到静态库libxxx.a

3. 制作动态库:

gcc a.o b.o -o libxxx.so -shared -fPIC

由此得到动态库libxxx.so


Linux 下库文件的使用

1. 编译时库的使用

gcc example.c -o example -L dir/ -lxxx

备注

  1. dir/ 中存放库中函数声明的头文件
  2. 除去前缀后缀之外的,如libxxx.a,库名就是xxx
  3. 当同一个目录下,有重名的动态、静态库,默认情况下会自动链接动态库,如果要指定静态库:
gcc example.c -o exmaple -L dir/ -lxxx -static

2. 运行时库的使用

静态库由于在程序编译阶段就已经编入了程序中,因此运行时就不再需要,而动态库则需要动态链接。

动态库链接的3种方法

  1. 设置环境变量 LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:dir/ 

注:dir/ 就是动态库的所在路径,也可以将命令写入系统配置文件使其永久生效。

  1. 将库文件,放入系统的标准库路径中:
/lib
/usr/lib
/usr/local/lib
...

注:避免使用这种方法,污染了系统!

  1. 在编译的阶段,告诉程序以后动态库会放在哪里:

告诉程序 example,运行时动态库的路径就在 dir/ 里面

gcc example.c -o example -L . -lxxx -Wl,-rpath=dir/

此篇文章仅供大家参考学习,内容较为浅显,如有不足之处,还请见谅!


  1. 所谓的可重定位,指的是该文件虽然已经包含可以让处理器直接运行的指令流,但是程序中所有的全局符号(函数和全局变量)尚未定位,地址仍为0,程序无法正确运行。该文件中所调用的函数或是全局变量的声明可能不在此文件中,需要结合其他文件中的信息才能重新定位此函数或是变量地址,程序才能正确运行。 ↩︎

听说这个有用 The following are typedefs of fundamental integral types or extended integral types. signed type unsigned type description intmax_t uintmax_t Integer type with the maximum width supported. int8_t uint8_t Integer type with a width of exactly 8, 16, 32, or 64 bits. For signed types, negative values are represented using 2's complement. No padding bits. Optional: These typedefs are not defined if no types with such characteristics exist.* int16_t uint16_t int32_t uint32_t int64_t uint64_t int_least8_t uint_least8_t Integer type with a minimum of 8, 16, 32, or 64 bits. No other integer type exists with lesser size and at least the specified width. int_least16_t uint_least16_t int_least32_t uint_least32_t int_least64_t uint_least64_t int_fast8_t uint_fast8_t Integer type with a minimum of 8, 16, 32, or 64 bits. At least as fast as any other integer type with at least the specified width. int_fast16_t uint_fast16_t int_fast32_t uint_fast32_t int_fast64_t uint_fast64_t intptr_t uintptr_t Integer type capable of holding a value converted from a void pointer and then be converted back to that type with a value that compares equal to the original pointer. Optional: These typedefs may not be defined in some library implementations.* Some of these typedefs may denote the same types. Therefore, function overloads should not rely on these being different. * Notice that some types are optional (and thus, with no portability guarantees). A particular library implementation may also define additional types with other widths supported by its system. In any case, if either the signed or the unsigned version is defined, both the signed and unsigned versions are defined.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值