本篇主要介绍的 库文件,包括静态库文件和动态库文件的使用及其他基本知识,相对比较基础和底层,对于后面对多线程编程的理解有很大帮助。本文所使用的环境为虚拟机 Ubuntu系统,使用 xshell 进行远程连接调试,所有示例均经过测试无误。
库文件是一类可以供使用者直接使用的变量、函数或类,属于一种特殊程序,在实际编写时与一般程序的区别不大,只是库不能单独运行
使用库可以实现代码保密,方便不熟和分发
Linux下和windows下对库的命名规则不一致
Linux : libxxx.a
Windows : libxxx.lib
使用 gcc 获得 .o 文件,将 .o 文件打包,使用 ar 工具(archive)
ar rcs libxxx.a xxx.o xxx.o
r – 将文件插入备存文件中
c – 建立备存文件
s – 索引
gcc -c xxx.c -----生成 .o 文件
ar rcs libcalc.a —lib表示生成库文件的名称,calc 才是库的名称
在该文件夹下,有放置头文件的文件夹 include,齐下放置头文件 head.h,有静态库文件夹,等会先生成静态库,然后放置静态库文件,程序入口点函数:main.c 以及各个函数文件夹下的各个函数文件 *.c
main.c
#include
#include "head.h"
int main()
{
int a = 20;
int b = 12;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
printf("a - b = %d\n", subtract(a, b));
printf("a * b = %d\n", multiply(a, b));
printf("a / b = %f\n", divide(a, b));
return 0;
}
下面是各个文件:
//add.c
#include
#include "head.h"
int add(int a, int b)
{
return a+b;
}
//div.c
#include
#include "head.h"
double divide(int a, int b)
{
return (double)a/b;
}
//mult.c
#include
#include "head.h"
int multiply(int a, int b)
{
return a*b;
}
//sub.c
#include
#include "head.h"
int subtract(int a, int b)
{
return a-b;
}
然后进入src 目录,对src 目录下的文件进行编译,生成 .o 文件
但是在编译时要注意,这几个文件都是在头文件 head.h 下的,所以就要包含头文件,使用编译选项 -I ,寻找头文件,就需要回到上一级目录,然后进入头文件目录
gcc -c add.c sub.c mult.c div.c -I ../include/
下一步对这些 .o 文件进行打包,生成 库文件
使用 ar 命令进行生成
ar rcs libsuanshu.a add.o div.o mult.o sub.o
这里是使用 ar 工具进行打包rcs 参数含义如下:
r – 将文件插入备存文件中
c – 建立备存文件
s – 索引
其中libsuanshu 为库文件,表示生成库文件的名称,calc 才是库的名称
使用 mv 命令进行移动,移动到父目录下的lib 文件夹下
mv libsuanshu.a ../lib/
编译运行文件时需要链接头文件和库文件(访问库文件才可以正常访问到各个文件)使用选项 -I 来访问头文件,使用 -L 来访问库文件,-l 表明访问的库文件名称(库文件名称是没有lib的)-o 可以生成运行文件,app 即为运行文件的文件名
gcc main.c -o app -I ./include/ -L ./lib/ -l suanshu
Linux : libxxx.so
在Linux下是一个可执行文件
Windows : libxxx.dll
gcc 得到 .o 文件,得到和位置无关的代码
gcc -c –fpic/-fPIC a.c b.c
gcc 得到动态库
gcc -shared a.o b.o -o libcalc.so
gcc -c -fpic add.c div.c mult.c sub.c
无关位置,生成动态库文件
gcc -shared add.o sub.o mult.o div.o -o libcalc.so
基于以上的目录结构,就可以正常的进行编译运行了
-o 生成名为 app 的可执行文件,-I 包含头文件 ,-L 包含库文件, -l 表明需要的库文件
gcc main.c -o app -I include/ -L lib -l calc
现在可以编译成功,但是还不可以直接运行,如果直接进行运行,就会出现如下报错:
这里涉及动态库的工作原理:
GCC 进行链接时,动态库的代码不会被打包到可执行程序中 ( 静态库中代码打包到可执行程序中 )
所以在使用动态库时,就需要在程序启动之后先把动态库 动态的 加载到内存中,**ldd (list dynamic dependencies)命令检查动态库依赖关系 **
通过报错也可以看到,如果要正常的运行程序,就需要定位共享库文件
此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是由ld-linux.so来完成的,它先后搜索elf文件的 DT_RPATH段 ——> 环境变量LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——> /lib/, /usr/lib目录找到库文件后将其载入内存。
在这其中,可以修改的是环境变量
export LD_LIBRARY_PATH=$LD_LIBRART_PATH:/home/william/clearn/lession04/library/lib
使用 $ 来获取环境变量的值,然后再拼接动态库的地址
但是这种方式只是暂时的,并不是永久的,如果下次再次运行,该环境变量就会失效
进入系统环境变量控制文件,写入环境变量
sudo vim /etc/profile
在最后插入这里的环境变量
export LD_LIBRARY_PATH=$LD_LIBRART_PATH:/home/william/clearn/lession04/library/lib
然后再更新设置就可以了
source/etc/profile
除了这个方法之外,还有可以修改文件列表
我们是无法直接对 /etc/ld.so.cache 文件进行修改的,因为它是二进制文件
可以退而求其次,对 /etc/ld.so.conf 文件进行修改
sudo vim /etc/ld.so.conf
然后进行更新刚刚的设置
sudo ldconfig
这样就完成了修改
要熟悉静态库与动态库,了解各自的优缺点,了解其制作方法。
感谢观赏,一起提高,慢慢变强