最近,我发现有些情况绝对会违反C++的ODR,但在C编译器中可以编译。
例如,这个奇怪的场景(和我一起):
来源 1
int var_global=-3;
来源 2
#include <stdio.h>
#include <conio.h>
unsigned int var_global;
int main() {
printf("%d n",var_global);
getch();
return 0;
}
我的打印结果是-3
(即使在源 2 中var_global
是unsigned
),并且重新定义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
是你所烦恼的)。
引用在线海湾合作委员会手册,
-fno-common
在 C 代码中,控制未初始化的全局变量的位置。Unix C编译器传统上允许将变量放在一个公共块中,在不同的编译单元中对此类变量进行多个定义。这是 -fcommon 指定的行为,也是大多数目标上 GCC 的默认设置。另一方面,ISO C 不要求此行为,并且在某些目标上可能会对变量引用进行速度或代码大小损失。-fno-common 选项指定编译器应将未初始化的全局变量放在目标文件的数据节中,而不是将它们生成为公共块。这具有这样的效果,如果在两个不同的编译中声明了相同的变量(不带
extern
),则在链接它们时会出现多定义错误。在这种情况下,必须改用 -fcommon 进行编译。使用 -fno-common 进行编译对于它提供更好的性能的目标很有用,或者如果您希望验证程序是否适用于始终以这种方式处理未初始化变量声明的其他系统。
我不知道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 指定),它使用相同的存储量(包括符号信息)并具有相同的对齐要求。