当您的应用程序被链接时,未解析的符号将使用您提供的库进行解析。如果您没有定义函数,它将在链接期间成为未解析的符号,因此,在这种情况下,链接将尝试使用 librmath 解析该符号。如果一个或多个符号无法解析,您将收到链接器错误。
但是,如果您确实在代码中定义了该函数,它将在链接期间已经定义,因此无需使用外部库中的符号来解析它。
您不能在应用程序中多次定义同一个符号。
编辑:由于另一个答案有很多争论,我做了一个实际的例子。我创建了一个共享对象(类似于windows中的DLL),它定义并导出了一个函数foo:
//lib.h
extern "C" {
void foo();
void bar();
};
//lib.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From lib\n";
}
void bar() {
std::cout << "Bar, calling foo\n";
foo();
}
为了测试这个共享对象,我创建了一个与之链接的应用程序:
//test.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From app\n";
}
int main() {
bar();
}
我已经编译了共享对象和应用程序:
g++ lib.cpp -o libtest.so -Wall -fPIC -shared -Wl,--export-dynamic -Wl,-soname,libtest.so -Wl,-z,defs
g++ test.cpp -o test -L. -ltest
当我执行test,将库路径设置为".",这样我的共享对象就可以被加载,我得到这个输出:
matias@master:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app
如您所见,应用程序(不是共享对象)中定义的foo 函数被调用。您基本上可以对共享对象中的每个导出符号执行此操作。
EDIT2:我在 lib.h 中添加了另一个导出函数。应用程序现在调用这个函数,最终调用 foo.结果和预期的一样。
EDIT3:好的,让我们更深入一点。这是来自函数bar 的转储:
Dump of assembler code for function bar@plt:
0x0804855c <+0>: jmp DWORD PTR ds:0x804a004
0x08048562 <+6>: push 0x8
0x08048567 <+11>: jmp 0x804853c
如果我们去地址0x804a004:
Dump of assembler code for function _GLOBAL_OFFSET_TABLE_:
0x08049ff4 <+0>: or BYTE PTR [edi+0x804],bl
0x08049ffa <+6>: add BYTE PTR [eax],al
0x08049ffc <+8>: add BYTE PTR [eax],al
0x08049ffe <+10>: add BYTE PTR [eax],al
.....
如您所见,它正在跳转到全局偏移表。您可以阅读有关 GOT here 和 here 的信息。动态符号(在运行时解析)存储在此表中。每当您调用应该在运行时解析的符号时,您实际上是跳转到该表,然后跳转到存储在该表对应条目中的地址。由于应用程序定义了foo,GOT 包含来自test.cpp 的定义地址,而不是我们共享对象中的地址。
EDIT4:好的,最后一次编辑。引用文档:
您需要提供统一的随机数生成器
double unif_rand(void)
或使用提供的(使用动态库或 DLL,您将
必须使用提供的(...)
文档明确指出,如果您使用动态库,则无法提供自己的 unif_rand 实现。因此,我相信我所指出的实际上回答了您的问题。