This code does not have undefined behavior. We can find a nice example in the Rationale for International Standard—Programming Languages—C in section 6.2.4
Storage durations of objects it says:
[...]There is a simple rule of thumb: the variable declared is created
with an unspecified value when the block is entered, but the
initializer is evaluated and the value placed in the variable when the
declaration is reached in the normal course of execution. Thus a jump
forward past a declaration leaves it uninitialized, while a jump
backwards will cause it to be initialized more than once. If the
declaration does not initialize the variable, it sets it to an
unspecified value even if this is not the first time the declaration
has been reached.
The scope of a variable starts at its declaration. Therefore, although
the variable exists as soon as the block is entered, it cannot be
referred to by name until its declaration is reached.
and provides the following example:
int j = 42;
{
int i = 0;
loop:
printf("I = %4d, ", i);
printf("J1 = %4d, ", ++j);
int j = i;
printf("J2 = %4d, ", ++j);
int k;
printf("K1 = %4d, ", k);
k = i * 10;
printf("K2 = %4d, ", k);
if (i % 2 == 0) goto skip;
int m = i * 5;
skip:
printf("M = %4d\n", m);
if (++i < 5) goto loop;
}
and the output is:
I = 0, J1 = 43, J2 = 1, K1 = ????, K2 = 0, M = ????
I = 1, J1 = 44, J2 = 2, K1 = ????, K2 = 10, M = 5
I = 2, J1 = 45, J2 = 3, K1 = ????, K2 = 20, M = 5
I = 3, J1 = 46, J2 = 4, K1 = ????, K2 = 30, M = 15
I = 4, J1 = 47, J2 = 5, K1 = ????, K2 = 40, M = 15
and it says:
where “????” indicates an indeterminate value (and any use of an
indeterminate value is undefined behavior).
This example is consistent with the draft C99 standard section 6.2.4
Storage durations of objects paragraph 5 which says:
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 is reached in
the execution of the block; otherwise, the value becomes indeterminate
each time the declaration is reached.