我有2个图书馆:test.1
和test.2
。这两个库都包含一个全局 extern“C” void f();
函数,具有不同的实现(测试只需cout
)。
我做了以下测试:
测试1 动态链接:
如果我在可执行文件的makefile中添加libtest.1.so
然后libtest.2.so
,然后在f();
中调用main
,则会调用libtest.1.so->f()
。
如果我更改了makefile中的顺序,则调用libtest.2.so->f()
测试2 静态链接:
静态库
测试3 动态加载
手动加载库时,一切都按预期工作。
我预计多个定义会出现错误,这显然不会发生。
此外,由于情况不同,这并没有打破单一定义规则。
它也不是一个依赖地狱(不是它与此有关),也不是任何链接惨败..
那么,这是什么?未定义的行为?未指明的行为?或者它确实取决于链接顺序?
有没有办法轻松检测到这种情况?
相关问题:
dlopen vs linking overhead
What is the difference between dynamic linking and dynamic loading
Is there a downside to using -Bsymbolic-functions?
Why does the order in which libraries are linked sometimes cause errors in GCC?
linking two shared libraries with some of the same symbols
编辑我再做了两次测试,证实了这个UB:
我在void g()
中添加了第二个函数test.1
,而不是test.2
。
使用动态链接和.so
库,同样的情况发生 - f
以相同的方式调用,g
也是可执行的(如预期的那样)。
但是现在使用静态链接更改了一些内容:如果test.1
在 test.2
之前是,则没有错误,来自test.1
的两个函数都会被调用。<登记/>
但是当订单改变时,会出现“多个定义”错误。
很明显,“不需要诊断”(参见@MarkB的答案),但有时发生错误是“奇怪的”,有时候 - 它没有。
无论如何,答案非常明确并解释了上述所有内容 - UB。
答案 0 :(得分:8)
库是对象文件的集合。链接器根据需要从库中提取对象以满足未解析的符号。重要的是,链接器按照它们在命令行中出现的顺序检查库,只查看每个库一次(除非命令行多次提到库),并且只接受满足某些引用的对象。
在第一组测试中,一切都很清楚:链接器满足第一个可用库中对f()
的引用,而且几乎就是它。
现在进行第二组测试。在成功案例test.1
满足f
和g
引用时,test.2
无关紧要。在失败的情况下,test.2
满足f
引用,但g
仍未定义。为了满足g
,链接器必须从test.1
中提取一些对象,这也恰好提供f
。显然它是多重定义。
请注意,为了发生错误,您必须在同一对象中包含f
和g
。如果test.1
由2个对象组成(一个定义f
,另一个定义g
),则错误消失。
答案 1 :(得分:1)
这绝对违反了案例1和案例2中的一个定义规则。在案例3中,由于您明确指定要执行的函数版本,可能会也可能不会。违反ODR是未定义的行为,无需诊断。
3.2 / 3:
每个程序都应包含每个非内联的一个定义 在该程序中使用的函数或变量;没有诊断 必需的。