0.c
int i = 5;
int main(){
return i;
}
1.c
int i;
上面用gcc 0.c 1.c
编译得很好,没有任何关于multiple definitions
的链接错误。原因是i
被生成为common blocks (-fcommon which is the default behaviour in gcc)
。正确的方法是使用此处缺少的extern
关键字。
我一直在网上搜索,看看这是否是未定义的行为,一些帖子说是,有些帖子说不是,这非常令人困惑:
是UB
在单独的文件中有多个临时定义是未定义的行为吗?
为什么我可以在C中定义一个变量两次?
如何使用extern在源文件之间共享变量?
http://port70.net/~nsz/c/c11/n1570.html#J.2
使用了具有外部链接的标识符,但在程序中,该标识符不存在一个确切的外部定义,或者该标识符没有使用,并且存在多个标识符的外部定义(6.9)
它不是UB
全局变量和.data部分
在C 中的多个文件中定义外部变量
C和C++一样有一个定义规则吗?
Look for -fno-common
:https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Code-Gen-Options.html
那是哪一个呢?使用-fcommon
是为数不多的允许使用multiple definition
的地方之一,编译器会为您进行排序吗?还是还是UB?
根据C标准分析代码
这包含在最新的C标准:的第6.9/5节中
语义
外部定义是一种外部声明,也是函数(内联定义除外)或对象的定义。如果在表达式中使用了用外部链接声明的标识符(而不是作为结果为整数常量的
sizeof
或_Alignof
运算符的操作数的一部分),则在整个程序的某个地方标识符;否则,不得超过一个。
术语";外部定义";不应与";"外部链接";或者extern
关键字,这些都是完全不同的概念,恰好具有相似的拼写。
"外部定义";指不是暂定的定义,也不在函数内部。
关于暂定定义,其内容见6.9.2/2:
一个对象的标识符声明,该对象具有文件作用域,没有初始值设定项,没有存储类说明符或具有存储类说明符static,构成临时定义。如果一个翻译单元包含一个或多个标识符的临时定义,而该翻译单元不包含该标识符的外部定义
因此,在您的文件1.c
中,根据6.9.2/2,行为与它所说的int i = 0;
完全相同。这将是一个外部定义。这意味着0.c
和1.c
都表现得好像它们有外部定义,这违反了规则6.9/5,即不应有一个以上的外部定义。
违反语义规则意味着行为未定义,不需要诊断
解释什么";未定义行为";手段
另请参阅:未定义、未指定和实现定义的行为
在不清楚的情况下,C标准说";行为是未定义的";意味着C标准没有定义行为。在不同的一致性实现上构建的相同代码(或在相同的一致性实施上重建的代码)可能会有不同的行为,包括拒绝程序、接受程序或您可能想象的任何其他结果。
(注意-一些程序的行为可以根据运行时条件进行定义;这些程序在编译时不能被拒绝,并且必须按照指定的方式运行,除非出现导致行为未定义的条件。但这不适用于这个问题中的程序,因为所有可能的执行都会遇到违反6.9/5的情况)
编译器供应商可能会也可能不会为C标准未定义行为的情况提供稳定和/或记录的行为
对于您问题中的代码,编译器供应商提供可靠行为是很常见的(哈哈);这记录在标准的非规范性附录J.5.11:中
J.5常见扩展
J.5.11多种外部定义1对象的标识符可能有多个外部定义,无论是否明确使用关键字
extern
;如果定义不一致,或者初始化了多个,则行为未定义(6.9.2)。
如果提供了-fcommon
开关,gcc编译器似乎会实现此扩展,如果提供-fno-common
,则会禁用它(默认设置可能因编译器版本而异)。
脚注:我有意避免使用";定义的";与C标准未定义的行为有关,在我看来,这是造成OP混乱的原因之一。