int x = x;
This is "valid" in the sense that it doesn't violate a constraint or syntax rule, so no compile-time diagnostic is required. The name x is visible within the initializer, and refers to the object being declared. The scope is defined in N1570 6.2.1 paragraph 7:
Any other identifier [other than a struct, union, or enum tag, or
an enum constant] has scope that begins just after the completion of
its declarator.
The declarator in this case is int x.
This allows for things like:
int x = 10, y = x + 1;
But the declaration has undefined behavior, because the initializer refers to an object that hasn't been initialized.
The explicit statement that the behavior is undefined is in N1570 6.3.2.1 paragraph 2, which describes the "conversion" of an lvalue (an expression that designates an object) to the value stored in that object.
Except when [list of cases that don't apply here], an
lvalue that does not have array type is converted to the value stored
in the designated object (and is no longer an lvalue); this is called
lvalue conversion.
[...]
If the lvalue designates an object of automatic storage duration that
could have been declared with the register storage class (never
had its address taken), and that object is uninitialized (not declared
with an initializer and no assignment to it has been performed prior
to use), the behavior is undefined.
The object in question is x, referenced in the initializer. At that point, no value has been assigned to x, so the expression has undefined behavior.
In practice, you'll probably get a compile-time warning if you enable a high enough warning level. The actual behavior might be the same as if you had omitted the initializer:
int x;
but don't count on it.
-1373317485Well, once, various values.