Each of the three lines triggers undefined behavior. The key part of the C standard, that explains this, is section 6.3.2.1p2 regarding Conversions:
Except when it is the operand of the sizeof
operator, the
_Alignof
operator, the unary &
operator, the ++
operator, the
--
operator, or the left operand of the .
operator or an
assignment operator, 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 has qualified type, the value has
the unqualified version of the type of the lvalue; additionally,
if the lvalue has atomic type, the value has the non-atomic version
of the type of the lvalue; otherwise, the value has the
type of the lvalue. If the lvalue has an incomplete type and does
not have array type, the behavior is undefined. 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.
In each of the three cases, an uninitialized variable is used as the right-hand side of an assignment or initialization (which for this purpose is equivalent to an assignment) and undergoes lvalue to rvalue conversion. The part in bold applies here as the objects in question have not been initialized.
This also applies to the int i = i;
case as the lvalue on the right side has not (yet) been initialized.
There was debate in a related question that the right side of int i = i;
is UB because the lifetime of i
has not yet begun. However, that is not the case. From section 6.2.4 p5 and p6:
5 An object whose identifier is declared with no linkage and without the storage-class specifier static
has automatic
storage duration, as do some compound literals. The result of
attempting to indirectly access an object with automatic storage
duration from a thread other than the one with which the object is
associated is implementation-defined.
6 For such an object that does not have a variable length array type, its lifetime extends from entry into the block
with which it is associated until execution of that block ends in any
way. (Entering an enclosed block or calling a function
suspends, but does not end,execution of the current block.) If
the block is entered recursively, a new instance of the object is
created each time. The initial value of the object is
indeterminate. If an initialization is specified for the
object, it is performed each time the declaration or compound
literal is reached in the execution of the block; otherwise,
the value becomes indeterminate each time the declaration is reached
So in this case the lifetime of i
begins before the declaration in encountered. So int i = i;
is still undefined behavior, but not for this reason.
The bolded part of 6.3.2.1p2 does however open the door for use of an uninitialized variable not being undefined behavior, and that is if the variable in question had it's address taken. For example:
int a;
printf("%p\n", (void *)&a);
printf("%d\n", a);
In this case it is not undefined behavior if:
- The implementation does not have trap representations for the given type, OR
- The value chosen for
a
happens to not be a trap representation.
In which case the value of a
is unspecified. In particular, this will be the case with GCC and Microsoft Visual C++ (MSVC) in this example as these implementations do not have trap representations for integer types.
i
later on will be undefined. I'm arguingint i=i;
itself is undefined behaviour. – Jesterint i = i;
isn't actually readingi
, hence NOT UB. please check the comments on the link. – Jesterint i = i;
isn't doing anything, why is it in your code? – Chamferint i = i;
is semantically equivalent toint i; i = i;
Both are UB. But just because you have UB in your code doesn't mean the compiler have to do something about it, it's part of the whole undefined bit. A decent compiler will be able to detect it and warn you about it though, but from the compilers point of view it's not an error. – Severeint i; i = i
invokes UBs, but isint i = i
in itself is not UB, per say? – Turnsoleint a; a = 5
is not UB. thusint a = a
it depends on next line. Soint a = a; a = 5
is this UB, I do not think so? – Turnsole