如果我理解正确,这意味着
extern void foo();
函数 foo 是在另一个翻译单元中声明的。
1) 为什么不只是#include 声明此函数的标头?
2) 链接器如何知道在链接时在哪里寻找函数?
编辑:也许我应该澄清上面的声明然后使用函数
foo();
它从未在此翻译单元中定义。
1)它可能没有头文件。但是是的,一般来说,对于大型项目,如果多个翻译单元要使用该功能,您应该有一个头文件(不要重复自己)。
2) 链接器搜索它被告知要查找函数和其他符号的所有目标文件和库。
不,这意味着函数foo
是用外部链接声明的。外部链接是指名称foo
在整个程序中引用相同的函数。函数的定义位置无关紧要。它可以在这个翻译单元中定义。可以在其他翻译单元中定义。
使用extern
示例中显示的关键字是多余的。默认情况下,函数始终具有外部链接。以上是100%相当于只是
void foo();
至于链接器,当链接器将程序链接在一起时,它看起来无处不在。它查看所有定义,直到找到 的定义foo
。
正如其他人已经说过的那样,extern
关键字用于说明名称(变量或函数)具有外部链接,这意味着该名称指的是整个程序中的同一对象。此外,这是在文件范围内定义的变量和函数的默认值,因此这种用法是多余的。
extern 关键字的另一种用法是这样的:
extern "C" void foo();
这意味着该函数foo
将使用 C 链接约定进行链接(可能是因为这是在 C 库中定义的函数,或者是打算由 C 程序调用的函数)。
这已经意味着没有 extern 关键字。默认情况下,函数具有外部链接,除非您将它们声明为静态。
使用函数原型是可以的,但很容易出错。当您重新定义函数实现时,您将得到的链接器错误并不容易诊断。链接器不知道去哪里寻找,你的工作是给它一个包含函数定义的目标文件,让它保持快乐。
1)我不知道为什么我需要这个功能。也许其他人可以介入。
2) 链接器通过遍历所有目标文件并检查每个目标文件中的符号来确定这一点。我假设根据您的链接器,确切的搜索顺序可能会有所不同。
对于 GNU binutils 的 ld,从左到右搜索包含缺失符号的对象后出现在链接器命令行中的所有对象文件和库,并选择第一个找到的符号。
示例 1:
$> ld ao -la -lb
将导致在 ao 中搜索未定义的符号。此后 ld 将从左到右遍历库以搜索这些符号,并将在 liba 中找到 bar 并在 libb 中找到 foo。
这可能会导致循环依赖出现奇怪的问题:
示例 2:
现在,liba 和 libb 之间存在循环依赖关系,链接将失败:
$> ld ao -la -lb
因为在搜索 libb 中未定义的符号时,ld 将确定-lb 右侧没有其他 lib提供此符号。这可以通过至少两种方式解决:
1) 链接 liba 两次: $> ld ao -la -lb -la
2) 使用 ld 的分组功能 $> ld ao --start-group -la -lb --end-group
在情况 2) 中,分组告诉 ld 搜索属于该组的所有库中的所有符号。