在C程序中,当在两个不同的库中用不同的实现定义相同的函数时会发生什么?

在C程序中,当在两个不同的库中用不同的实现定义相同的函数时会发生什么?,c,static-libraries,C,Static Libraries,如果我们有一个函数foo(),它在两个不同的库中具有相同的原型,具有不同的实现,并且如果我们只包含一个头文件(具有函数声明),那么如果我们尝试在编译或运行时编译或执行程序,会发生什么情况呢?发生的情况是特定于实现的(甚至可能在C11标准中没有指定,但我让您检查一下;好吧,该标准没有提到-只是提到)。我相信您给出的场景是一些,所以您可以(因为任何事情都是允许发生的)。它可能是实现定义的 实际上,在我的Linux系统上,我有时会遇到一些错误(至少对于静态库是这样)。请阅读和。请注意,编译器(有时)正

如果我们有一个函数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