目录
认识动静态库
一个程序编译为可执行程序需要经历四个步骤:
-
预处理: 在此阶段,源代码会被预处理器处理。预处理器会执行如移除注释、展开宏定义、处理条件编译指令等任务。生成一个修改过的源代码文件,通常以.i(对于C语言)或.ii(对于C++)作为扩展名。
-
编译: 编译器接着对预处理后的源代码进行词法分析、语法分析、语义分析,并进行优化。这一系列过程将高级语言代码转换为汇编代码,文件拓展名为.s或.asm
-
汇编: 汇编器将汇编代码转换为目标代码,也就是机器语言,每个指令对应一条机器语言指令。这些指令是二进制格式的,可以直接被CPU执行。此步骤产生的文件是目标文件,常见扩展名为.o。
-
链接: 链接器将一个或多个目标文件以及需要的库文件组合起来,生成一个可执行文件。可执行文件包含了程序执行所需的所有代码和数据,操作系统可以直接加载并运行它。
库文件实际上就是已经编译和链接的代码集合,它们包含预先编译好的函数和数据,可供其他程序在链接阶段使用。库文件主要分为动态库与静态库,它们在链接和运行时的行为上有显著的不同。
静态库
静态库通常以 .a(Linux) 或 .lib(Windows) 作为文件扩展名。程序在编译链接的时候把库的代码链接到可执行文件中,即静态链接,链接后程序运行的时候将不再需要静态库。
动态库
动态库通常以 .so(Linux) 或 .dll(Windows) 作为文件扩展名。与静态库不同,动态库在程序运行时被加载,而不是在编译链接时。所以程序在运行时才需要动态库。
当程序启动时,动态库代码会被加载至内存,映射至进程地址空间的共享区,允许多个进程共享同一份库代码,程序中的函数调用会被解析为动态库中的函数地址。
动静态库的优缺点
动态库的优点
- 节省磁盘空间:多个程序可以共享同一个动态库文件,减少了磁盘空间的使用。
- 节省内存:如果多个进程使用了同一个动态库,操作系统可以确保所有进程共享同一份库的内存副本,节省内存资源。
- 易于更新:开发者可以独立于应用程序更新动态库,用户只需要替换库文件而无需重新安装应用程序。
- 版本控制:可以同时存在多个版本的动态库,便于过渡和兼容性管理。
动态库的缺点
- 依赖性:程序运行时需要动态库,如果库文件缺失或版本不兼容,程序可能无法运行。
- 安全风险:如果动态库被恶意篡改,会带来安全风险。
- 兼容性问题:不同版本的动态库可能不兼容,需要仔细管理库的版本。
静态库的优点
- 部署简单:静态库在编译时被整合到可执行文件中,部署时不需要额外的库文件。
- 性能:由于所有必要的代码都包含在可执行文件中,程序启动和运行更快,省去了加载外部库的步骤。
- 兼容性:不存在运行时找不到库或库版本不兼容的问题。
- 安全性:因为库代码是编译进可执行文件的,所以更不容易被篡改。
静态库的缺点
- 增加可执行文件大小:静态库的代码会被复制到每个使用它的可执行文件中,导致文件大小增加。
- 更新不便:如果库的代码需要更新,所有使用该库的可执行文件都需要重新编译。
- 磁盘空间:如果多个程序使用相同的静态库,相同的代码会被多次复制到不同的可执行文件中,浪费磁盘空间。
- 内存使用:如果多个进程加载了包含相同静态库的可执行文件,相同的代码会被多次加载到内存中,浪费内存资源。
创建与使用动静态库
以上面五个文件为例
main.c
#include<stdio.h>
#include<add.h>
#include<sub.h>
int main()
{
int x=9;
int y=3;
printf("x+y=%d\n",add(x,y));
printf("x-y=%d\n",sub(x,y));
return 0;
}
add.h
extern int add(int x,int y);
sub.h
extern int sub(int x,int y);
add.c
int add(int x,int y)
{
return x+y;
}
sub.c
int sub(int x,int y)
{
return x-y;
}
我们将 add.c ,add.h ,sub.c , sub.h 打包为一个库文件
静态库的创建
首先我们需要将源文件编译为 .o文件,我们使用gcc命令编译文件
gcc -c source.c -o source.o
现在我们有了所有需要的.o文件,您可以使用ar命令创建静态库。
ar rcs libmylib.a source.o
可以看到当前目录下生成了一个库文件。
我们可以用ar命令的-t选项和-v选项查看静态库当中的文件。
如果有更多文件要添加到库中,可以使用ar命令的q选项。
ar q libmylib.a another_source.o
如果我们要使用一个库时,必须有两个文件,一个是库的头文件,一个是库文件。
我们将编写的库文件与头文件单独存放在一个目录里。
静态库的使用
我们编译main.c,可以看到gcc报错了。
main.c:2:16: fatal error: add.h: No such file or directory #include<add.h> ^ compilation terminated.
这个错误信息指出编译器在编译 main.c 文件时尝试包含一个名为 add.h 的头文件,但是在指定的搜索路径中没有找到这个文件。错误发生在 main.c 文件的第二行。
要想让编译器找到头文件与库文件,我们需要指定头文件和库文件所在的路径。
- -I: 指定头文件路径。
- -L:指定库文件路径。
- -l: 指明库文件路径下需要链接的库
现在让我们再试一次。
gcc -o test main.c -I ./include -L ./lib -l mymath
可以看到程序成功编译并运行了。 那为什么我们平时使用c标准库时不需要指定呢?
C标准库的头文件与库文件已经安装在系统的默认包含路径和默认库路径中。编译器在编译时会自动搜索这些默认路径,因此不需要使用选项来指定。
所以我们还可以使用另一种方式,即将文件拷贝至默认路径。
可以看到同样成功了。 需要注意的是此时同样需要指定库名称。那为什么c标准库不需要呢?因为C标准库是每个C程序的基础依赖,为了保持兼容性和简化编译过程,编译器和链接器会自动处理标准库的链接。
动态库的创建
动态库的创建与静态库相似
同样是这五个文件,第一步生成.o文件。
与静态库不同这里我们需要添加 -fpic 选项生成位置无关代码。
位置无关代码是一种编译技术,它允许编译后的程序或库在内存中的任何位置执行而无需修改。这种代码不依赖于固定的内存地址,而是使用相对于程序加载基址的偏移量或其他动态计算的地址来访问数据和代码。它具有重定位能力,可以在多个进程间共享,节省内存并提高效率。通常用于创建动态链接库,因为这些库可以在不同的程序中重用,而不受加载地址的影响。编译时需要特定的编译器选项(如gcc中的-fPIC)来生成。
接下来我们使用gcc创建动态库。
gcc -shared -o libmymath.so add.o sub.o
同样我们将编写的库文件与头文件单独存放在一个目录里。
动态库的使用
与使用静态库相同,我们同样需要指定库文件和头文件的搜索路径。
我们发现程序不能直接执行并且报了错。这个错误信息表明程序在运行时尝试加载一个名为 libmymath.so 的动态库,但是没有找到这个库文件。我们在这里介绍两种方法
- 将文件拷贝至库文件的默认搜索路径
- 修改 LD_LIBRARY_PATH 条件变量。
拷贝文件到系统路径下
sudo cp ./libmymath.so /lib64
可以看到成功运行了。
修改条件变量
可以通过设置 LD_LIBRARY_PATH 环境变量来添加库文件的路径。 LD_LIBRARY_PATH 是一个在Linux上使用的环境变量,它为动态链接器提供一个搜索动态库的附加路径列表。当程序运行时,动态链接器会使用这个环境变量来查找程序依赖的动态库文件
export LD_LIBRARY_PATH=/home/user/test/ku/lib/libmymath:$LD_LIBRARY_PATH
可以看到程序成功运行了。
标签:文件,编译,静态,代码,int,动态 From: https://blog.csdn.net/2301_80926085/article/details/138914618