gpt4 book ai didi

c - 在C程序中,如果在两个具有不同实现的不同库中定义了相同功能,会发生什么情况?

转载 作者:行者123 更新时间:2023-12-03 05:29:32 30 4
gpt4 key购买 nike

如果我们有一个函数foo()在具有不同实现的两个不同库中具有相同的原型,并且如果仅包含单个头文件(具有函数的声明),那么当我们尝试在编译或运行时编译或执行程序时会发生什么情况?

最佳答案

对于每个标签,您都在询问静态库的链接。链接器
不知道或不在乎其输入文件使用了什么源语言
从。 C语言对这个问题无关紧要,但我将使用C进行说明。

阅读Stackoverflowtag Wiki about static libraries,它将说明该链接
您的带有静态库的程序与链接程序完全相同
具有0个或多个静态库中存储的目标文件-即
链接程序需要提供0或更多目标文件的定义,否则
程序中未解析的符号引用。

链接器在提供它的静态库p.o中找到目标文件libx.a文件后,
带有程序引用的某些符号foo的定义,它将链接
将目标文件libx.a(p.o)放入程序以解析foo。它不会
尝试在其他任何目标文件中找到foo的其他定义
链接中q.o之后的静态库liby.a

因此,如果任何其他静态库libx.a中有任何其他目标文件q.o
在链接中比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__);
}


c

#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__);
}


函数 foop.cq.c中定义。

将所有 .c文件编译为 .o文件:

$ gcc -c main.c p.c q.c r.c


创建三个静态库,分别来自 p.oq.or.o

$ ar rcs libx.a p.o
$ ar rcs liby.a q.o
$ ar rcs libz.a r.o


然后链接程序,在 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


诊断链接选项 -Wl,-trace-symbol=foo要求链接器告诉
我们链接到 prog的文件名,在其中找到对 foo的未解析引用,并且
也是定义了 foo的文件的名称。您会看到在 foo中引用了 main.o
并且 libx.a(p.o)提供的定义已链接。 foo中的另一个定义
liby.a(q.o)未链接。此链接与

gcc -o prog main.o r.o p.o


该程序仅包含 foop.o的定义
显示:

$ ./prog
foo from p.c
bar from r.c


现在,重新链接 prog,这次在 liby.a之前与 libx.a链接:

$ 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


该程序仅包含 fooq.o的定义
显示:

$ ./prog
foo from q.c
bar from r.c


链接器不在乎您提供的 foo的定义是多少
不同静态库中的不同目标文件。它只在乎
如果在程序中引用了 foo,则 foo被定义一次,
通过它链接到程序中的文件。

如果您强迫链接器将文件链接到包含更多内容的程序中
foo的定义多,默认情况下,链接器通常会
给您一个多定义错误,并且链接将失败,因为
一个程序中的 foo定义不能超过一个。这是一个
的说明:

qr.c

#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__);
}


编译该文件:

$ gcc -c qr.c


qr.o归档到新的静态库中:

$ ar rcs libyz.a qr.o


目标文件 libyz.a(qr.o)定义了 foobar。所以我们可以链接
我们的程序像:

$ gcc -o prog main.o libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libyz.a(qr.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar


它的运行方式如下:

$ ./prog
foo from qr.c
bar from qr.c


但是,如果我们尝试像这样链接它:

$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): in function `foo':
qr.c:(.text+0x0): multiple definition of `foo'; libx.a(p.o):p.c:(.text+0x0): first defined here
/usr/bin/ld: libyz.a(qr.o): definition of bar
collect2: error: ld returned 1 exit status


foo有多个定义。那是因为:


链接器需要 foo的定义,并在 libx.a(p.o)中找到第一个。
因此它将该文件链接到程序中。它不会搜索其他任何一个。
链接器需要 bar的定义,并在 libyz.a(qr.o)中找到第一个。
因此它将该文件链接到程序中。它不会搜索其他任何一个。
但是 libyz.a(qr.o)包含 foo的另一个定义以及一个定义
bar。因此,现在已经链接了 foo的两个定义,这是一个错误。


我说过,如果您将
链接程序尝试将多个定义符号的文件链接到程序中。

但是您可以通过告诉链接器符号不是 weakly defined来避免这种情况,前提是您的链接器理解此概念(就像GNU和Apple链接器一样)。

GCC编译器支持非标准语言扩展 __attribute__ syntax
您可以用来与链接器交流符号定义很弱的情况。
这是一个说明:

qr.c(2)

#include <stdio.h>

void __attribute__((weak)) foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}

void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}


重新编译:

$ gcc -c qr.c


删除 libyz.a并重新创建它:

$ rm libyz.a
$ ar rcs libyz.a qr.o


重试刚刚失败的链接:

$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar


这次,没有错误。 foolibx.a(p.o)的定义是
链接。 libyz.a(qr.o)中的弱定义将被忽略。该程序
运行像:

$ ./prog
foo from p.c
bar from qr.c


如果符号定义不弱,则符号强。链接器的规则是:


最多可以链接一个符号的强定义。
如果输入了一个或多个弱定义以及强定义,
然后将一个强定义链接起来,而忽略所有弱定义。
如果仅输入弱定义,则链接器可以选择其中任何一个
任意地。 (在实践中,它会选择首先找到的那个)。


不要仅仅使用弱符号定义来逃避多个定义
令您惊讶的错误。这样的惊喜意味着你不明白
您的联系。分析并修复它,以便您不再尝试制作
包含同一事物的多个定义的程序。

弱符号定义通常是由编译器在
场景,以实现需要它们的源语言功能(例如
全局内联函数定义或C ++中的模板实例化)。
仅当您完全了解为什么要链接程序时,才自己使用它们
输入相同符号的多个定义。

关于c - 在C程序中,如果在两个具有不同实现的不同库中定义了相同功能,会发生什么情况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54050014/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com