了解如何使用静态库将多个 C 对象文件组合成一个可执行文件。
使用 C 编写的应用程序代码通常有多个源文件,但最终您需要将它们编译成一个可执行文件。
您可以通过两种方式做到这一点:通过创建静态库或动态库(也称为共享库)。这两种类型的库在创建和链接方式方面有所不同。您选择使用哪个取决于您的用例。
在本文中,我将解释如何创建静态链接的可执行文件。
使用带有静态库的链接器
链接器是将程序的多个部分组合在一起并重新组织它们的内存分配的命令。
链接器的功能包括:
- 整合程序的所有部分
- 找出一个新的记忆组织,使所有的部分组合在一起
- 恢复地址以便程序可以在新的内存组织下运行
- 解析符号引用
作为所有这些链接器功能的结果,创建了一个称为可执行文件的可运行程序。
通过将程序中使用的所有必要的库模块复制到最终的可执行映像中来创建静态库。链接器链接静态库作为编译过程的最后一步。可执行文件是通过解析外部引用、将库例程与程序代码相结合来创建的。
创建目标文件
下面是一个静态库的示例,以及链接过程。首先,使用这些函数签名创建头文件mymath.h:
int add(int a, int b);
int sub(int a, int b);
int mult(int a, int b);
int divi(int a, int b);
使用以下函数定义创建add.c、sub.c和:mult.cdivi.c
// add.c
int add(int a, int b){
return (a+b);
}
//sub.c
int sub(int a, int b){
return (a-b);
}
//mult.c
int mult(int a, int b){
return (a*b);
}
//divi.c
int divi(int a, int b){
return (a/b);
}
现在生成目标文件add.o, sub.o, mult.o, divi.o,并使用 GCC:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ gcc -c add.c sub.c mult.c divi.c
该-c选项跳过链接步骤并仅创建目标文件。
创建一个名为 的静态库libmymath.a,然后删除不再需要的目标文件。(请注意,使用比rm命令使用更安全的trash命令 。)
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ ar rs libmymath.a add.o sub.o mult.o divi.o
$ trash *.o
$ ls
add.c divi.c libmymath.a mult.c mymath.h sub.c
您现在已经创建了一个名为 的简单示例数学库libmymath,您可以在 C 代码中使用它。当然,那里有非常复杂的 C 库,这是他们的开发人员用来生成最终产品的过程,您和我安装这些产品以用于 C 代码。
接下来,在一些自定义代码中使用您的数学库,然后链接它。
创建静态链接的应用程序
假设你写了一个数学命令。创建一个名为mathDemo.c的文件并将以下代码粘贴到其中:
#include <mymath.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x, y;
printf("Enter two numbers\n");
scanf("%d%d",&x,&y);
printf("\n%d + %d = %d", x, y, add(x, y));
printf("\n%d - %d = %d", x, y, sub(x, y));
printf("\n%d * %d = %d", x, y, mult(x, y));
if(y==0){
printf("\nDenominator is zero so can't perform division\n");
exit(0);
}else{
printf("\n%d / %d = %d\n", x, y, divi(x, y));
return 0;
}
}
请注意,第一行是include按名称引用您自己的libmymath库的语句。
从mathDemo.c创建一个名为mathDemo.o的目标文件:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ gcc -I . -c mathDemo.c
该-I 选项告诉 GCC 搜索其后列出的头文件。在这种情况下,您指定的是当前目录,由一个点 ( .) 表示。
链接mathDemo.o以libmymath.a创建最终的可执行文件。有两种方法可以向 GCC 表达这一点。
您可以指向文件:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ gcc -static -o mathDemo mathDemo.o libmymath.a
或者,您可以指定库路径以及库名称:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ gcc -static -o mathDemo -L . mathDemo.o -lmymath
在后一个示例中,该-lmymath选项告诉链接器将存在的目标文件libmymath.a与目标文件链接mathDemo.o以创建最终的可执行文件。该-L选项指示链接器在以下参数中查找库(类似于您将使用的-I)。
分析结果
使用file命令确认它是静态链接的:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ file mathDemo
mathDemo: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=c43a2cdedc68087433caf94b67ae079a02bc0bc9, for GNU/Linux 3.2.0, not stripped
使用ldd命令,可以看到可执行文件没有动态链接:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ ldd ./mathDemo
不是动态可执行文件
您还可以检查可执行文件mathDemo的大小:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ du -h ./mathDemo
984K ./mathDemo
运行命令查看它的工作情况:
linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
⚡ ./mathDemo
Enter two numbers
20
5
20 + 5 = 25
20 - 5 = 15
20 * 5 = 100
20 / 5 = 4
何时使用静态链接
动态链接的可执行文件通常比静态链接的可执行文件更受欢迎,因为动态链接使应用程序的组件保持模块化。如果库收到重要的安全更新,它可以很容易地修补,因为它存在于使用它的应用程序之外。
当您使用静态链接时,库的代码会“隐藏”在您创建的可执行文件中,这意味着修补它的唯一方法是在每次库获得更新时重新编译和重新发布新的可执行文件 - 你有更好的与你的时间有关的事情,相信我。
但是,如果库的代码存在于与使用它的可执行文件相同的代码库中,或者存在于预期不会接收更新的专用嵌入式设备中,则静态链接是一个合理的选择。