在C程序中,当在两个不同的库中用不同的实现定义相同的函数时会发生什么?
如果我们有一个函数foo(),它在两个不同的库中具有相同的原型,具有不同的实现,并且如果我们只包含一个头文件(具有函数声明),那么如果我们尝试在编译或运行时编译或执行程序,会发生什么情况呢?发生的情况是特定于实现的(甚至可能在C11标准中没有指定,但我让您检查一下;好吧,该标准没有提到-只是提到)。我相信您给出的场景是一些,所以您可以(因为任何事情都是允许发生的)。它可能是实现定义的 实际上,在我的Linux系统上,我有时会遇到一些错误(至少对于静态库是这样)。请阅读和。请注意,编译器(有时)正在运行在C程序中,当在两个不同的库中用不同的实现定义相同的函数时会发生什么?,c,static-libraries,C,Static Libraries,如果我们有一个函数foo(),它在两个不同的库中具有相同的原型,具有不同的实现,并且如果我们只包含一个头文件(具有函数声明),那么如果我们尝试在编译或运行时编译或执行程序,会发生什么情况呢?发生的情况是特定于实现的(甚至可能在C11标准中没有指定,但我让您检查一下;好吧,该标准没有提到-只是提到)。我相信您给出的场景是一些,所以您可以(因为任何事情都是允许发生的)。它可能是实现定义的 实际上,在我的Linux系统上,我有时会遇到一些错误(至少对于静态库是这样)。请阅读和。请注意,编译器(有时)正
ld
(有许多额外的选项,这些选项通常是隐藏的)。因此(了解gcc
命令如何启动ld
)
如果您在Linux上链接两个共享库,它会变得更有趣。但是阅读Dreper的.IIRC,第一个符号定义会覆盖第二个。例如,您可以在隐式-lc
之前使用并链接-ljemalloc
(两个库都定义了malloc
符号)。还要注意(在Linux上,请参阅&)
Linux也有弱符号,请参见,并且它有属性
在Windows上(我不知道也从未使用过),情况可能会有所不同。但是,在链接时,您仍然可以看到一些“多重定义符号”错误
操作系统以特定于操作系统的方式工作。因此,请阅读Levine的(其中解释了Windows和Unix链接器的工作原理,详细信息有所不同)。要了解更多有关操作系统的信息,请阅读(可免费下载)。根据您的标记,您需要询问静态库的链接。链接器 不知道或不关心其输入文件编译的源语言 C语言对这个问题无关紧要,但我将用C语言来说明 阅读文章,它会解释链接 带有静态库的程序与链接程序完全相同 在静态库中存档0个或多个对象文件,即 链接器需要为其提供定义的0个或多个对象文件 程序中未解析的符号引用 链接器在提供它的静态库
libx.a
中找到对象文件p.o
后
通过对程序引用的某个符号foo
的定义,它将链接
将对象文件libx.a(p.o)
导入程序以解析foo
。它不会
尝试在任何其他对象文件q.o
中查找foo
的任何其他定义
链接中libx.a之后的静态库liby.a
因此,如果在任何其他静态库liby.a
它在链接中的出现时间晚于libx.a
,后者还包含
foo
,该对象文件liby.a(q.o)
甚至不会链接到程序中
除非链接器需要,否则它将提供其他符号的定义bar
假设不是这样,liby.a(q.o)
也不存在,用于链接目的
链接器不会链接来自libx.a(p.o)
和liby.a(q.o)
的同一符号的多个定义,如果
不需要。它将链接第一个对象文件libx.a(p.o)
,该文件定义foo
进入程序,然后定义foo
下面是一个例子:
main.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
p.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
创建三个静态库,分别来自p.o
、q.o
、r.o
:
$ ar rcs libx.a p.o
$ ar rcs liby.a q.o
$ ar rcs libz.a r.o
然后链接一个程序,在liby.a
之前输入libx.a
:
$ gcc -o prog main.o libz.a libx.a liby.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: libx.a(p.o): definition of foo
$ gcc -o prog main.o libz.a liby.a libx.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: liby.a(q.o): definition of foo
诊断链接选项-Wl,-trace symbol=foo
要求链接器告知
使用链接到prog
的文件的名称,其中查找到foo
的未解析引用,以及
还有定义了foo
的文件名。您可以看到foo
在main.o
而libx.a(p.o)
提供的定义是链接的
liby.a(q.o)
未链接。此链接与
它只包含来自p.o
的foo
的定义,作为程序
显示:
现在重新链接prog
,这次在libx.a
之前使用liby.a
:
$ gcc -o prog main.o libz.a libx.a liby.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: libx.a(p.o): definition of foo
$ gcc -o prog main.o libz.a liby.a libx.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: liby.a(q.o): definition of foo
这一次,foo
的定义是从liby.a(q.o)
链接而来的。此链接与:
gcc -o prog main.o r.o q.o
它只包含来自q.o
的foo
的定义,作为程序
显示:
链接器并不关心您提供了多少定义的foo
不同的静态库中有不同的对象文件。它只关心
如果程序中引用了foo
,则定义了foo
,精确一次,
通过它链接到程序中的文件
如果强制链接器将包含更多内容的文件链接到程序中
foo
的多个定义,默认情况下链接器将
如果出现多定义错误,链接将失败,因为
一个程序中不能有多个foo
的定义
举例说明:
qr.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
在新的静态库中归档qr.o
:
$ ar rcs libyz.a qr.o
对象文件libyz.a(qr.o)
定义了foo
和bar
,因此我们可以链接
我们的计划是:
$ ./prog
foo from p.c
bar from qr.c