Linux练级宝典->动态库和静态库

server/2025/3/13 16:06:11/

动静态库的原理

我们知道可执行文件前的4步骤 预编译->编译->汇编->链接

  • 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件。
  • 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件。
  • 汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件。
  • 链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序。

例如,我们上面用5个文件组合出了可执行文件。.0文件就是汇编后的文件。

 我们在最后一步的时候不链接,而是把这些.o文件打包,

这样的一堆.o文件组合到一起就是一个库了。 

认识动静态库

#include <stdio.h>int main()
{printf("hello world\n"); //库函数return 0;
}

上述代码我们用了一个库函数printf。

生成后,我们用ldd查看一个可执行程序依赖的库文件。

libc.so.6就是依赖的文件。

我们找到上面的文件目录,我们看到我们上面的连接其实就是一个软连接。 

我们可以查查看这个库文件

我们可以看到他是一个shared object。

 shared object说明这就是一个共享的目标文件库,准确来说,还是一个动态库

  • linux中 以.so为后缀的是动态库,以.a为后缀的是静态库
  • Windows中,以.dll为后缀的是动态库,以.lib为后缀的是静态库。

这里可执行程序锁依赖的.so文件就是动态库,gcc/g++默认的是动态链接,若想进行静态链接,可以加一个-static选项。

gcc -o mytest-s mytest.c -static

可以看到我们静态库 的大小远远大于可执行文件。

我们查看二者的情况,一个是dynamically 动态链接 一个是 statically 静态链接。

 动静态库各自的特征

静态库

静态库是在编译链接时期,直接把库的代码复制一份到可执行文件中,生成的可执行程序在运行的时候不再需要静态库,所以里面多了一个库的代码所以肯定会大。 

优点:

静态库,只要生成可执行程序后,这个程序就可以独立运行了,静态库就没用了

缺点:

由上面也可以看出静态库生成的代码会占用大量的空间,并且如果这个库被多个程序使用,就会导致每个程序都多一个静态库代码。代码还都是重复的

动态库

 动态库:就是在程序运行时,在共享区中找到对应的动态库,此时去动态库中调用代码。

动态库就相当于,一开始就在物理内存加载好一份,然后程序调用对应代码时,通过虚拟内存和物理内存映射的方式,找到对应的函数。

优点:

节省空间,多个库调用的都是在物理内存中的同一个库,即同一个代码。

缺点:

必须依赖动态库,否则无法运行。

静态库的打包与使用

 我们写4个文件,add.h和add.c和sub.h和sub.c都是很简单的代码

就是一个头文件,一个原文件,里面就一个加减函数。头文件声明这个函数。

1.生成.o文件。

 生成两个.o文件 gcc -c选项。

2.使用ar命令将所有目标文件打包为静态库

ar指令常用于将目标文件打包为静态库

-r: 若静态库文件中的目标文件有更新,则用新的目标文件替换为旧的目标文件(replace)

-c:建立静态库文件(create)

ar -rc libcal.a add.o sub.o

 -t:列出静态库的文件

-v:显示详细信息。

 3.把头文件和生成的静态库组织起来

当我们把自己的库给别人用的时候,可以看到上面我们有一个静态库文件和剩下的.h的文件,所以我们就是要把头文件和.o的静态库文件给到别人就可以。

makefile文件:

