为什么要从 .o 文件创建 .a 文件进行静态链接?

25

考虑以下代码:

one.c:

#include <stdio.h>

int one() {
   printf("one!\n");
   return 1;
}

two.c:

#include <stdio.h>

int two() {
   printf("two!\n");
   return 2;
}

prog.c

#include <stdio.h>

int one();
int two();

int main(int argc, char *argv[]) 
{
   one();
   two();

   return 0;
}

我想将这些程序链接起来。所以我这样做:

gcc -c -o one.o one.c
gcc -c -o two.o two.c
gcc -o a.out prog.c one.o two.o

这个很好用。

或者我可以创建一个静态库:

ar rcs libone.a one.o
ar rcs libtwo.a two.o
gcc prog.c libone.a libtwo.a
gcc -L. prog.c -lone -ltwo

我的问题是:为什么我要使用第二种版本——创建“.a”文件,而不是链接我的“.o”文件?它们似乎都是静态链接,所以一个与另一个相比是否有优势或架构上的区别?

6个回答

48

通常库是由多个目标文件组成的集合,可以在多个程序中使用。

在您的示例中没有优势,但您可能已经这样做了:

ar rcs liboneandtwo.a one.o two.o

那么连接您的程序就变得更简单了:

gcc -L. prog.c -loneandtwo

这实际上是一个打包的问题。你是否有一组对象文件,它们自然地形成了一组相关的功能,并且可以在多个程序中重复使用?如果是这样,那么它们可以合理地被归档为一个静态库,否则可能没有任何优势。

最终链接步骤有一个重要的区别。您链接的任何对象文件都将包含在最终程序中。在库中的对象文件仅在它们可以帮助解决其他对象文件中的未定义符号时才会被包含在最终可执行文件中。如果不行,它们将不会被链接到最终可执行文件中。


12

区别在于可执行文件的大小,但对于你的示例可能并非如此。

当链接到库时,仅包含可执行文件使用的位。当链接对象文件时,整个文件都会被引入。

例如,如果您的可执行文件必须包含数学库中的每个数学函数,而您仅使用了其中一个,则它将比所需的大得多,并包含许多未使用的代码。

这与Windows的动态链接模型形成了有趣的对比。在那里,操作系统必须完全加载你的可执行文件使用的所有 DLL(动态链接库),这可能会导致 RAM 中的膨胀。这种模型的优点是您的可执行文件本身较小,并且已经链接的 DLL 可能已经在某些其他可执行文件使用的内存中,因此它们不需要再次加载。

在静态链接中,库函数为每个可执行文件单独加载。


3

从技术上讲,结果完全相同。通常,您会创建用于实用函数的库,因此无需提供数十个对象文件给链接器,您只需要链接库即可。

顺便说一句,仅包含一个.o文件的 .a 文件毫无意义。


2
结果通常会非常不同。在这个人为的例子中却恰好相同。 - user66363

2

您可以将一组文件放入存档(.a)文件中以供以后重用。标准库是一个很好的例子。

有时将大型项目组织成库是有意义的。


1
主要的优点在于当你需要链接时,只需指定一个库而不是所有单独的目标文件。此外,在管理文件方面也有一些小优势,只需处理一个库而不是一堆目标文件。曾经,这也可以显著节省磁盘空间,但当前硬盘价格使得这个因素变得不那么重要了。

0
每当我的团队中的新手问我这个问题时,“为什么(有时甚至是‘什么是’).a?”,我使用以下答案,以.zip作为类比。
“一个dotAy就像是你想要在构建exe/lib时链接的所有dotOhs的zip文件。节省磁盘空间,而且不需要输入涉及到的所有dotOhs的名称。”
到目前为止,这似乎让他们理解了。;)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接