问题
Recently, I found out there are some cases that will absolutely violate the ODR of C++ but will be compiled OK in C compiler.
For example, this wierd scenario (with me):
Source 1
int var_global=-3;
Source 2
#include <stdio.h>
#include <conio.h>
unsigned int var_global;
int main() {
printf("%d \n",var_global);
getch();
return 0;
}
I have the printed result is -3
(even though in Source 2 var_global
is unsigned
) and there is no error about the redefined of var_global
.
I knew that C have different rules with C++ but I don't think it's so different like that.
I have google and read a lot of results but there is no official result like this of C++.
So question is:
Does C have One Definition Rule like C++ ?
and:
What is it called officially?
I need it to compare with the rule of C++ so that I can understand both languages deeper.
p/s: I used Visual Studio 2010 for compiling the code above.
回答1:
I think what you're looking for is chapter §6.2.7 from the C11
standard, Compatible type and composite type, (emphasis mine)
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
and related to compatible type,
Two types have compatible type if their types are the same.
In your case, int
and unsigned int
are not compatible types. Hence undefined behavior.
Just to add a bit of clarity, in your source 2, unsigned int var_global;
is a declaration, and it does not match the other declatation (and definition), so, this is UB.
That said, a statement like
printf("%d \n",var_global);
will always consider the argument to %d
to be of type int
. In case the type and the format specifier does not match, you'll again invoke undefined behavior.
EDIT:
After the edit, the answer is, use -fno-common
to get the desired error. (missing extern
is what you're bothered with, I believe).
Quoting from online GCC manual,
-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.
I don't know of any mention of the wordings "one definition rule" in the C standard, but along the line, you can look into annex §J.5.11, Multiple external definitions,
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.
回答2:
What you're seeing has nothing to do with a one-definition rule. It has to do with the fact that %d
expects a signed value and therefore will almost certainly just be treating it as a signed value in your implementation.
However, this is not something you should rely on. As per the C standard 7.19.6.1 The fprintf function /9
(I'm referencing C99 but C11 is pretty much the same in term of the aspects shown here):
If any argument is not the correct type for the corresponding conversion specification, the behaviour is undefined.
Since you're using undefined behaviour, the implementation is free to do whatever it wants. In addition, the standard also specifically states that it's undefined behaviour if (from Annex J):
two declarations of the same object or function specify types that are not compatible.
In your case, those two declarations do specify the same object since they both have external linkage.
Now you might think that signed and unsigned integers would be compatible but you would be wrong: 6.2.7 Compatible and composite type
, and 6.2.5 Types
makes it clear that signed and unsigned variants are not compatible:
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.
来源:https://stackoverflow.com/questions/34986335/does-c-have-one-definition-rule-like-c