mylib=libcal.a
CC=gcc$(mylib) : add.o sub.oar -rc -o $(mylib) $^
%.o:%.c$(CC) -c $<.PHONY:clean
clean:rm -f $(mylib) ./*.o.PHONY:output
output:mkdir -p mathlib/includemkdir -p mathlib/libcp ./*.h mathlib/includecp ./*.a mathlib/lib

 make编译文件,makeout将文件放入mathlib文件夹中。

此时就是把mathlib给到别人,此时别人有静态库和头文件.h,只差最后一步链接即可使用。

使用静态库

创建一个源文件main.c,然后使用库函数。

#include <stdio.h>
#include <add.h>int main()
{int x = 20;int y = 10;int z = my_add(x, y);printf("%d + %d = %d\n", x, y, z);return 0;
}

此时我们的文件夹里只有两个文件,main.c和我们打包的静态库

 方法一:使用选项

 此时使用gcc编译main.c文件生成可执行程序就需要携带三个选项了

  • -I : 指定头文件的搜索路径
  • -L:指定库文件的搜索路径
  • -l:指明需要链接库文件路径下的哪一个库

指定库代码如下:

gcc main.c -I ./mathlib/include/ -L ./mathlib/lib/ -lcal

 结果如下:

 为什么要指定搜索文件?

我们知道操作系统中有环境变量这个说法,所以我们在使用一些系统的库的时候,没有指定路径,而是直接使用,而我们自己定义的库没在环境变量下,此时使用就得带上指定的路径,让系统去找。当然也可以把这个路径放到环境变量下。

1.指定头文件的路径,是找到头文件->找到声明。

2.指定库的路径,是找到库->找到定义。

3.因为我们库可能不只是一个,所以我们要指定需要找到哪个库文件,所以-lcal就是指定名字。

4.上述三个选项后可以加空格也可以不加。

方法二:把头文件和库文件塞到对应的系统路径下

 就是进入环境变量的意思,放到系统路径后就能让操作系统自动找到对应库了。

将库文件拷贝进系统路径下。 

sudo cp mathlib/lib/libcal.a /lib64/
sudo cp mathlib/include/* /usr/include/

 结果如下:

我们发现我们把文件放到系统路径后,只用指定库的名字就完成了调用。 

实际我们把头文件和库文件塞到系统路径就是安装库的过程,但不推荐将自己写的头文件和库文件拷贝到系统路径下,这样其实是造成污染的。

动态库的打包与使用

打包

动态库和静态库都是库,但是二者在打包时有一点点区别。我们还是使用上述的4个文件进行打包

 1.还是让源文件生成.o文件

这里生成文件时,需要多加一步操作:

带上选项 -fPIC(position independent code):产生位置无关码。

gcc -fPIC -c add.c sub.c

选项说明:

-c 生成.o文件。

-fPIC :生成位置无关码。告诉编译器产生位置无关码,给动态库使用

2.使用-shared选项将所有目标文件打包为动态库

和静态库ar 指令不同,动态库的生成用 gcc 带 -shared选项。 

gcc -shared -o libcal.so add.o sub.o

我们发现此时生成的动态库的大小 << 静态库的大小。 

3.将头文件和生成的静态库组织到一个文件夹下面

 使用makefile把文件组织起来

原始步骤就是

我们用makefile将指令操作汇总一下:

mylib=libcal.so%.o:%.cgcc -fPIC -c $<
$(mylib):add.o sub.ogcc -shared -o $(mylib) $^.PHONY:clean
clean:rm -rf $(mylib) ./*.o.PHONY:output
output:mkdir -p mlib/includemkdir -p mlib/libcp ./*.h mlib/includecp ./*.so mlib/lib

注意:makefile的缩进要用tab键,用空格键可能会出错。

 结果如下:

 此时继续使用main.c测试整个库代码。

#include <stdio.h>
#include <add.h>int main()
{int x = 20;int y = 10;int z = my_add(x, y);printf("%d + %d = %d\n", x, y, z);return 0;
}
 gcc main.c -I./mlib/include -L./mlib/lib -lcal

使用指令后,生成可执行文件。

我们发现文件不能执行。

我们发现我们的动态库文件是not found的。

方法一:拷贝.so文件到系统共享路径下

 这里就是让你把.so拷贝到和 c库文件同一个目录下,此时操作系统就会自动找到库了。

sudo cp ./libcal.so /lib/x86_64-linux-gnu/

 方法二:更改LD_LIBRARY_PATH

 LD_LIBRARY_PATH是一个环境变量,这个环境变量是程序运行时要搜索的路径。所以把动态库所在的目录添加到LD_LIBRARY_PATH即可。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/~/blog-code/DSL-D

 

我们看到.so上面也有一串地址,说明当前库是找到了。

 方法三:配置/etc/ld.so.conf.d/

这个目录下面放的都是以.conf为后缀的配置文件,这些配置文件就是一堆路径,和环境变量一样,系统会自动进行查找。

echo ~/blog-code/DSL-D/mlib/lib > aron.conf
sudo cp aron.conf /etc/ld.so.conf.d/

上述两个命令:第一个是将上面的地址打印到aron.conf中

第二个是把aron.conf放到上述目录中。

sudo ldconfig

使用上述指令刷新配置文件。

此时执行文件


http://www.ppmy.cn/server/174658.html

相关文章

《深度解析DeepSeek-M8:量子经典融合,重塑计算能效格局》

在科技飞速发展的今天&#xff0c;量子计算与经典算法的融合成为了前沿领域的焦点。DeepSeek-M8的“量子神经网络混合架构”&#xff0c;宛如一把钥匙&#xff0c;开启了经典算法与量子计算协同推理的全新大门&#xff0c;为诸多复杂问题的解决提供了前所未有的思路。 量子计算…

MySQL中 IN 到底走不走索引?

文章目录 前言数据库表结构查询sqlEXPLAIN介绍EXPLAIN 的输出每列解释 强制走索引查询时添加条件(复合索引字段)查询小时查询分钟 总结 前言 在 MySQL 中&#xff0c;IN 语句是否能够利用索引取决于多个因素&#xff0c;包括但不限于查询的具体形式、表的统计信息、索引的选择…

使用expect工具实现远程批量修改服务器密码

使用expect工具实现远程批量修改服务器密码 linux服务器安装Expect工具 1、首先查看系统中是否有安装expect。 # whereis expect 2、Expect工具是依赖tcl的&#xff0c;需要先安装tcl #wget https://sourceforge.net/projects/tcl/files/Tcl/8.4.19/tcl8.4.19-src.tar.gz …

LuaJIT 学习(1)—— LuaJIT介绍

文章目录 介绍Extensions Modulesbit.* — Bitwise operationsffi.* — FFI libraryjit.* — JIT compiler controlC API extensionsProfiler Enhanced Standard Library Functionsxpcall(f, err [,args...]) passes arguments例子&#xff1a; xpcall 的使用 load*() handle U…

利用java实现数据分析

1 问题 在日常生活中&#xff0c;对于数据的处理&#xff0c;为了使数据更加直观&#xff0c;我们可以使用柱状图&#xff0c;饼图&#xff0c;折线图等来呈现&#xff0c;同时也可以对数据直接进行一些处理&#xff0c;那怎样用java来处理这类问题呢&#xff1f; 2 方法 代码清…

emacs使用mongosh的方便工具发布

github项目地址: GitHub - csfreebird/emacs_mongosh: 在emacs中使用mongosh快速登录mongodb数据库 * 用途 在emacs中使用mongosh快速登录mongodb数据库&#xff0c; 操作方法: M-x mongosh, 输入数据库名称&#xff0c;然后就可以自动登录&#xff0c;前提是你已经配置好了…

蓝桥杯省赛真题C++B组2024-握手问题

一、题目 【问题描述】 小蓝组织了一场算法交流会议&#xff0c;总共有 50 人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手(且仅有一次)。但有 7 个人&#xff0c;这 7 人彼此之间没有进行握手(但这…

UI自动化测试 —— web端元素获取元素等待实践!

前言 Web UI自动化测试是一种软件测试方法&#xff0c;通过模拟用户行为&#xff0c;自动执行Web界面的各种操作&#xff0c;并验证操作结果是否符合预期&#xff0c;从而提高测试效率和准确性。 目的&#xff1a; 确保Web应用程序的界面在不同环境(如不同浏览器、操作系统)下…