在我的Linux机器上,我有2个库:
libfoo1.a and libfoo2.a
它们都包含
的实现void foo(int)
我的主程序调用foo:
int main() { foo(1); return 0; }
我使用g ++
以两种方式编译程序g++ main.cpp libfoo1.a libfoo2.a -o a1.out
g++ main.cpp libfoo2.a libfoo1.a -o a2.out
当我运行程序时,a1显然正在使用libfoo1.a中的foo()实现,而a2显然使用了libfoo2。也就是说,g ++链接到它首先看到的foo()。
我的问题(最后)是,这个“贪婪”的链接策略实际上是在C ++标准中指定的吗?或者不同的编译器/平台会以实现定义的方式表现不同吗?
PS:为了将问题放在实际的环境中,我非常喜欢这个g ++示例的工作方式。在我的实际应用程序中,我有一个遗留的libfoo2,它实现了许多(很多!)函数,但我想在libfoo1中为少数几个函数提供新的实现。一方面,我可以在libfoo1中编写一个全新的接口,实现我的少数,然后委托libfoo2进行其余的操作。但是,如果我可以依赖链接器为我做这些代码代码,我宁愿无效写入所有代理代码(即使对于像icc这样的非g ++编译器)。
PPS:将它放在真正的实际上下文中,libfoo2是blas,而libfoo1是一些自制程序的OpenMP实现。我还没准备好为MKL做好准备。 ATLAS没有多线程我想要调用的函数。它非常擅长多线程GEMM,但是我需要LAPACK中的一些奇怪的例程(zsptrf / zsptrs / zspr)。我的缓存无效的OpenMP实现似乎比它的缓存调优顺序实现更好。
对不起帖子的长度。
答案 0 :(得分:3)
该标准没有说明链接顺序。我会说依靠编译器使用的任何顺序都不是好习惯。
答案 1 :(得分:3)
几乎所有链接器都会按照您的描述运行。
链接器的传统行为是在命令行中指定的库中从左到右搜索外部函数。这意味着包含函数定义的库应出现在使用它的任何源文件或目标文件之后。
答案 2 :(得分:2)
根据标准,您有两个相同功能的定义,这违反了One定义规则。结果是未定义的行为。
处理此问题的“正确”方法可能是获取原始存档并从中提取所有目标文件。然后将新目标文件复制到目录中,并创建一个只包含所需目标文件版本的新库。最后,链接到您的组合库(并希望原始库中的任何内容都取决于您替换的任何函数未记录的任何内容)。
答案 3 :(得分:1)
据我所知,C ++标准不涉及链接器。这就是为什么mingw32和microsoft的C ++编译器可以使用不同的名称修改方案。