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