Error declaring a variable in a switch statement [duplicate] - c

case 1:
{ //question is about this curly brace
int val;
scanf("%d", &val);
if(top1 == NULL){
enqueue(top1, val, bottom1);
}
else{
enqueue(top1, val);
}
break;
}
Without the curly brace after case 1: it gave an error: *
a label can only be part of a statement and a declaration is not a
statement: int val;
*

That is how the C grammar is defined. Variable declarations are not considered statements:
int x; //a declaration
int y = 3; //another declaration
x = y + 1; //a statement
A label is required to be followed by a statement. A label plus a declaration is not allowed.
foo: int x; //error
bar: x = y +1; //ok
That is, IMO, inconvenient:
while (...)
{
//...
goto end;
end: //error, no statement
}
And remember that a case is just a special kind of label, so:
case 1: int x; //error
case 2: x = y +1; //ok
The issue with braces is that in C they are used to build a compound statement, that of course is a kind of statement. The lines inside the compound statements can be both declarations and statements, all mixed (old C versions only allowed declarations at the beginning, not in the middle). So:
case 1: int x; //error: label plus declaration
case 2: { int x; } //ok: label plus compound statement
As a footnote, since modern C allows to intermix declarations and statements you can also write:
case 1:; int x; //ok: label plus empty statement.
because an isolated ; is an empty statement, it can be used to satisfy the grammar whereever a no-op statement is needed.
Whether to use a ; or a { ... } is a matter of readability. In the end: example I'd use a ;, but in the case: I prefer the {...}.
while (...)
{
//...
goto end;
end:; //ok, empty statement
}
switch (...)
{
case 1: //ok, compound statement
{
int x;
}
}
Of course more creative solutions can be written, such as:
case 1: {} int x; //ok, label plus empty compound statement

C Rules About Case Labels
The rules of the C standard that prevent a declaration from following a case label are:
A case label must be followed by a statement (C 2011 [N1570] 6.8.1).
The C standard defines a statement as one of labeled-statement, compound-statement, expression-statement, selection-statement, iteration-statement, or jump-statement (6.8). None of these is a declaration.
The C standard treats declarations and statements separately. The rule that allows declarations to be largely mingled with statements is that a compound-statement is a list of block-items in braces (that is, { block-item-listopt }) (6.8.2), and a block-item is defined as a declaration or a statement. So, inside braces, you can mix declarations and statements. But a case label must be part of a statement; it is not a separate thing you can insert anywhere.
Declarations can be included inside a switch using two alternatives. One is to use an empty statement after the case label, as in:
case 123:
;
int foo;
…
Another is to use a compound statement after the case label, as in:
case 123:
{
int foo;
…
}
Generally, the latter is preferable, because the scope of foo is limited to the compound statement, so it cannot be used accidentally in another section of the switch statement.
Reasons For the Rules
I do not see a reason for this other than history. Originally, declarations were even more restricted than they are now. Inside functions, declarations had to be the first statements inside braces. You could not put a declaration after any statement. That has been relaxed in modern C, but why is there still a restriction on what follows a case label?
There cannot be a semantic reason that a declaration cannot follow a case label in modern C, because the empty-statement example above would have the same semantics as:
case 123:
int foo;
That is, the compiler would have to be prepared to create and initialize a new object at the same point in execution. Since it has to do that for the legal example code, it would be able to do it for this version too.
I also do not see a syntactic or grammatical barrier. The colon after the constant expression of a case label is pretty distinct. (The constant expression can have colons in it from ? : operators, but the first : not associated with a ? will be the end of the case label.) Once parsing reaches that colon, the current parsing state seems clean. I do not see why it could not recognize either a declaration or a statement there, just as it was prepared to do before the case.
(If somebody can find a problem in the grammar that would be caused by allowing a case label to be followed by a declaration, that would be interesting.)

