Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do..while inside switch

I found some C code that has this structure:

switch (n) {
  do {
     case 1:
       // do some things
       if (some condition)
          goto go_on;
     case 2:
       // do some things
       if (some condition)
          goto go_on;
     case 3:
       // do some things
       if (some condition)
          goto go_on;
  } while (1);
  do {
     case 4:
       // do some things
       if (some condition)
          goto go_on;
     case 5:
       // do some things
       if (some condition)
          goto go_on;
  } while (1);
}

go_on:

I programmed in C for years (many years ago) and would have thought this would be a syntax error. I think it has something to do with optimizing the loop, but I was wondering if someone could explain what it does. What happens when a while(1) is reached, does it effectively go back to the switch? And in particular why are there two do..while's in there?

By the way, I don't want to start a discussion on the use of goto or if this is bad design. I didn't write this and I assume the author did it this way because it made the loop run as fast as possible.

like image 965
kh99 Avatar asked Sep 29 '14 14:09

kh99


People also ask

Can we use while inside switch case?

The switch and case only affect the initial entry into the while loops. There are 2 while loops to accommodate the original coder's flow to jump into intermediate points into 1 of 2 loops.

Can you do an OR statement in a switch?

The logical OR operator (||) will not work in a switch case as one might think, only the first argument will be considered at execution time.

Can I use switch case in for loop in C?

Yes. You can. How do I put my switch case operation inside a loop in C#?


2 Answers

Let me rewrite this code for you, maybe this will make it more obvious. The following code is more or less equivalent to the one you posted:

if (n == 1) goto ONE;
if (n == 2) goto TWO;
if (n == 3) goto THREE;
if (n == 4) goto FOUR;
if (n == 5) goto FIVE;
goto SKIP_ALL;

while (true) {
ONE:
    // do some things
    if (some condition) goto go_on;
TWO:
    // do some things
    if (some condition) goto go_on;
THREE:
    // do some things
    if (some condition) goto go_on;
}

while (true) {
FOUR:
    // do some things
    if (some condition) goto go_on;
FIVE:
    // do some things
    if (some condition) goto go_on;
}

SKIP_ALL:
go_on:

The loops are within the switch, they don't cause the switch to take place more often. The switch basically decides to which loop the program flow jumps and at which instruction within that loop it starts. Once it jumped there, the loops continue normally. Also note that a switch is usually faster than all those if statements.

And no, goto is not bad design in general. A switch is merely just a goto and a switch is not bad design. In reality, pretty much every code branch within a function/method executed on a CPU or within a VM is a simple goto (sometimes a conditional one, sometimes not). It's just that goto is most primitive, low level way of branching and tells the reader little about the intention. Whenever there is a higher level one, one that makes your intention more obvious, it is preferable to use that one instead. Using a goto is only bad design if you could have easily written the same kind of code without using a goto and it would not have been much worse either. In some (albeit very rare) cases a goto is almost unavoidable or any attempt to avoid it creates ugly, unreadable, very complex code or very poor performance as a result.

The "goto considered harmful" essay comes from a time, where some people were using goto for everything: For if/else code branches, for loops, for switches, to break out of loops/switches, to avoid recursion, etc. And if you overuse goto like that and if you do things like jumping to a label that immediately jumps to another label, people lose overview. Code like this is unreadable and extremely hard to debug. This is the way how you write assembly code, but that shouldn't be the way how we write C code.

like image 176
Mecki Avatar answered Oct 14 '22 12:10

Mecki


Equivalent code that may better show program flow.

Akin to Duff's device to allow entering a loop at some intermediate position. @ tangrs

General frowned upon these days as 1) compilers typically do a better job at optimizing & 2) as OP has found, can easily obscure the meaning of the code. Use with caution.

In OP's code, after either while condition, program flow does not return back to the switch statement. The switch and case only affect the initial entry into the while loops.

if (n == '1') goto case1;
if (n == '2') goto case2;
...
if (n == '5') goto case5;
goto go_on;

do {
  case1:
     // do some things
     if (some condition) goto go_on;
  case2:
     // do some things
     if (some condition) goto go_on;
  case3:
     // do some things
     if (some condition) goto go_on;
} while (1);

do {
  case4:
    // do some things
    if (some condition) goto go_on;
  case5:
    // do some things
    if (some condition) goto go_on;
} while (1);

go_on:

[Edit]

There are 2 while loops to accommodate the original coder's flow to jump into intermediate points into 1 of 2 loops.

Candidate re-write follows. Having access to the overall code, certainly a cleaner solution can be had.

int n2 = n;  // Only evaluate n once as in the switch statement.
if (n2 >= 1) {
  if (n2 <= 3) {
    while (1) {
      if (n2 <= 1) {
        // do some things
        if (some condition) { break; }
      }
      if (n2 <= 2) {
        // do some things
        if (some condition) { break; }
      }
      // do some things
      if (some condition) { break; }
      n2 = 1;
    }

  else if (n2 <= 5) {
    while (1) {
      if (n2 <= 4) {
        // do some things
        if (some condition) { break; }
      }
      // do some things
      if (some condition) { break; }
      n2 = 4;
    }
  }

}
like image 39
chux - Reinstate Monica Avatar answered Oct 14 '22 11:10

chux - Reinstate Monica