C有像C ++这样的一个定义规则吗?
最近,我发现有些情况绝对会违反C ++的ODR,但会在C编译器中编译好。
例如,这个奇怪的场景(和我一起):
来源1
int var_global=-3;
来源2
#include #include unsigned int var_global; int main() { printf("%d \n",var_global); getch(); return 0; }
我的打印结果是-3
(即使在Source 2中 var_global
是unsigned
)并且没有关于var_global
重新定义的var_global
。
我知道C与C ++有不同的规则,但我认为它不是那么不同。
我有谷歌并阅读了很多结果,但没有像 C ++ 这样的官方结果。
所以问题是:
C有像C ++这样的一个定义规则吗?
和:
什么叫官方?
我需要它与C ++规则进行比较,这样我才能更深入地理解这两种语言。
p / s:我使用Visual Studio 2010编译上面的代码。
我认为您正在寻找的是C11
标准的章节§6.2.7, 兼容型和复合型 ,( 强调我的 )
引用同一对象或函数的所有声明都应具有兼容类型 ; 否则,行为未定义。
并且与兼容类型有关,
如果类型相同,则两种类型具有兼容类型。
在您的情况下, int
和unsigned int
不是兼容的类型。 因此未定义的行为 。
只是为了在源2中添加一点清晰度, unsigned int var_global;
是声明,它与其他声明(和定义)不匹配,因此,这是UB。
那说,一个声明就好
printf("%d \n",var_global);
将始终认为%d
的参数为int
类型。 如果类型和格式说明符不匹配,您将再次调用未定义的行为 。
编辑:
编辑后,答案是,使用-fno-common
来获得所需的错误。 ( 我相信,缺少extern
是你所困扰的 )。
引自在线GCC手册,
-fno-common
在C代码中,控制未初始化的全局变量的放置。 Unix C编译器传统上允许通过将变量放在公共块中而在不同的编译单元中对这些变量进行多种定义。 这是-fcommon指定的行为,是大多数目标上GCC的默认行为。 另一方面,ISO C不需要此行为,并且在某些目标上可能会对变量引用进行速度或代码大小惩罚。 -fno-common选项指定编译器应将未初始化的全局变量放在目标文件的数据部分中,而不是将它们生成为公共块。 这样做的结果是,如果在两个不同的编译中声明相同的变量(没有
extern
),则在链接它们时会出现多重定义错误。 在这种情况下,您必须使用-fcommon进行编译。 使用-fno-common进行编译对于提供更好性能的目标非常有用,或者如果您希望validation程序是否可以在其他始终以这种方式处理未初始化变量声明的系统上运行。
我不知道在C标准中提到“一个定义规则”的措辞,但是沿着这一行,你可以看一下附件§J.5.11, 多个外部定义 ,
对象的标识符可能有多个外部定义,有或没有明确使用关键字
extern
; 如果定义不一致,或者初始化了多个,则行为未定义。
您所看到的与单定义规则无关。 它与%d
期望有符号值的事实有关,因此几乎肯定会将其视为实现中的签名值。
但是,这不是你应该依赖的东西。 根据C标准7.19.6.1 The fprintf function /9
(我引用的是C99,但C11在这里显示的方面几乎相同):
如果任何参数不是相应转换规范的正确类型,则行为未定义。
由于您正在使用未定义的行为,因此实现可以随意执行任何操作。 此外,该标准还明确规定,如果(来自附件J),它是未定义的行为:
同一对象或函数的两个声明指定不兼容的类型。
在您的情况下,这两个声明确实指定了同一个对象,因为它们都有外部链接。
现在你可能会认为有符号和无符号整数是兼容的但是你错了: 6.2.7 Compatible and composite type
,以及6.2.5 Types
清楚地表明有符号和无符号变量不兼容:
如果类型相同,则两种类型具有兼容类型。
对于每个有符号整数类型,存在相应(但不同 )的无符号整数类型(使用关键字unsigned指定),其使用相同数量的存储(包括符号信息)并具有相同的对齐要求。