Domanda

Given this code :

typedef enum {
    Test0 = 0,
    Test1 = 1,
    Test3 = 3
} TestEnum;

What happens if this occurs?

TestEnum a = 2;

switch(a) {
    case Test0: println("0"); break;
    case Test1: println("1"); break;
    case Test3: println("3"); break;
}

I'm trying not to have a default case in my switch so the compiler will tell me when I'm not dealing with any new values in my enum (the enum changes fairly frequently and is provided by an external library).

What's the best way to deal with my method being given garbage? Can I just let the switch deal with the value or will it have unintended consequences?


Lots of comments/answer have said nothing happens because it's not handled which is (a) great news but (b) kinda goes against how compilers implement switches.

I've learned that compilers don't just go through each option one by one, they use a lookup table of function pointers to implement a switch. That way we don't have to else-if on each case until we reach the one that matches.

Given the switch above, the compiler would create 3 functions that would look something like this . . .

address     code

    1       a = 2
    2       locations = [100, 200, 0, 300] // Compiler makes this lookup for us
    3       goto locations[a] // This single line is our switch :)
    4       carry on with the rest of the app 

  100       println("1")
  101       goto 4
  200       println("2")
  201       goto 4
  300       println("3")
  301       goto 4

OK, so my simplified pseudo-code sucks but you get the idea - this lookup approach allows a C switch to be 1 instruction, regardless of the number of options.

So, my question is - what happens if the value you pass in isn't in that lookup array (locations). I can understand that locations[2] is 0 so it knows at runtime not to go there but what if I pass in 4 - or -1?

È stato utile?

Soluzione

The switch doesn't match with any of the cases. It won't enter the main body.

To address your edit. Are you sure about your idea of the switch being compiled? I compiled this function with GCC and -02

    typedef enum {
        Test0 = 0,
        Test1 = 1,
        Test3 = 3
    } TestEnum;

    void myfunc(TestEnum a)
    {

    switch(a) {
        case Test0: println("0"); break;
        case Test1: println("1"); break;
        case Test3: println("3"); break;
    }

    return;
    }

The assembly is this:

    .file   "myfunc.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "0"
.LC1:
    .string "1"
.LC2:
    .string "3"
    .text
    .p2align 4,,15
    .globl  myfunc
    .type   myfunc, @function
myfunc:
.LFB0:
    .cfi_startproc
    cmpl    $1, %edi
    je  .L3
    jb  .L4
    cmpl    $3, %edi
    jne .L8
    movl    $.LC2, %edi
    xorl    %eax, %eax
    jmp println
    .p2align 4,,10
    .p2align 3
.L8:
    rep ret
    .p2align 4,,10
    .p2align 3
.L4:
    movl    $.LC0, %edi
    xorl    %eax, %eax
    jmp println
    .p2align 4,,10
    .p2align 3
.L3:
    movl    $.LC1, %edi
    xorl    %eax, %eax
    jmp println
    .cfi_endproc
.LFE0:
    .size   myfunc, .-myfunc
    .ident  "GCC: (GNU) 4.8.2 20140206 (prerelease)"
    .section    .note.GNU-stack,"",@progbits

You can see that it's doing compare and jumps. There is no indirect branches like you suspected.

Altri suggerimenti

A switch statement is a kind of calculated goto. Each case statement is a label to which execution can jump. You can think of the evaluation as being (1) the expression in the switch is evaluated to compute a value, (2) the computed value is compared against each of the case statements, (3) if a match is found then execution jumps to that case statement.

The reason for the break statement is to provide a jump from an individual case section to the end of the switch body. Without a break statement or a return statement or some other kind of a jump, execution will just continue into the following case statement.

The default case is designed to allow you to catch those times when none of the individual cases match the switch computed value. If you do not provide a default case then a kind of hidden default is added at the end of the switch body as a place holder. In other words, if there is no default case and none of the case statement values match the computed value of the switch, then the entire switch body is skipped and execution continues after the closing brace.

The most common use of the default case is to provide a the default action if none of the cases match. However another typical use of default is to catch when the switch computed value does not match any of the case statements in order to generate an ASSERT or a log.

For an interesting use of switch() see Duff's device for serial copy using case statements with fall through.

In C the enum is treated more loosely as a type than it is in C++ and is basically just symbolic, syntactical sugar for an int though the underlying type used to represent an enum value may be an int or smaller depending on the compiler implementation. See C Enumeration Specifiers for the basic characteristics of C enum.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top