13

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?

sakthi
  • 675
  • 2
  • 10
  • 18
  • I am asking about before case label variable definition friend. Because i am trying to use i is local variable inside switch. – sakthi Mar 08 '16 at 11:43
  • 3
    Please don't close as a C++ duplicate. Find a C version. – 2501 Mar 08 '16 at 11:46
  • 2
    This is not a duplicate of the post by @user3121023 since in the linked question, the declaration of `i` is **inside** a `case` statement, so you simply wrap it around brackets. In this case the declaration of `i` is outside any `case` statement, I am not sure this is valid C. – Holt Mar 08 '16 at 11:51
  • I didn't know the answer to this so had to google it. There is an explanation [here](https://www.securecoding.cert.org/confluence/display/c/DCL41C.+Do+not+declare+variables+inside+a+switch+statement+before+the+first+case+label): – kaylum Mar 08 '16 at 11:52
  • Ok friend. But i am trying to say inside switch(before lable) why definition and function call also not working. – sakthi Mar 08 '16 at 11:55
  • 1
    @undur_gongor I fixed that one, apparently I was one of the people who incorrectly closed it as a dupe. It's re-opened now, since it was no duplicate. The answers there were so-so though, so it is probably better to close that one as a dupe to this :) – Lundin Mar 08 '16 at 12:01

5 Answers5

15

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.

Community
  • 1
  • 1
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Nice answer, I think the part from 6.8 is necessary to explain this. Could you per chance take the example I cited in my answer and add it to your answer too? (Since it is a normative example. Feel free to copy/paste the formatting stuff from my answer) Then I'll delete my answer in favour of yours. – Lundin Mar 08 '16 at 12:10
  • I think, the key part is "as if it were a statement". It should stand out even more. – undur_gongor Mar 08 '16 at 12:14
  • 1
    In fact, any answer that doesn't mention automatic storage duration is incomplete. Because if you declare `i` as `static` then voila, suddenly the code behaves as expected. Meaning this has not so much to do with the behavior of the switch statement, as with the rule of initialization of variables with automatic storage duration. – Lundin Mar 08 '16 at 12:26
4

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

  1. 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.
Community
  • 1
  • 1
2501
  • 25,460
  • 4
  • 47
  • 87
3

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++)

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

Line for initialization of i variable int i = 1; is never called because it does not belong to any of available cases.

1

The initialization of variables with automatic storage durations is detailed in C11 6.2.4p6:

  1. 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:

  1. [...] 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).