As written, without introducing VLAs or additional blocks, no. C23 §6.2.4(5–6):
> An object whose identifier is declared with no linkage and without the storage-class specifier `static` has automatic storage duration [...].
> 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 representation of the object is indeterminate. If an initialization is specified for the object and it is not specified with `constexpr`, it is performed each time the declaration or compound literal is reached in the execution of the block [...]; otherwise, the representation of the object becomes indeterminate each time the declaration is reached.
That is, a local variable is live from the moment the block that contains its declaration is entered (however and wherever that happens) until it is left (ditto), but is initialized or, for lack of a better word, uninitialized each time execution passes that declaration (however many times that happens, including none). This is despite the fact that at compile time the variable’s name is not in scope until the = introducing its initializer (or the place where such a = would go if there isn’t one). Modulo its smaller feature set, C89 §6.1.2.4(3) stipulates the same.
In addition to GGP’s deliberately confusing example, this permits the much more reasonable and C89-compatible
switch (x) {
int i, j;
case 1:
/* use i and j */
break;
case 2:
/* use i and j */
break;
}
The only exception is locals of variably modified type (e.g. variable-length arrays), whose declarations you can’t jump over on pain of undefined behaviour.
No wonder basically every C compiler allocates a single stack frame at function entry.