c - 静态库不需要头?

标签 c linux gcc linker

我是 C 语言的新手,尤其是编写静态库时,我的库出现了奇怪的行为。

我写了一个叫做 cde 的小静态库。我用 gcc 将不同的部分编译成 .o 文件,然后我用 ar 将它们全部放在一个 .a 文件中

现在,当我想测试我的库时,我会执行以下操作:

gcc test.c -L../bin -lcde -lelf

libcde.a 是我位于 ../bin 的库。 libelf.a 是我的库需要的库(我不知道如何将它直接放入我自己的库中......)。

关键是我可以调用我的库的每个函数,而不必包含我的库的头文件。这怎么可能?在编译时不应链接文件,因此编译器应该不知道我的库中有哪些可用函数...

当我按以下方式运行时,

gcc -L../bin -lcde -lelf test.c

文件 test.c 找不到我在头文件中定义的任何函数,即使我已经包含它也是如此。

我认为我在这里做的事情从根本上是错误的,但我真的无法找出是什么。

最佳答案

这里有两个问题:

  1. 为什么 test.c 编译时没有提供库中例程声明的 header ?
  2. 为什么链接对命令行中第一个列出的 test.c 有效,但对命令行最后一个列出的 test.c 无效?

我们无法对第一个问题提供完整的答案,因为您没有显示源代码。正如其他人所指出的那样,C 有一些回旋余地来提供隐式声明,这主要是出于历史原因。这些隐式声明可能与您的例程的实际定义不匹配,这可能会导致错误,因此您应该避免隐式声明。

第二个问题的答案是这样的。给定您显示的两个命令行中的任何一个,编译器编译 test.c 然后调用链接器。 (也可以让编译器做其他事情,例如在不链接的情况下进行编译,或者从先前编译的源链接目标模块。)当编译器调用链接器时,它按照与您的顺序相对应的顺序传递链接器参数将它们传递给链接器。特别是,如果您将 -lcde 放在 test.c 之前,则编译器会将 -lcde 放在来自 的目标模块之前test.ctest.o,当它运行链接器时。

这很重要,因为链接器的操作方式。除其他外,链接器有一个它需要定义的符号列表。该列表最初是空的。链接器从左到右处理来自命令行的输入。当链接器在其命令行中看到像 test.o 这样的目标模块时,它会读取目标模块并对其进行处理。通常,目标模块包含对它未定义的某些符号的引用,例如对库例程的调用。如果链接器已经从以前的文件中定义了这些符号,它会将引用连接到这些定义。如果它没有定义,它会将符号添加到链接器需要定义的符号列表中。

当链接器处理库文件时,它会检查库中的每个目标模块,以查看该目标模块是否定义了链接器所需定义列表中的符号。如果是这样,链接器读取该目标模块并将其(及其定义)添加到链接器正在构建的可执行文件中。如果不是,链接器将忽略该目标模块。

现在我们可以明白为什么 test.c -lcde 有效而 -lcde test.c 无效。在前一种情况下,链接器列出test.o 需要的所有内容,然后从cde 库中获取这些内容。在后一种情况下,链接器看到了 cde 库,但还不需要它的任何东西,因此它继续进行而不从库中获取任何东西。然后链接器读取 test.c 并添加到所需符号列表中。然后命令行结束,链接器没有更多的文件,但仍然有没有定义的符号。所以报错。

因此,一般来说,库应该在命令行最后列出。

关于c - 静态库不需要头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11359349/

相关文章:

C 联编文件? : linker command failed with exit code 1

编译器优化不编译常量?

c++11 - DSO 库符号似乎具有默认可见性,但应该是隐藏的

java - 有没有办法从命令行指定 Ant 使用哪个 JVM?

php - mysql注入(inject)破坏?

gcc - 配置 gcc 时 --enable-gold 的用途

c++ - 如何将 C 转换为 C++?

c - 将 `log10(2)` 的结果分配给一个常量

c - 在 VS Express 2012 中用 c 定义二维数组

linux - bash if elif 语句总是打印第一个值