这两种文件类型有什么区别?我发现我的 C++ 应用在构建可执行文件时链接了这两种类型。
如何构建 .a 文件?希望能提供链接、参考资料以及特别是示例。
这两种文件类型有什么区别?我发现我的 C++ 应用在构建可执行文件时链接了这两种类型。
如何构建 .a 文件?希望能提供链接、参考资料以及特别是示例。
.o
文件是目标文件。它们是编译器的输出,也是链接器/库工具的输入。
.a
文件是归档文件。它们是一组目标文件或静态库,也是链接器的输入之一。
附加内容
我没有注意到您问题中的 "examples" 部分。通常,您将使用makefile来生成静态库。
AR = ar
CC = gcc
objects := hello.o world.o
libby.a: $(objects)
$(AR) rcu $@ $(objects)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
这将会编译hello.c
和world.c
成为对象,然后将它们打包成库。根据平台的不同,您可能还需要运行一个称为ranlib
的实用程序,在存档中生成目录表。
有趣的是:.a
文件在技术上是存档文件而不是库文件。它们类似于没有压缩的zip文件,尽管它们使用一个更古老的文件格式。由像ranlib
这样的工具生成的目录表是使存档成为库的关键。Java归档文件(.jar
)也类似,它们是由Java归档器创建的带有一些特殊目录结构的zip文件。
.o 文件是编译单个编译单元(基本上是一个源代码文件,带有相关的头文件)的结果,而 .a 文件则是一个或多个 .o 文件打包成的库。
D Shawley的回答很好,我想要补充一些观点,因为其他回答反映了对正在发生的事情的不完整理解。
请记住,归档文件(.a)不仅限于包含目标文件(.o)。它们可以包含任意文件。虽然很少有用,但参见动态链接器依赖项信息嵌入档案以了解愚蠢的链接器技巧。
还要注意,目标文件(.o)不一定是单个编译单元的结果。可以将几个较小的对象文件部分链接成单个较大的文件。
http://www.mihaiu.name/2002/library_development_linux/ - 在此页面中搜索“partial”
.a
和链接.o
的另一个方面是:在链接时,所有作为参数传递的.o
文件都包含在最终的可执行文件中,而任何.a
参数的条目仅在程序解析符号依赖关系时才包含在链接器输出中。.a
文件都是由多个.o
文件组成的存档。您可以认为每个.o
都是代码的原子单位。如果链接器需要从这些单元之一获取符号,则整个单元将被吸入最终二进制文件中;但除非它们也被使用,否则不会吸入其他任何单元。.o
文件时,链接器会吸入它,因为您请求了它。a.o
和b.o
。我们的程序只引用a.o
中的符号。我们将比较链接一起传递a.o
和b.o
与包含相同两个对象的静态库时,链接器的处理方式。// header.hh
#pragma once
void say_hello_a();
void say_hello_b();
// a.cc
#include "header.hh"
#include <iostream>
char hello_a[] = "hello from a";
void say_hello_a()
{
std::cout << hello_a << '\n';
}
// b.cc
#include "header.hh"
#include <iostream>
char hello_b[] = "hello from b";
void say_hello_b()
{
std::cout << hello_b << '\n';
}
// main.cc
#include "header.hh"
int main()
{
say_hello_a();
}
我们可以使用这个Makefile编译代码:
.PHONY = compile archive link all clean
all: link
compile:
@echo ">>> Compiling..."
g++ -c a.cc b.cc main.cc
archive: compile
@echo ">>> Archiving..."
ar crs lib.a a.o b.o
link: archive
@echo ">>> Linking..."
g++ -o main_o main.o a.o b.o
g++ -o main_a main.o lib.a
clean:
rm *.o *.a main_a main_o
并获得两个可执行文件main_o
和main_a
,它们的不同之处在于在第一种情况下,a.cc
和b.cc
的内容是通过两个.o
文件提供的,而在第二种情况下则是通过一个.a
文件。
最后,我们使用nm
工具检查最终可执行文件的符号:
$ nm --demangle main_o | grep hello
00000000000011e9 t _GLOBAL__sub_I_hello_a
000000000000126e t _GLOBAL__sub_I_hello_b
0000000000004048 D hello_a
0000000000004058 D hello_b
0000000000001179 T say_hello_a()
00000000000011fe T say_hello_b()
$ nm --demangle main_a | grep hello
00000000000011e9 t _GLOBAL__sub_I_hello_a
0000000000004048 D hello_a
0000000000001179 T say_hello_a()
注意观察,main_a
实际上缺少来自 b.o
的不必要符号。也就是说,链接器没有将存档文件 lib.a
中的 b.o
内容引入,因为没有使用 b.cc
中的任何符号。
ar
从.o
文件(目标文件)创建.a
文件(静态库)。man ar
。
libby.a
目标的配方行($(AR) rcu $@ $(objects)
)中使用$<
或$^
来引用第一个或所有前置目标。这将产生相同的效果,但也消除了指定$(objects)
两次的冗余,如果变量名称或前提条件发生更改,则需要在两个位置更新引用,并且代码量较少。 - Shammel Lee$^
,因为它是GNU Make的扩展,在BSD make或POSIX make中不存在。虽然大多数Linux发行版都非常基于GNU,但从bash到dash的/bin/sh切换足以让我继续避免使用GNU特定的扩展。 - D.Shawley.a
文件作为存档,是的,您可以完全这样做。那就是它们被制作的目的。tar
格式比旧的ar
格式包含更多的文件元数据。 - D.Shawley