一、Native动态库
1、定义
Native动态库,通常指的是由非Java等高级语言(如C、C++等)编写并编译成的动态链接库(Dynamic Link Library,简称DLL,在Windows环境下)或共享对象文件(Shared Object,简称SO,在Linux/Unix环境下)。这些库文件在程序运行时被加载到内存中,供程序中的代码调用。
2、特点
- 运行时加载:与静态库不同,动态库在编译时不会被嵌入到可执行文件中,而是在程序运行时根据需要动态加载。
- 代码共享:动态库可以在多个程序之间共享,从而节省磁盘空间和内存。
- 更新方便:由于动态库是独立于程序存在的,因此更新库文件而不需要重新编译或重新分发整个程序。
- 跨语言支持:Native动态库可以被多种编程语言调用,如Java通过JNI(Java Native Interface)调用C/C++编写的动态库。
3、用途
Native动态库广泛用于需要高性能、底层访问或与特定硬件或系统接口交互的场合。例如:
- 图形渲染和游戏开发中的高性能计算。
- 数据库和操作系统的底层功能实现。
- 跨平台软件开发中的特定平台功能支持。
- 性能敏感的应用程序,如实时交易系统。
4、与Java的交互
在Java中,可以通过JNI(Java Native Interface)与Native动态库进行交互。JNI是Java提供的一种标准编程接口,允许Java代码与其他语言写的代码进行交互。通过JNI,Java可以调用Native动态库中的函数,实现Java代码与本地代码的集成。
5、生成和使用
Native动态库的生成通常使用C/C++等编译型语言,并依赖于特定的编译器和工具链。在Windows上,可以使用Microsoft Visual Studio等工具生成DLL文件;在Linux/Unix上,则可以使用GCC等编译器生成SO文件。
使用Native动态库时,需要在程序中指定动态库的路径,并在需要时加载动态库中的函数。在Java中,这通常通过System.loadLibrary()
方法实现,该方法会加载与平台相关的动态库(如Windows上的DLL或Linux上的SO)。
6、注意事项
- 跨平台兼容性:不同操作系统上的动态库格式和加载方式可能不同,需要为不同的目标平台准备相应的动态库文件。
- 性能优化:虽然Native动态库可以提供高性能的计算能力,但过度使用或不当使用可能会导致性能下降或内存泄漏等问题。
- 安全性考虑:由于Native动态库直接与系统底层交互,因此可能存在安全风险。在调用Native动态库时,需要确保代码的安全性和稳定性。
综上所述,Native动态库是软件开发中一种重要的资源,它提供了跨语言交互、高性能计算和底层访问等能力。然而,在使用Native动态库时需要注意跨平台兼容性、性能优化和安全性等问题。
二、静态库
静态库(Static Library)是一种在程序编译时期被链接到目标程序中的库文件。它是预先编译好的代码集合,通常以.lib
(在Windows平台上)或.a
(在Unix/Linux平台上)为文件扩展名。静态库包含了函数的定义和变量,当程序在编译时链接到静态库时,链接器会将库中的代码复制到生成的可执行文件中。
静态库的主要特点包括:
-
代码复制:静态库中的代码在链接过程中被完整地复制到可执行文件中。这意味着每个使用静态库的程序都会有一份库代码的独立拷贝。
-
独立性:由于静态库中的代码被直接嵌入到可执行文件中,因此生成的可执行文件不依赖于外部的库文件。这使得程序可以在没有静态库文件的系统上独立运行。
-
增加可执行文件大小:由于静态库中的代码被复制到每个使用它的程序中,因此这通常会导致生成的可执行文件比使用动态库时更大。
-
难以更新:如果静态库中的代码需要更新,那么所有使用该库的程序都需要重新编译,因为库代码已经被嵌入到程序中。
-
简单性:静态库的使用相对简单,不需要处理动态链接时的复杂问题,如依赖解析、版本冲突等。
静态库在软件开发中有其特定的应用场景,例如:
- 当程序需要在没有安装库文件的系统上运行时。
- 当对程序的安全性要求较高,不希望依赖外部库文件时。
- 当需要确保程序在所有目标平台上具有相同的行为时(因为静态链接消除了不同平台上库文件之间的差异)。
然而,随着软件规模的增大和复杂性的提高,动态库由于其代码共享、易于更新和维护等优点,逐渐成为更受欢迎的选择。
三、动态库与静态库的区别
它们的主要区别在于它们被链接到程序中的方式以及程序运行时的行为。
3.1静态库
- 静态库是一组预编译的目标文件的集合,其中包含了函数和数据,这些函数和数据可以被程序调用和使用。
- 在编译链接过程中,静态库的内容会被完整地复制到生成的可执行文件中。
- 静态库一旦被链接到程序中,在程序运行时就不会发生变化,因为静态库的代码被完全复制到了可执行文件中。
- 使用静态库的优点是在程序执行时不需要外部依赖,但缺点是会增加可执行文件的大小,而且如果多个程序使用同一个静态库,会造成存储资源的浪费。
3.2动态库
- 动态库也是一组预编译的目标文件的集合,其中包含了函数和数据,同样可以被程序调用和使用。
- 在编译链接过程中,动态库的引用会被放置到生成的可执行文件中,但动态库的实际代码并不会复制到可执行文件中。
- 在程序运行时,可执行文件会在需要的时候加载动态库,并将其映射到内存中,程序可以在运行时动态地调用动态库中的函数。
- 使用动态库的优点是减少了可执行文件的大小,因为动态库的代码是在运行时加载的,多个程序可以共享同一个动态库,节省了存储资源。
- 动态库的缺点是在运行时需要外部依赖,如果动态库缺失或版本不匹配,程序可能无法正常运行。
四、创建静态库
4.1创建
1、编译源代码生成目标文件(.o)在 obj
目录下生成 file1.o
和 file2.o
目标文件。
gcc -c file1.c file2.c -o obj/file1.o obj/file2.o
2、打包为静态库文件(.a)
ar crv libmylib.a obj/file1.o obj/file2.o
在当前目录下生成 libmylib.a
静态库文件,其中 obj/file1.o
和 obj/file2.o
是目标文件。
参数解析:
如果需要将一个目录下的所有 .o
文件打包为一个静态库文件,可以使用通配符 *
,如下:
ar crv libmylib.a obj/*.o
ar:调用归档工具
c
:创建静态库文件
r
:创建或更新静态库文件中的文件
v
:标准输出每个文件的名称,即列出添加到静态库文件中的文件名
4.2 使用静态库文件
在需要使用静态库文件的代码文件中,需要包含头文件(如果有),并在编译时指定静态库文件的路径和名称,如下:
gcc main.c -o main -L. -lmylib
解释一下这个命令:
-L.
:添加当前目录到链接器搜索目录列表,这样链接器就能找到静态库文件。
-lmylib
:指定要链接的库文件,链接器会按照约定的命名方式(libxxx.a
)搜索链接的静态库文件,所以只需要在命令中指定库文件的名称,不需要包括.a
扩展名。
如果需要将静态库文件安装到系统目录下供其他程序使用,可以将库文件复制到 /usr/lib
或 /usr/local/lib
目录下,并使用 ldconfig
命令更新动态链接库缓存:
sudo cp libmylib.a /usr/lib
sudo ldconfig
这样其他程序就可以直接使用 libmylib.a
库文件了。
4.3静态库的示例
当然,让我们通过一个简单的示例来详细说明如何在Linux环境下创建一个C语言的静态库,并在另一个程序中使用这个静态库。我们将创建一个静态库,该库包含两个函数:一个用于计算两数之和,另一个用于计算两数之差。
步骤1:编写源代码
首先,我们创建两个C源文件,分别实现加法和减法功能。
// add.c
int add(int a, int b) {
return a + b;
}
// subtract.c
int subtract(int a, int b) {
return a - b;
}
步骤2:编译源文件为对象文件
接下来,我们需要将这两个源文件编译成对象文件(.o文件)。在终端中执行以下命令:
gcc -c add.c
gcc -c subtract.c
这将生成 add.o 和 subtract.o 两个对象文件。
步骤3:创建静态库
然后,我们使用ar命令将这两个对象文件打包成一个静态库文件。假设我们想命名为 libmathlib.a:
ar rcs libmathlib.a add.o subtract.o
步骤4:编写使用静态库的程序
现在,我们创建一个主程序 main.c,它将使用我们刚创建的静态库中的函数:
// main.c
#include <stdio.h>// 前向声明,告诉编译器这些函数存在于某个地方
int add(int, int);
int subtract(int, int);int main() {
int x = 5;
int y = 3;
printf("Sum: %d\n", add(x, y));
printf("Difference: %d\n", subtract(x, y));
return 0;
}
步骤5:编译并链接静态库
最后,我们编译 main.c 并链接到我们的静态库。这里需要注意的是,因为我们没有安装静态库到系统标准路径,所以需要指定库的路径以及库名(去掉前缀 lib 和后缀 .a):
gcc main.c -L. -lmathlib -o my_program
这里的 -L. 表示在当前目录下寻找库,-lmathlib 指定了要链接的库名为 mathlib。
步骤6:运行程序
编译完成后,直接运行生成的可执行文件即可:
./my_program
输出应该是:
Sum: 8
Difference: 2
这样,我们就成功地创建了一个静态库,并在一个简单的程序中使用了它。
五、创建动态库
5.1创建
动态库(也称为共享库)与静态库不同,它在运行时被动态加载到内存中,因此可以被多个程序共享,从而减少内存的使用,同时也方便了更新和维护。下面是创建动态库的基本步骤:
1.编译源代码生成目标文件(.o)
gcc -fPIC -c file1.c file2.c -o obj/file1.o obj/file2.o
-fPIC
参数告诉编译器生成位置独立的代码,这是创建动态库所必需的。
2 打包为动态库文件(.so)
gcc -shared -o libmylib.so obj/file1.o obj/file2.o
这个命令将目标文件链接成一个名为libmylib.so的动态库文件。
5.2 使用动态库文件
在需要使用动态库文件的代码文件中,需要包含头文件(如果有),并在编译时指定动态库文件的路径和名称,如下:
gcc main.c -o main -L. -lmylib
参数
-L.
和-lmylib
的含义与静态库的使用方式相同,都是指定库文件的路径和名称。
如果需要将动态库文件安装到系统目录下供其他程序使用,可以将库文件复制到 /usr/lib
或 /usr/local/lib
目录下,并更新动态链接库缓存:
sudo cp libmylib.so /usr/lib
sudo ldconfig
这样其他程序就可以直接使用 libmylib.so
库文件了。
5.3动态库的示例
动态库的创建和使用过程与静态库类似,但涉及到一些不同的编译和链接步骤,以确保库能够在运行时被加载。下面是在Linux环境下创建一个简单的C语言动态库,并在另一个程序中使用它的步骤。
步骤1:编写源代码
首先,我们同样创建两个C源文件,分别实现加法和减法功能。
// add.c
int add(int a, int b) {
return a + b;
}
// subtract.c
int subtract(int a, int b) {
return a - b;
}
步骤2:编译源文件为共享对象文件
与静态库不同,这里我们需要直接编译成共享对象(.so)文件。使用-fPIC选项生成位置无关代码,并使用-shared选项创建动态库。
gcc -fPIC -c add.c
gcc -fPIC -c subtract.c
gcc -shared -o libmathlib.so add.o subtract.o
这将生成名为 libmathlib.so 的动态链接库。
步骤3:编写使用动态库的程序
接下来,我们修改之前的 main.c 文件,以便使用动态库。注意,动态库的使用在代码层面与静态库相同,区别在于编译和链接阶段。
// main.c
#include <stdio.h>// 前向声明
int add(int, int);
int subtract(int, int);int main() {
int x = 5;
int y = 3;
printf("Sum: %d\n", add(x, y));
printf("Difference: %d\n", subtract(x, y));
return 0;
}
步骤4:编译并链接动态库
编译时,我们需要使用 -L 来指定动态库所在的目录,以及 -l 来指定库名(不包括 lib 前缀和 .so 后缀)。但是,直接这样编译可能找不到库,因为动态加载发生在运行时,所以我们通常只编译不链接:
gcc main.c -o my_program -L. -lmathlib
为了运行时能够找到动态库,有几种方法:
将动态库复制到系统库目录,如 /usr/lib 或 /usr/local/lib。
设置环境变量 LD_LIBRARY_PATH 包含库文件的路径。
这里我们使用第二种方法,假设库就在当前目录下:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
或export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
步骤5:运行程序
设置好环境变量后,就可以运行程序了:
./my_program
输出与之前相同:
Sum: 8
Difference: 2
这样,我们就完成了动态库的创建和使用过程。动态库的优势在于,多个程序可以共享同一份库的代码和数据,节省内存;且更新库时不需要重新编译链接依赖它的应用程序。
原文链接:https://blog.csdn.net/qq_51372804/article/details/136554480