跳转到内容

制作和使用静态库动态库

使用一个虚构的例子来讲解。

系统: Debian GNU/Linux 12 (bookworm) 编译器: g++ (Debian 12.2.0-14) 12.2.0

基础

现在我想要编写一个程序,从命令行读取数字,计算该数字的平方根,并打印出结果。程序名字为calcSqrt,假想的使用效果如下:

$ ./calcSqrt 9
The square root of 9 is 3

为了实现该程序,我编写了一个数学函数库MathFunctions,程序 calcSqrt 调用其中的函数 MathFunctions::sqrt() 来计算平方根。

根据上述描述,最终得到的代码如下:

calcSqrt.cpp

#include <iostream>
#include "MathFunctions/MathFunctions.h"
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
const double inputValue{std::stof(argv[1])};
const double result{mathfunctions::sqrt(inputValue)};
std::cout << "The square root of " << inputValue << " is " << result << std::endl;
return 0;
}

MathFunctions/MathFunctions.h

#pragma once
namespace mathfunctions {
double sqrt(double x);
}

MathFunctions/MathFunctions.cpp

#include "MathFunctions.h"
#include <cmath>
namespace mathfunctions {
double sqrt(double x) {
return std::sqrt(x);
}
} // namespace mathfunctions

编译运行程序:

$ g++ -o calcSqrt calcSqrt.cpp MathFunctions/MathFunctions.cpp
$ ./calcSqrt
Usage: ./calcSqrt number
$ ./calcSqrt 9
The square root of 9 is 3

非常完美地实现了想要的效果。

静态库

现在我想要把MathFunctions制作成静态库,原因是这个库里含有商业机密,我想要的效果是,在我分发给别人后,别人只能使用该库,而无法看到实现代码。

制作静态库

通用的“公式”为:g++ -c -o lib库名.a 代码文件列表

g++ -c -o libMathFunctions.a MathFunctions/MathFunctions.cpp

运行上述命令,得到静态库libMathFunctions.a

各选项解释如下:

-c: 告诉编译器只进行编译,而不进行链接。 -o: 指定生成的目标文件的文件名。

使用静态库

通用的“公式”为:g++ 选项 代码文件列表 -l 库名 -L 库文件所在目录名

g++ -o calcSqrt calcSqrt.cpp -l MathFunctions -L /home/aoyu/math_sqrt/

运行上述命令,成功生成目标程序calcSqrt,和前面一样。

动态库

使用静态库,生成的目标程序二进制文件包含静态库的代码,程序体积会比较大。如果有多个程序都使用某个静态库,那么在这些程序中就会存在多份该库的拷贝,有点浪费空间。如果库代码更新了,使用该静态库的程序也需要重新编译,有点麻烦。

如果使用动态库,就没有这样的烦恼了。如果一个程序使用了一个动态库,在该程序运行的时候才会链接到该库。

制作动态库

通用的“公式”为:g++ -fPIC -shared -o lib库名.so 代码文件列表

g++ -fPIC -shared -o libMathFunctions.so MathFunctions/MathFunctions.cpp

运行上述命令,得到动态库libMathFunctions.so

各选项解释如下:

-fPIC: 告诉编译器生成位置无关代码(Position Independent Code)。 -shared: 告诉编译器生成一个共享库。

使用动态库

命令和使用静态库时一样。通用的“公式”为:g++ 选项 代码文件列表 -l 库名 -L 库文件所在目录名

g++ -o calcSqrt calcSqrt.cpp -l MathFunctions -L /home/aoyu/math_sqrt/

运行上述命令,成功生成目标程序calcSqrt,和前面一样。

运行生成的目标程序calcSqrt,有如下提示:

$ ./calcSqrt
./calcSqrt: error while loading shared libraries: libMathFunctions.so: cannot open shared object file: No such file or directory

意思是找不到动态库文件libMathFunctions.so在哪。设置环境变量LD_LIBRARY_PATH

$ echo $LD_LIBRARY_PATH
$ LD_LIBRARY_PATH="/home/aoyu/math_sqrt:$LD_LIBRARY_PATH"
$ echo $LD_LIBRARY_PATH
/home/aoyu/math_sqrt:

然后再次运行程序,这次成功运行:

$ ./calcSqrt
Usage: ./calcSqrt number
$ ./calcSqrt 9
The square root of 9 is 3

疑问

既然使用动态库和使用静态库的命令一样,那么,如果同时存在动态库和静态库,程序是会使用动态库,还是会使用静态库呢?也就是,如果同时存在libMathFunctions.solibMathFunctions.a,程序会链接哪个?

答案是,程序会优先使用动态库。

通过比较编译生成的程序文件大小来证明:

# 静态库
$ g++ -c -o libMathFunctions.a MathFunctions/MathFunctions.cpp
$ g++ -o calcSqrt calcSqrt.cpp -l MathFunctions -L /home/aoyu/math_sqrt/
$ ls -la calcSqrt
-rwxr-xr-x 1 aoyu aoyu 25296 4月10日 20:04 calcSqrt
$ rm calcSqrt libMathFunctions.a
# 动态库
$ g++ -fPIC -shared -o libMathFunctions.so MathFunctions/MathFunctions.cpp
$ g++ -o calcSqrt calcSqrt.cpp -l MathFunctions -L /home/aoyu/math_sqrt/
$ ls -la calcSqrt
-rwxr-xr-x 1 aoyu aoyu 25216 4月10日 20:06 calcSqrt
$ rm calcSqrt libMathFunctions.so
# 同时存在动态库和静态库
$ g++ -c -o libMathFunctions.a MathFunctions/MathFunctions.cpp
$ g++ -fPIC -shared -o libMathFunctions.so MathFunctions/MathFunctions.cpp
$ g++ -o calcSqrt calcSqrt.cpp -l MathFunctions -L /home/aoyu/math_sqrt/
$ ls -la calcSqrt
-rwxr-xr-x 1 aoyu aoyu 25216 4月10日 20:07 calcSqrt

观察到,当同时存在动态库和静态库时,生成的可执行程序calcSqrt的文件大小与使用动态库时生成的可执行程序文件大小相同。证明,当同时存在动态库和静态库时,编译器优先使用动态库。