xavier roche's homework

random thoughts and sometimes pieces of code

An Unusual Case of Case (/switch)

I have been programming in C for something like 20 years, and I don’t consider myself as illiterate in coding. But time to time, I still discover little gems and curiosities, even in the very deep roots of the C language.

The latest gem was this code snippet:

1
2
3
4
5
6
7
  switch (step) {
    while (1) {
      case step_A:
      case step_B:
        ...
     }
  }

No, there’s no mistake – there’s a while interleaved between switch and case. Darn.

The fact is that switch is actually a disguised dispatch of goto (horror!) equivalent instructions, and case a disguised label case. As you can jump from/to nested loops or conditions, the code is perfectly valid (and logical).

The above code can be rewritten as something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  do {
    if (step == step_A) {
      goto A;
    } else if (step == step_B) {
      goto B;
    } else {
      goto E;
    }
    while (1) {
      A:
      B:
        ...
    }
    E:
  } while(0);

This was confirmed by the specialists of comp.lang.c, with interesting remarks:

  • This coding technic is actually pretty classic in highly-optimized code (shame on me), and called Duff’s device, and allows, for example, to copy blocs of memory in a single row, inside a reduced loop, rather than copying the remainder outside the loop, and copying full chunks inside it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void somewhat_fast_memcpy(char *to, const char *from, const size_t count) {
  size_t n = (count + 7) / 8;
  switch(count % 8) {
    case 0:
    do {
      *to = *from++;
      case 7: *to = *from++;
      case 6: *to = *from++;
      case 5: *to = *from++;
      case 4: *to = *from++;
      case 3: *to = *from++;
      case 2: *to = *from++;
      case 1: *to = *from++;
    } while(--n > 0);
  }
}
  • You may end up with the same issues as for the goto equivalent ; such as uninitialized variables: (note that clang detect this case, but not my version of gcc)
1
2
3
4
5
6
7
8
  int i;
  switch (step) {
    for (i = 0; i < loops; i++) {
      case step_A: // WARNING: uninitialized i
      case step_B:
        ...
     }
  }
  • There’s one constraint you have to follow (please take your breath before reading): “If a switch statement has an associated case or default label within the scope of an identifier with a variably modified type, the entire switch statement shall be within the scope of that identifier.”. Basically, it means that if you are foolish enough to use this coding style, AND foolish enough to use variable-length-variables (such as dynamic arrays), then the compiler has the right to put a contract on you.

  • Abuse of interleaving of switch/case and other constructs may turn your code into a steaming pile of crap. Did I tell you that switch/case was somewhat equivalent to goto?

TL;DR: take an aspirin and good nite.

Comments