C 在公共块中有全局变量是一种未定义的行为吗?
0.cC 在公共块中有全局变量是一种未定义的行为吗?,c,C,0.c int i = 5; int main(){ return i; } int i; 1.c int i = 5; int main(){ return i; } int i; 上面使用gcc0.c1.c编译,没有关于多个定义的任何链接错误。原因是,i生成为公共块(-fcommon,这是gcc中的默认行为)。 正确的方法是使用此处缺少的extern关键字 我一直在网上搜索,看看这是否是未定义的行为,一些帖子说是,一些帖子说不是,这非常令人困惑: 它是UB 使用了具有
int i = 5;
int main(){
return i;
}
int i;
1.c
int i = 5;
int main(){
return i;
}
int i;
上面使用gcc0.c1.c
编译,没有关于多个定义的任何链接错误。原因是,i
生成为公共块(-fcommon,这是gcc中的默认行为)
。
正确的方法是使用此处缺少的extern
关键字
我一直在网上搜索,看看这是否是未定义的行为,一些帖子说是,一些帖子说不是,这非常令人困惑:
它是UB
使用了具有外部链接的标识符,但在程序中,标识符的外部定义并不完全相同,或者未使用标识符,且标识符存在多个外部定义(6.9)
它不是UB
查找-fno common
:
那是哪一个呢?使用-fcommon
是允许使用多定义的少数几个地方之一,并且编译器会为您进行排序吗?或者它仍然是UB?根据C标准分析代码
最新的C标准第6.9/5节对此进行了说明:
语义学
外部定义是一种外部声明,也是函数(内联定义除外)或对象的定义。如果在表达式中使用了通过外部链接声明的标识符(而不是作为结果为整数常量的sizeof
或\u Alignof
运算符的操作数的一部分),则在整个程序中的某个地方,该标识符应有一个精确的外部定义
标识符;否则,不得超过一个
术语“外部定义”不应与“外部链接”或extern
关键字混淆,它们是完全不同的概念,碰巧有相似的拼写
“外部定义”是指非暂定的定义,不在函数内部
关于暂定定义,见6.9.2/2:
对于具有文件作用域但没有初始值设定项、没有存储类说明符或具有存储类说明符static的对象,其标识符的声明构成了一个暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,而翻译单元不包含该标识符的外部定义,则行为与翻译单元包含该标识符的文件范围声明完全相同,复合类型为翻译单元的末尾,初始值设定项等于0
因此,在您的文件1.c
中,根据6.9.2/2,该行为与它所说的int i=0完全相同代码>取而代之。这将是一个外部定义。这意味着0.c
和1.c
的行为就像它们有外部定义一样,违反了规则6.9/5,即外部定义不得超过一个
违反语义规则意味着行为未定义,无需诊断。
解释“未定义行为”的含义
另见:
如果不清楚,C标准中的“行为未定义”表示C标准未定义该行为。构建在不同一致性实现(或在相同一致性实现上重建)上的同一代码的行为可能不同,包括拒绝程序、接受程序或您可能想象的任何其他结果
(注意-某些程序的行为定义取决于运行时条件;这些程序不能在编译时被拒绝,除非出现导致行为未定义的条件,否则必须按照指定的方式运行。但这不适用于本问题中的程序,因为所有可能的执行都会遇到违反第6.9/5条)
对于C标准未定义行为的情况,编译器供应商可能提供也可能不提供稳定和/或记录的行为。
对于您问题中的代码,编译器供应商通常(ha)提供可靠的行为;这在本标准的非规范性附录J.5.11中有记录:
J.5通用扩展插件
J.5.11多重外部定义
1一个对象的标识符可能有多个外部定义,无论是否明确使用关键字extern
;如果定义不一致,或初始化了多个,则行为未定义(6.9.2)
如果提供了-fcommon
开关,则gcc编译器似乎实现了此扩展;如果提供了-fno common
,则禁用此扩展(默认设置可能因编译器版本而异)
脚注:我有意避免在C标准未定义的行为中使用“已定义”一词,因为在我看来,这是导致OP混淆的原因之一。这是根据标准未定义的,但在gcc中定义了实现。这不是唯一一个未定义的“起作用”的例子由于扩展。答案解释为:…如果程序违反此规则,C标准不会定义行为(C 2018 4 2)。相反,我们让编译器和链接器定义行为。
和整个段落。尝试使用gcc 0.c 1.c-pedantic errors-Wall-Wextra
重新编译,如果这是一个扩展,它可能会给您一个错误/警告。(尚未测试)不管它值多少钱,MSVC的link和LLVM的lld都无法链接并抛出重复的符号错误。Clang还警告说,当没有任何东西用-extern引用非静态全局变量时,会出现非静态全局变量。祝贺你提出了一个很好的问题。