C
假设我有以下 C 模块:
模块 1
#include <stdio.h>
int x;
int main(){
foo();
printf("%i\n",x);
return 0;
}
模块 2
double x;
void foo(){
x = 3.14;
}
我的问题是:在这种情况下链接器会做什么?在我正在阅读的教科书中,它说编译器只为链接器符号表选择两个弱全局变量之一。这两个选哪个?还是两个都选?如果是这样,为什么?谢谢。
C
假设我有以下 C 模块:
模块 1
#include <stdio.h>
int x;
int main(){
foo();
printf("%i\n",x);
return 0;
}
模块 2
double x;
void foo(){
x = 3.14;
}
我的问题是:在这种情况下链接器会做什么?在我正在阅读的教科书中,它说编译器只为链接器符号表选择两个弱全局变量之一。这两个选哪个?还是两个都选?如果是这样,为什么?谢谢。
C 说这是未定义的行为。
(C99, 6.9p5) “如果在表达式中使用了用外部链接声明的标识符(而不是作为结果为整数常量的 sizeof 运算符的操作数的一部分),则在整个程序中的某处应恰好有一个外部标识符的定义;否则,不得超过一个"
未定义的行为意味着链接器可以在存在多个外部对象定义的情况下中止链接过程。
现在链接器很好(或邪恶,您可以选择)并且通常具有默认扩展来处理多个外部对象定义并且在某些情况下不会失败。
如果你使用gcc
和ld
from binutils,如果你的两个对象被显式初始化,你会得到一个错误。例如,您int x = 0;
在第一个翻译单元中有double x = 0.0;
.
否则,如果外部对象之一未显式初始化(您的示例中的情况)gcc
将默默地将两个对象组合成一个符号。您仍然可以通过将 option 传递给链接器来要求链接器报告警告--warn-common
。
例如链接模块时:
gcc -Wl,--warn-common module1.o module2.o
要中止链接过程,您可以使用--fatal-warnings
选项 ( -Wl,--fatal-warnings,--warn-common
) 请求链接器将所有警告视为错误。
中止链接过程的另一种方法是使用-fno-common
编译器选项,正如@teppic在他的回答中所解释的那样。-fno-common
禁止外部对象在编译时获取通用符号类型。如果你对两个模块都这样做,然后链接,你也会得到多定义链接器错误。
gcc -Wall -fno-common -c module1.c module2.c
gcc module1.o module2.o
如果实现支持多个外部定义,您最终将得到一个对象,该对象有效地转换为每个模块中的每种类型,就像某种隐式联合变量一样。将分配较大类型的内存量,并且两者都将作为外部声明。
如果您使用 clang 或 gcc 进行编译,请使用该选项-fno-common
导致错误。
这是 gcc 手册中的部分:
In C code, controls the placement of uninitialized global
variables. Unix C compilers have traditionally permitted multiple
definitions of such variables in different compilation units by
placing the variables in a common block. This is the behavior
specified by -fcommon, and is the default for GCC on most targets.
On the other hand, this behavior is not required by ISO C, and on
some targets may carry a speed or code size penalty on variable
references. The -fno-common option specifies that the compiler
should place uninitialized global variables in the data section of
the object file, rather than generating them as common blocks.
This has the effect that if the same variable is declared (without
"extern") in two different compilations, you will get a multiple-
definition error when you link them.
此选项有效地强制执行对多个定义的严格 ISO C 合规性。
对于相同类型的外部变量,这种行为通常被接受。正如 GCC 手册所述,大多数编译器都支持这一点,并且(如果类型相同),C99 标准将其用作扩展。