First to say, the error you post is a syntax error related to the format of a case label statement. It allows you to use only an executable statement, and not a declaration. Put an empty statement before the declaration and you'll be ok. Try the following:
#include <stdio.h>
int main()
{
switch(3) {
int x = 3; /* the initializer is ignored by the compiler
* you can include declarations here, but as
* this code is never executed, the initializer
* never takes place. */
case 3:; /* <=== look this semicolon to allow the next declaration */
int y = 5;
printf("x = %d, y = %d\n", x, y);
break;
}
}
The first variable, x, will be declared properly, but the initializer will not be executed, as the case statement selected is the one corresponding to case label 3. The printf will print
x = 0, y = 5
(note: this happens on my machine, as the variable x is not initialized, Undefined Behaviour is expected)
In C, some evolution has been realized over the years, concerning the use of declarations in a block.
In ancient C, variables can be declared only at the beginning of a block (the piece of code between { and }, but this approach has been thrown for the new possibility of declaring a variable whenever you need it (even after some executable sentences after a block begin) But a case statement permits only to put an executable statement, and not a declaration, so that's the reason of your compiler error.
If you follow the ancient C way, you can only declare new local variables only after the opening { curly brace after the switch, as in:
switch(something) {
int var1;
case BLA_BLA:
/* use var1 here */
which, although counterintuitive, is valid since the old K&R code. The problem with this approach is that the variable is valid from the point of definition until the end of the switch statement, and so, it is global to all the case parts.
Another way, is the form you propose, in which you declare a new block by opening curly braces. This works also since the old K&R code, and makes it easier to control the scope of the variables defined. Personally, I prefer this second approach. A block is an executable statement, so there's no problem in using it as the labeled case statement of the switch (the declarations happen inside it).
Case labels don't delimit blocks of code, they label executable statements, so their syntax is specific to the case statement syntax (which finishes after the semicolon of the statement it is attached to, or the closing curly br)

Related

fscanf crash segmentation fault with gdb [duplicate]

In the following code, why is the variable i not assigned the value 1?
#include <stdio.h>
int main(void)
{
int val = 0;
switch (val) {
int i = 1; //i is defined here
case 0:
printf("value: %d\n", i);
break;
default:
printf("value: %d\n", i);
break;
}
return 0;
}
When I compile, I get a warning about i not being initialized despite int i = 1; that clearly initializes it
$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
printf("value %d\n", i);
^
If val = 0, then the output is 0.
If val = 1 or anything else, then the output is also 0.
Please explain to me why the variable i is declared but not defined inside the switch. The object whose identifier is i exists with automatic storage duration (within the block) but is never initialized. Why?
According to the C standard (6.8 Statements and blocks), emphasis mine:
3 A block allows a set of declarations and statements to be grouped
into one syntactic unit. The initializers of objects that have
automatic storage duration, and the variable length array declarators
of ordinary identifiers with block scope, are evaluated and the values
are stored in the objects (including storing an indeterminate value
in objects without an initializer) each time the declaration is
reached in the order of execution, as if it were a statement, and
within each declaration in the order that declarators appear.
And (6.8.4.2 The switch statement)
4 A switch statement causes control to jump to, into, or past the
statement that is the switch body, depending on the value of a
controlling expression, and on the presence of a default label and the
values of any case labels on or in the switch body. A case or default
label is accessible only within the closest enclosing switch
statement.
Thus the initializer of variable i is never evaluated because the declaration
switch (val) {
int i = 1; //i is defined here
//...
is not reached in the order of execution due to jumps to case labels and like any variable with the automatic storage duration has indeterminate value.
See also this normative example from 6.8.4.2/7:
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17; /* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with
automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero
value, the call to the printf function will access an indeterminate
value. Similarly, the call to the function f cannot be reached.
In the case when val is not zero, the execution jumps directly to the label default. This means that the variable i, while defined in the block, isn't initialized and its value is indeterminate.
6.8.2.4 The switch statement
A switch statement causes control to jump to, into, or past the statement that is the
switch body, depending on the value of a controlling expression, and on the presence of a
default label and the values of any case labels on or in the switch body. A case or
default label is accessible only within the closest enclosing switch statement.
Indeed, your i is declared inside the switch block, so it only exists inside the switch. However, its initialization is never reached, so it stays uninitialized when val is not 0.
It is a bit like the following code:
{
int i;
if (val==0) goto zerovalued;
else goto nonzerovalued;
i=1; // statement never reached
zerovalued:
i = 10;
printf("value:%d\n",i);
goto next;
nonzerovalued:
printf("value:%d\n",i);
goto next;
next:
return 0;
}
Intuitively, think of raw declaration like asking the compiler for some location (on the call frame in your call stack, or in a register, or whatever), and think of initialization as an assignment statement. Both are separate steps, and you could look at an initializing declaration in C like int i=1; as syntactic sugar for the raw declaration int i; followed by the initializing assignment i=1;.
(actually, things are slightly more complex e.g. with int i= i!=i; and even more complex in C++)
Line for initialization of i variable int i = 1; is never called because it does not belong to any of available cases.
The initialization of variables with automatic storage durations is detailed in C11 6.2.4p6:
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.
I.e. the lifetime of i in
switch(a) {
int i = 2;
case 1: printf("%d",i);
break;
default: printf("Hello\n");
}
is from { to }. Its value is indeterminate, unless the declaration int i = 2; is reached in the execution of the block. Since the declaration is before any case label, the declaration cannot be ever reached, since the switch jumps to the corresponding case label - and over the initialization.
Therefore i remains uninitialized. And since it does, and since it has its address never taken, the use of the uninitialized value to undefined behaviour C11 6.3.2.1p2:
[...] 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.
(Notice that the standard itself here words the contents in the clarifying parenthesis incorrectly - it is declared with an initializer but the initializer is not executed).

Usage of enclosing scope in C

Are inner-scopes ever used in C, or is this similar to something like a goto statement that isn't used too much in production code? The only thing I can think of that might use it is making something volatile temporarily, for example:
int main(void)
{
int a=1;
const int b=4;
printf("a=%d, b=%d\n", a, b);
{
int b=5;
printf("a=%d, b=%d\n", a, b);
}
}
But that seems like a pretty non-practical example. How would these be used in practice?
One case when blocks are required is defining local variables in a switch statement.
switch(foo())
{
case 0:
printf("no '{}' block generally required, except...\n");
break;
case 1:
{
int n = bar();
printf("%d\n", (n + 1) * n);
}
break;
//...
}
Without the {} block, the code would not compile because case 1: expects a statement right after, and declarations are not statements in C.
(Incidentally, the block is usually required in C++, too, though for an entirely different reason, because initialization of n would be skipped by other case labels.)
Are inner-scopes ever used in C, or is this similar to something like a goto statement that isn't used too much in production code?
Not so much by themselves, but inner scopes come up naturally all the time as the bodies of control statements such as loops and conditional statements. For example, a variable declared inside the body of a loop goes out of scope at the end of each iteration of the loop and is instantiated again during the next iteration.
we don't use them very often in production code. but this one very useful when you need your variable's scope very specific.
as far our use case, suppose you are coding in production code, and you want your some of variable's scope very specific . in this circumstances, we just use block {.....} to limit variable scope.

Is there objective reason against using braces even where not necessary?

Both kernel coding style and gnome's C style guide states that:
Do not unnecessarily use braces where a single statement will do.
if (condition)
action();
but at the same time it should be sometimes used, as in else branch of:
if (condition) {
do_this();
do_that();
} else {
otherwise();
}
Is there any technical or usability reasons to prefer it this way? Are there any objective reasons not to put the braces there everytime?
There are only stylistic and ease-of-editing-related reasons.
Whether you omit the brace or not, C compilers must act as if the braces were there (+ a pair around the whole iteration statement (if or if-else)).
6.8.4p3:
A selection statement is a block whose scope is a strict subset of the
scope of its enclosing block. Each associated substatement is also a
block whose scope is a strict subset of the scope of the selection
statement.
The existence of these implicit blocks can be nicely demonstrated with enums:
#include <stdio.h>
int main()
{
enum{ e=0};
printf("%d\n", (int)e);
if(1) printf("%d\n", (sizeof(enum{e=1}),(int)e));
if(sizeof(enum{e=2})) printf("%d\n", (int)e);
printf("%d\n", (int)e);
//prints 0 1 2 0
}
A similar rule also exists for iteration statements: 6.8.5p5.
These implicit blocks also mean that a compound literal defined inside an iteration or selection statement is limited to such an implicit block. That is why example http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p15 from the standard puts a compound literal in between a label an explicit goto instead of simply using a while statement, which would limit the scope of the literal, regardless of whether or not explicit braces were used.
While it may be tempting, don't ever do:
if (Ptr) Ptr = &(type){0}; //WRONG way to provide a default for Ptr
The above leads to UB (and actually nonworking wit gcc -O3) because of the scoping rules.
The correct way to do the above is either with:
type default_val = {0};
if (Ptr) Ptr = &default_val; //OK
or with:
Ptr = Ptr ? Ptr : &(type){0}; //OK
These implicit blocks are new in C99 and the inner ones (for selection statements (=ifs)) are well rationalized (C99RationaleV5.10.pdf, section 6.8) as aids in refactoring, preventing braces that are added from previously unbraced branches from changing meaning.
The outermost branch around the whole selection statements doesn't appear to be so well rationalized, unfortunately (more accurately, it's not rationalized at all). It appears copied from the rule for iterations statements, which appears to copy the C++ rules where for-loop-local variables are destructed at the very end of the whole for loop (as if the for loop were braced).
(Unfortunately, I think that for selection statement the outermost implicit {} does more harm than good as it prevents you from having macros that stack-allocate in just the scope of the caller but also need a check, because then you can only check such macros with ?: but not with if, which is weird.)
Well, there's one special case in which braces do need to be used: Suppose you have the following code:
if (a)
if (b)
f();
else g();
As it is indented, one could assume the else g(); statement belongs to the first if(a) statement, but C syntax rules say that it is interpreted as (now with braces):
if (a) {
if (b) {
f();
}
else {
g();
}
}
which actually means:
if (a) {
if (b) {
f();
}
else {
g();
}
}
in case you wanted the other possibility, then you must use braces. For example you can write it this way:
if (a) {
if (b)
f();
}
else
g();
which actually means:
if (a) {
if (b) {
f();
}
}
else {
g();
}
Note
As all elementary programming books recommend: If you are in doubt about operator precedence, then use parentheses; if you extend that to statements coding, if you are in doubt, use braces! :)
I hate those "if in doubt" guidelines with a passion. They engender laziness that pushes the cost onto the code reader.
Such guidelines lead to code that is more cluttered, slower to read, and therefore harder to debug.
If in doubt go and read the precedence table.
If still hesitating, write some test code to verify the interpretation.
Repeat this every time you code until precedence becomes second nature.
When you are sure you have a firm grasp of precedence, then and only then write the production code.
If you really can't manager that, then always break up your statements so that they contain no more than two levels of grouping parentheses in any one statement. If that means you have to make up lots of temporary variable names, that's a good thing.

Why can we not declare a variable after a switch case colon without using curly braces?

case 1:
{ //question is about this curly brace
int val;
scanf("%d", &val);
if(top1 == NULL){
enqueue(top1, val, bottom1);
}
else{
enqueue(top1, val);
}
break;
}
Without the curly brace after case 1: it gave an error: *
a label can only be part of a statement and a declaration is not a
statement: int val;
*
That is how the C grammar is defined. Variable declarations are not considered statements:
int x; //a declaration
int y = 3; //another declaration
x = y + 1; //a statement
A label is required to be followed by a statement. A label plus a declaration is not allowed.
foo: int x; //error
bar: x = y +1; //ok
That is, IMO, inconvenient:
while (...)
{
//...
goto end;
end: //error, no statement
}
And remember that a case is just a special kind of label, so:
case 1: int x; //error
case 2: x = y +1; //ok
The issue with braces is that in C they are used to build a compound statement, that of course is a kind of statement. The lines inside the compound statements can be both declarations and statements, all mixed (old C versions only allowed declarations at the beginning, not in the middle). So:
case 1: int x; //error: label plus declaration
case 2: { int x; } //ok: label plus compound statement
As a footnote, since modern C allows to intermix declarations and statements you can also write:
case 1:; int x; //ok: label plus empty statement.
because an isolated ; is an empty statement, it can be used to satisfy the grammar whereever a no-op statement is needed.
Whether to use a ; or a { ... } is a matter of readability. In the end: example I'd use a ;, but in the case: I prefer the {...}.
while (...)
{
//...
goto end;
end:; //ok, empty statement
}
switch (...)
{
case 1: //ok, compound statement
{
int x;
}
}
Of course more creative solutions can be written, such as:
case 1: {} int x; //ok, label plus empty compound statement
C Rules About Case Labels
The rules of the C standard that prevent a declaration from following a case label are:
A case label must be followed by a statement (C 2011 [N1570] 6.8.1).
The C standard defines a statement as one of labeled-statement, compound-statement, expression-statement, selection-statement, iteration-statement, or jump-statement (6.8). None of these is a declaration.
The C standard treats declarations and statements separately. The rule that allows declarations to be largely mingled with statements is that a compound-statement is a list of block-items in braces (that is, { block-item-listopt }) (6.8.2), and a block-item is defined as a declaration or a statement. So, inside braces, you can mix declarations and statements. But a case label must be part of a statement; it is not a separate thing you can insert anywhere.
Declarations can be included inside a switch using two alternatives. One is to use an empty statement after the case label, as in:
case 123:
;
int foo;
…
Another is to use a compound statement after the case label, as in:
case 123:
{
int foo;
…
}
Generally, the latter is preferable, because the scope of foo is limited to the compound statement, so it cannot be used accidentally in another section of the switch statement.
Reasons For the Rules
I do not see a reason for this other than history. Originally, declarations were even more restricted than they are now. Inside functions, declarations had to be the first statements inside braces. You could not put a declaration after any statement. That has been relaxed in modern C, but why is there still a restriction on what follows a case label?
There cannot be a semantic reason that a declaration cannot follow a case label in modern C, because the empty-statement example above would have the same semantics as:
case 123:
int foo;
That is, the compiler would have to be prepared to create and initialize a new object at the same point in execution. Since it has to do that for the legal example code, it would be able to do it for this version too.
I also do not see a syntactic or grammatical barrier. The colon after the constant expression of a case label is pretty distinct. (The constant expression can have colons in it from ? : operators, but the first : not associated with a ? will be the end of the case label.) Once parsing reaches that colon, the current parsing state seems clean. I do not see why it could not recognize either a declaration or a statement there, just as it was prepared to do before the case.
(If somebody can find a problem in the grammar that would be caused by allowing a case label to be followed by a declaration, that would be interesting.)
First to say, the error you post is a syntax error related to the format of a case label statement. It allows you to use only an executable statement, and not a declaration. Put an empty statement before the declaration and you'll be ok. Try the following:
#include <stdio.h>
int main()
{
switch(3) {
int x = 3; /* the initializer is ignored by the compiler
* you can include declarations here, but as
* this code is never executed, the initializer
* never takes place. */
case 3:; /* <=== look this semicolon to allow the next declaration */
int y = 5;
printf("x = %d, y = %d\n", x, y);
break;
}
}
The first variable, x, will be declared properly, but the initializer will not be executed, as the case statement selected is the one corresponding to case label 3. The printf will print
x = 0, y = 5
(note: this happens on my machine, as the variable x is not initialized, Undefined Behaviour is expected)
In C, some evolution has been realized over the years, concerning the use of declarations in a block.
In ancient C, variables can be declared only at the beginning of a block (the piece of code between { and }, but this approach has been thrown for the new possibility of declaring a variable whenever you need it (even after some executable sentences after a block begin) But a case statement permits only to put an executable statement, and not a declaration, so that's the reason of your compiler error.
If you follow the ancient C way, you can only declare new local variables only after the opening { curly brace after the switch, as in:
switch(something) {
int var1;
case BLA_BLA:
/* use var1 here */
which, although counterintuitive, is valid since the old K&R code. The problem with this approach is that the variable is valid from the point of definition until the end of the switch statement, and so, it is global to all the case parts.
Another way, is the form you propose, in which you declare a new block by opening curly braces. This works also since the old K&R code, and makes it easier to control the scope of the variables defined. Personally, I prefer this second approach. A block is an executable statement, so there's no problem in using it as the labeled case statement of the switch (the declarations happen inside it).
Case labels don't delimit blocks of code, they label executable statements, so their syntax is specific to the case statement syntax (which finishes after the semicolon of the statement it is attached to, or the closing curly br)

Variable i when was it defined but statement was never executed? [duplicate]

In the following code, why is the variable i not assigned the value 1?
#include <stdio.h>
int main(void)
{
int val = 0;
switch (val) {
int i = 1; //i is defined here
case 0:
printf("value: %d\n", i);
break;
default:
printf("value: %d\n", i);
break;
}
return 0;
}
When I compile, I get a warning about i not being initialized despite int i = 1; that clearly initializes it
$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
printf("value %d\n", i);
^
If val = 0, then the output is 0.
If val = 1 or anything else, then the output is also 0.
Please explain to me why the variable i is declared but not defined inside the switch. The object whose identifier is i exists with automatic storage duration (within the block) but is never initialized. Why?
According to the C standard (6.8 Statements and blocks), emphasis mine:
3 A block allows a set of declarations and statements to be grouped
into one syntactic unit. The initializers of objects that have
automatic storage duration, and the variable length array declarators
of ordinary identifiers with block scope, are evaluated and the values
are stored in the objects (including storing an indeterminate value
in objects without an initializer) each time the declaration is
reached in the order of execution, as if it were a statement, and
within each declaration in the order that declarators appear.
And (6.8.4.2 The switch statement)
4 A switch statement causes control to jump to, into, or past the
statement that is the switch body, depending on the value of a
controlling expression, and on the presence of a default label and the
values of any case labels on or in the switch body. A case or default
label is accessible only within the closest enclosing switch
statement.
Thus the initializer of variable i is never evaluated because the declaration
switch (val) {
int i = 1; //i is defined here
//...
is not reached in the order of execution due to jumps to case labels and like any variable with the automatic storage duration has indeterminate value.
See also this normative example from 6.8.4.2/7:
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17; /* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with
automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero
value, the call to the printf function will access an indeterminate
value. Similarly, the call to the function f cannot be reached.
In the case when val is not zero, the execution jumps directly to the label default. This means that the variable i, while defined in the block, isn't initialized and its value is indeterminate.
6.8.2.4 The switch statement
A switch statement causes control to jump to, into, or past the statement that is the
switch body, depending on the value of a controlling expression, and on the presence of a
default label and the values of any case labels on or in the switch body. A case or
default label is accessible only within the closest enclosing switch statement.
Indeed, your i is declared inside the switch block, so it only exists inside the switch. However, its initialization is never reached, so it stays uninitialized when val is not 0.
It is a bit like the following code:
{
int i;
if (val==0) goto zerovalued;
else goto nonzerovalued;
i=1; // statement never reached
zerovalued:
i = 10;
printf("value:%d\n",i);
goto next;
nonzerovalued:
printf("value:%d\n",i);
goto next;
next:
return 0;
}
Intuitively, think of raw declaration like asking the compiler for some location (on the call frame in your call stack, or in a register, or whatever), and think of initialization as an assignment statement. Both are separate steps, and you could look at an initializing declaration in C like int i=1; as syntactic sugar for the raw declaration int i; followed by the initializing assignment i=1;.
(actually, things are slightly more complex e.g. with int i= i!=i; and even more complex in C++)
Line for initialization of i variable int i = 1; is never called because it does not belong to any of available cases.
The initialization of variables with automatic storage durations is detailed in C11 6.2.4p6:
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.
I.e. the lifetime of i in
switch(a) {
int i = 2;
case 1: printf("%d",i);
break;
default: printf("Hello\n");
}
is from { to }. Its value is indeterminate, unless the declaration int i = 2; is reached in the execution of the block. Since the declaration is before any case label, the declaration cannot be ever reached, since the switch jumps to the corresponding case label - and over the initialization.
Therefore i remains uninitialized. And since it does, and since it has its address never taken, the use of the uninitialized value to undefined behaviour C11 6.3.2.1p2:
[...] 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.
(Notice that the standard itself here words the contents in the clarifying parenthesis incorrectly - it is declared with an initializer but the initializer is not executed).

Resources