C++ 链接器如何知道外部函数的定义在哪里?
我读了几篇文章,得出的结论是extern告诉编译器“这个函数存在,但它的代码在其他地方。不要惊慌。”但是链接器如何知道函数的定义位置呢 我的情况是:- 我正在制作Keil uvision 4。有一个头文件grlib.h,主函数在grlib_demo.c中(包括grlib.h)。现在,有一个函数GrCircleDraw()在Circle.c中定义,在grlib_demo.c中调用,还有一个语句 外部无效GrCircleDraw(所有参数) 在grlib.h。我的问题是链接器如何知道GrCircleDraw()的定义在哪里,因为Circle.c不包括在grlib.h和grlib_demo.c中C++ 链接器如何知道外部函数的定义在哪里?,c++,c,extern,keil,C++,C,Extern,Keil,我读了几篇文章,得出的结论是extern告诉编译器“这个函数存在,但它的代码在其他地方。不要惊慌。”但是链接器如何知道函数的定义位置呢 我的情况是:- 我正在制作Keil uvision 4。有一个头文件grlib.h,主函数在grlib_demo.c中(包括grlib.h)。现在,有一个函数GrCircleDraw()在Circle.c中定义,在grlib_demo.c中调用,还有一个语句 外部无效GrCircleDraw(所有参数) 在grlib.h。我的问题是链接器如何知道GrCircle
注意:-文件grlib.h和Circle.c位于同一文件夹中。代码成功运行。简单的答案是“编译器不需要知道,但链接器必须能够找到它”。通过多个
.o
文件或库,链接器必须能够找到GrCircleDraw
函数的单个定义。编译器只是将外部
函数的名称放入.obj
文件中。编译器不需要更多地了解它
开始链接时,作为开发人员,您有责任向链接器提供所有必要的对象文件和库文件。链接器将把所有这些函数排列成一个二进制文件。如果没有指定正确的库或.obj
文件,链接将失败,出现未解析的废话
默认库通常隐式包含。这会使事情复杂化并产生幻觉。您可以始终指定不需要任何隐式库,并显式包含所有内容。不幸的是,每个系统都以自己的方式进行链接。链接通常以这种方式进行:命令行被迭代,每个参数都被指定
如果是对象文件,则直接使用
在需要的范围内使用(=满足迄今为止未解决的所有参考)
最后,为了成功链接,必须完成每个引用。链接器命令行中给出的行顺序很重要。在中编译.o文件时,.o
文件中有许多内容,例如:
- 包含代码的
.text
部分李>
.data
,.rodata
,.rss
包含全局变量的部分李>
- a
.symtab
包含.o
中的符号列表(函数、全局变量和其他)(及其在文件中的位置)以及.o
文件使用的符号李>
- 诸如
.rela.text
等部分是重新定位列表——这些是链接编辑器(和/或动态链接器)必须进行的修改,以便将程序的不同部分链接在一起
在呼叫方
让我们编译一个简单的C文件:
extern void GrCircleDraw(int x);
int foo()
{
GrCircleDraw(42);
return 3;
}
int bla()
{
return 2;
}
与:
(我使用的是我系统的本机编译器,但在交叉编译到ARM时,它的工作原理是一样的)
您可以使用以下工具查看.o文件的内容:
readelf -a test.o
在符号表中,您将发现:
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
[...]
8: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 foo
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND GrCircleDraw
10: 0000000000000015 11 FUNC GLOBAL DEFAULT 1 bla
此地址位于foo
中:链接编辑器将使用GrCircleDraw
函数的地址修补此地址处的指令
在被叫方
现在让我们自己编译一个GrCircleDraw
的实现:
void GrCircleDraw(int x)
{
}
让我们看看它的符号表:
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
[...]
8: 0000000000000000 9 FUNC GLOBAL DEFAULT 1 GrCircleDraw
符号表“.symtab”包含9个条目:
Num:值大小类型绑定到Ndx名称
[...]
8:0000000000000000 9函数全局默认值1 GrCircleDraw
它有一个用于GrCircleDraw
的条目,用于定义其.text
部分中的位置
将它们连接在一起
因此,当链接编辑器将两个文件组合在一起时,它知道:
- 在哪个
.o
文件中定义了哪些函数及其位置李>
- 其中,在调用方的代码中,它必须使用被调用方的地址进行更新
欢迎来到linkerAt的魔力一级,编译器不知道;查找函数是链接器的工作。链接器知道,因为你告诉它在哪里可以找到它——或者在命令行的对象文件中,或者在库中。@jozefg你是说黑魔法,对吧?废话,我写错了编译器,当然它必须是链接器。但是,链接器必须知道以某种形式定义的位置。在my code Circle.c中(其中包括未添加任何位置的定义)。链接器怎么可能仍然找到它?链接器必须找到GrCircleDraw
的至少一个定义。通常,它会忽略第一个定义之后的任何额外定义,但如果定义在两个对象文件中,或者如果您正在链接一个静态库,并且库中包含第二个定义的对象文件也需要满足某些其他符号,则它会反对;它会抱怨双重定义。@JonathanLeffler没有第二个定义。我的问题是链接器是如何找到一个定义的。@AnkitGupta:那么你告诉我们:(1)有一个circle.c
,但它没有被编译;(2) GrCircleDraw
的唯一定义是在circle.c
中;(3)您的程序编译、链接和运行成功。我不相信所有这些都是真的。特别是,我怀疑(2),并且确实有另一个定义GrCircleDraw
,可能是在您链接的库文件中。您的链接器应该附带一些实用程序来帮助您完成类似的工作。我认为VisualC++工具是名为<代码> DimpBi.exe < /C>但你没有说你使用的是哪一个编译器工具链。另一种方法是让你的链接器创建一个映射文件输出,它显示了很多关于l
void GrCircleDraw(int x)
{
}
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
[...]
8: 0000000000000000 9 FUNC GLOBAL DEFAULT 1 GrCircleDraw