C是否像C++一样有一个定义规则?
Does C have One Definition Rule like C++?
最近发现有些情况绝对违反了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
(即使在 Source 2 var_global
中是 unsigned
)并且没有错误重新定义 var_global
。
我知道 C 与 C++ 有不同的规则,但我不认为它有那么大的不同。
我有 google 并阅读了很多结果,但没有官方结果 like this C++。
所以问题是:
C是否像C++一样有一个定义规则?
和:
官方叫什么?
我需要它来与C++的规则进行比较,以便我更深入地理解这两种语言。
p/s: 我使用 Visual Studio 2010 编译上面的代码。
我认为您要查找的是 C11
标准的第 6.2.7 章,兼容类型和复合类型,(强调我的)
All declarations that refer to the same object or function shall have compatible type;
otherwise, the behavior is undefined.
与兼容类型相关,
Two types have compatible type if their types are the same.
在您的情况下,int
和 unsigned int
不是兼容的类型。因此 undefined behavior.
为了清楚起见,在您的 source 2 中,unsigned int var_global;
是一个声明,它与其他声明(和定义)不匹配,所以,这是 UB。
就是说,像这样的声明
printf("%d \n",var_global);
将始终将 %d
的参数视为 int
类型。如果类型和格式说明符不匹配,您将再次调用 undefined behavior.
编辑:
编辑后,答案是,使用-fno-common
得到想要的错误。 (缺少 extern
是你所烦恼的,我相信)。
引用在线 GCC 手册,
-fno-common
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 get a multiple-definition error when you link them. In this case, you must compile with -fcommon instead. Compiling with -fno-common is useful on targets for which it provides better performance, or if you wish to verify that the program will work on other systems that always treat uninitialized variable declarations this way.
我不知道 C 标准中有没有提到 "one definition rule" 的措辞,但是顺带一提,您可以查看附件 §J.5.11,多个外部定义,
There may be more than one external definition for the identifier of an object, with or
without the explicit use of the keyword extern
; if the definitions disagree, or more than
one is initialized, the behavior is undefined.
您所看到的与 one-definition 规则无关。它与 %d
期望有符号值的事实有关,因此几乎肯定会在您的实现中将其视为有符号值。
但是,这不是您应该依赖的东西。根据 C 标准 7.19.6.1 The fprintf function /9
(我参考的是 C99,但 C11 在此处显示的方面几乎相同):
If any argument is not the correct type for the corresponding conversion specification, the behaviour is undefined.
由于您使用的是未定义的行为,因此实现可以自由地做任何它想做的事。此外,该标准还明确指出,如果(来自附件 J):
是未定义的行为
two declarations of the same object or function specify types that are not compatible.
在你的例子中,这两个声明确实指定了同一个对象,因为它们都有外部链接。
现在您可能认为有符号整数和无符号整数是兼容的,但您错了:6.2.7 Compatible and composite type
,6.2.5 Types
清楚地表明有符号整数和未签名的变体不兼容:
Two types have compatible type if their types are the same.
For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements.
最近发现有些情况绝对违反了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
(即使在 Source 2 var_global
中是 unsigned
)并且没有错误重新定义 var_global
。
我知道 C 与 C++ 有不同的规则,但我不认为它有那么大的不同。
我有 google 并阅读了很多结果,但没有官方结果 like this C++。
所以问题是:
C是否像C++一样有一个定义规则?
和:
官方叫什么?
我需要它来与C++的规则进行比较,以便我更深入地理解这两种语言。
p/s: 我使用 Visual Studio 2010 编译上面的代码。
我认为您要查找的是 C11
标准的第 6.2.7 章,兼容类型和复合类型,(强调我的)
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
与兼容类型相关,
Two types have compatible type if their types are the same.
在您的情况下,int
和 unsigned int
不是兼容的类型。因此 undefined behavior.
为了清楚起见,在您的 source 2 中,unsigned int var_global;
是一个声明,它与其他声明(和定义)不匹配,所以,这是 UB。
就是说,像这样的声明
printf("%d \n",var_global);
将始终将 %d
的参数视为 int
类型。如果类型和格式说明符不匹配,您将再次调用 undefined behavior.
编辑:
编辑后,答案是,使用-fno-common
得到想要的错误。 (缺少 extern
是你所烦恼的,我相信)。
引用在线 GCC 手册,
-fno-common
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 get a multiple-definition error when you link them. In this case, you must compile with -fcommon instead. Compiling with -fno-common is useful on targets for which it provides better performance, or if you wish to verify that the program will work on other systems that always treat uninitialized variable declarations this way.
我不知道 C 标准中有没有提到 "one definition rule" 的措辞,但是顺带一提,您可以查看附件 §J.5.11,多个外部定义,
There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword
extern
; if the definitions disagree, or more than one is initialized, the behavior is undefined.
您所看到的与 one-definition 规则无关。它与 %d
期望有符号值的事实有关,因此几乎肯定会在您的实现中将其视为有符号值。
但是,这不是您应该依赖的东西。根据 C 标准 7.19.6.1 The fprintf function /9
(我参考的是 C99,但 C11 在此处显示的方面几乎相同):
If any argument is not the correct type for the corresponding conversion specification, the behaviour is undefined.
由于您使用的是未定义的行为,因此实现可以自由地做任何它想做的事。此外,该标准还明确指出,如果(来自附件 J):
是未定义的行为two declarations of the same object or function specify types that are not compatible.
在你的例子中,这两个声明确实指定了同一个对象,因为它们都有外部链接。
现在您可能认为有符号整数和无符号整数是兼容的,但您错了:6.2.7 Compatible and composite type
,6.2.5 Types
清楚地表明有符号整数和未签名的变体不兼容:
Two types have compatible type if their types are the same.
For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements.