Initialization of Objects with Static Storage Duration in C vs C++ [duplicate]
Possible Duplicate:
What does main return?
For example, the following code compiles without any warning:
#include <stdio.h>
int i = i + 1;
int main(int argc, char *argv[])
{
fprintf (stderr, "%d\n", i);
return 开发者_Go百科0;
}
I think this is illegal in syntax, because i
is used before it's declared, is it right?
And in my opinion, the appearance of int i = i + 1;
is surely a bug, why doesn't the compiler warn about it? I use gcc 4.5.1.
(notice: I'm referring to the current C++ standard)
I'm not really sure about this, but, if my interpretation of the standard is correct, the code should be fine and not UB.
The first initialization of that variable is the zero-initialization of objects with static storage duration that happens before any other initialization takes place (§3.6.2 ¶1).
So, first of all i
is set to zero.
Then, dynamic initialization (i.e. non-zero and non-constant initialization) takes place, so it uses the current value of i
(0) to actually initialize it again. At the end it should evaluate to 1.
This seems confirmed by §8.5 ¶6, that explicitly says:
The memory occupied by any object of static storage duration shall be zero-initialized at program startup before any other initialization takes place. [Note: in some cases, additional initialization is done later. ]
(If you find some flaw in the analysis please just tell me in the comments and I'll be glad to correct/delete the answer, it's slippery floor and I'm conscious of it :) )
In C++ it is syntactically correct. In C you can initialize a global variable only with a constant. So your code would not compile in C.
In C this is legal BTW
int main()
{
int i = i+1;
}
3.3.1/1 Point of declaration
The point of declaration for a name is immediately after its complete declarator and before its initializer (if any).
The behaviour is well defined as per §3.6.2/1
which says:
"Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place."
Your code is not legal C.
If your compiler compiles it without a diagnostic,
your compiler is not a C compiler
You must use constants to initialize a variable.
In your code, the initializer expression ( i + 1
) is not a constant.
This violates 6.7.8/4:
All the expressions in an initializer [...] shall be constant expressions or string literals.
The code is illegal in C.
initializer element is not constant
C99 -- 6.7.8 Initialization
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
The is valid in C++.
C++ Standard States in 3.6.2 Initialization of non-local objects:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place.
You cannot assign a value to variable using another variable outside any function. The statement i + 1;
is evaluated during runtime, while int i = i + 1;
is outside any function so it needs to be evaluated in compile time.
Whether its really syntactically illegal I'm not sure (it would definitly be valid inside the method). However, as you suggest, the is a semantic problem and the compiler should issue a warning as i
was used without initialization. IMO the C/C++ compiler does not generally warn of such thing however (Java for instance would give an error), though you might turn on such warning by adding -Wall
parameter to gcc.
Since a compiler takes statements and emits low-level code for the CPU to use, you have to split apart what is really happening here. It would go something like this:
- Create a memory slot for "i".
- Initialize the memory to zero (normal default behavior).
- Read the value of "i" (which is zero).
- Add 1.
- Store it in "i".
I won't repeat the same things: it is undefined behavior, you should not do it... but provide a use case (which is a common idiom) that shows why it is sometimes interesting to allow the use of the variable there (in C):
int * p = malloc( 10 * sizeof *p );
If usage of p
in the right hand side was disallowed that would be a compiler error. You can circumvent it by explicitly stating the type in the rhs:
int * p = malloc( 10 * sizeof(int) );
But that is prone to subtle errors if at a later time the type is changed, as the compiler will not detect this case:
double * p = malloc( 10 * sizeof(int) ); // will compile and probably cause havoc later
Now, in C++ I can only assume that it is there for backwards compatibility. Note also, that some compilers will be able to detect that invalid use and trigger a warning from the more general group of uninitialized use of variable:
int i = i + 1;
// ^ uninitialized read
There are other situations, however, in C++ where you can pass a reference/pointer to uninitialized objects and it is perfectly fine. Consider:
template <typename T>
struct NullObjectPattern { // intentionally out of order:
T* ptr;
T null;
NullObjectPattern() : ptr( &null ), null() {}
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
};
While null
has not yet been initialized, using it in an expression that only takes it's address (but does not dereference it) is well defined: the memory location exists, it has been allocated and is present. The object itself has not been initialized, and as such dereferencing it will cause UB, but the fact that an uninitialized object is used in an expression does not mean that the code is actually wrong.
To address your question about "i
is used before it's declared, right?"
Not in C++. [basic.scope.pdecl]
says
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below. [ Example:
int x = 12; { int x = x; }
Here the second
x
is initialized with its own (indeterminate) value. — end example ]
精彩评论