This question already has answers here:
Nested case statements
(2 answers)
Closed 8 years ago.
This code works for some reason but it does not make sense at all.
#include <stdio.h>
int main(void)
{
switch(1)
{
case 0:
while(1)
{
case 1: puts("Works"); break;
}
}
return 0;
}
Can someone explain why it does work and what applications does this have?
The case labels are almost exactly like labels used by goto.1 If you think of your code in those terms, it should be clear that it's valid. Namely, you can consider a switch statement to be a glorified conditional goto.
That said, I would slap anyone who wrote code like that in a production environment.2
In fact, they are both listed in the same grammar section (6.8.1) of the C99 standard.
Yes, this is almost identical to Duff's device, but that last had any practical use decades ago.
The reason this works is somewhat non-intuitive: case labels of a switch statement act very much like regular labels, i.e. the ones designed for use with goto statement. You can place such labels anywhere in your code.
It turns out that the same rule applies to the case label: you can place them anywhere inside their corresponding switch statement, which incidentally includes the bodies of any nested loops.
The reasons why you may want to place labels inside control statements within the body of your switch statement are even less intuitive: it turns out that you can perform loop unrolling with a cumbersome-looking but very intuitive construct called Duff's Device. It is this construct that lead to popularizing the idea of embedding case labels inside other control structures within switch statements.
You can interleave statements through the labels of switch, since they're just labels. What happens here is:
you have an infinite loop defined using while (1);
the switch (1) statement jumps to the case 1: label;
where "Works" is printed, then break; exits the inifinite loop.
Related
Programming in C I found out it was convenient in a switch-case to make little groups of cases by giving them the same name and add a number to it like:
case initiating:
break;
case (initiating+1):
break;
etc etc.
Currently I am still using brackets around around (initializing+1). But I wonder, do I have to do that?
would
case (initiating+2):
work?
I could not really find an anwser.
As you can see here a switch requires a constant expression. Every label should be known at compile time. Brackets are not required but can improve readability. Be sure that you really need (initiating + 2). 2 is a magic number and does not provide any additional information to the reader of your program. Using an enum will give you the same result but better readability.
The best way to verify if this works is by simply writing an example down and compiling it.
If initiating is a constant it should work.
Case labels are not required to be enclosed in parentheses, even if they are expressions containing arithmetic operations.
In a program, I saw code like this (simplyfied):
switch (x){
case 1:
//dostuff
break;
/*___________________*/
//Here it is important
case 2:
default:
//dostuff
break;
}
Now I was wondering why someone writes a case and leaves it empty before the default case.
(Clearly it would make sense before another case).
I know that in C, there is a fallthrough if there is no break, so if x is 2, the program runs in the case 2: part, and directly falls through to the default-case.
So is the case 2:-case useless, since there is no code in it, and default will be done also without the label, so the same things are done with and without the case?
Is there a reason to write code like this (like easier modification when maintaining, but in my opinion not really relevant), or did the programmer just not remove it by mistake?
I have used switch several times in different languages, but never would have needed such code...
There is no reason for it. The explicit case 2 could be an attempt of writing self-documenting code, but here it doesn't really add anything, as the code lacks meaningful comments that explain what's unique with case 2.
Sometimes you could write code like this to explicitly document to the reader that you have considered all possible values that a variable can have. For example such self-documenting code might make sense with enums.
In this case, it really just looks like it is code still in development. Or it's some sloppily written left-overs that made it to the production code.
Last time I used MISRA C (embedded C rules for vehicles), all switch statements had to have a default clause. Potentially, this could be a reason for what you are seeing, as this would mean that ALL values passed into the switch would do something. Admittedly, that would mean the case 2 is redundant, but it might make things clearer when reading the code as a whole. It could also be some sort of embedded compiler optimisation (sometimes embedded compilers generate more efficient code when given a switch, rather than several ifs).
I've recently read this page about strange C snippet codes. Most of them was understandable. But I can't understand this one:
switch(c & 3) while((c -= 4) >= 0){
foo(); case 3:
foo(); case 2:
foo(); case 1:
foo(); case 0:
}
Can anyone please help me out what logic is behind of this code? And how does it work?
The duff's device comment should explain the background well enough, so I ll try to explain this very case:
The switch checks the last 2 bits of c, and jumps to the respective case-statement inside the while loop. The code below the case statement is also executed. Control then reaches the end of the while loop, so it jumps to the beginning again to check if the condition is still true. If it is, all the statements inside the loop are executed, and the loop is repeated until the condition is false. The initial switch usually ensures that c will be a multiple of 4 when the while loop runs for the first time.
Edit: duff's device on Wikipedia. Adding link to make more obvious what I meant with "the duff's device comment". Please consider upvoting interjay's comment should you upvote this answer.
May be possible duplicate but couldn't have found the same.
Suppose I have following C code :
int a;
printf("Enter number :");
scanf("%d",&a); // suppose entered only an integer
// ignoring return value of scanf()
I got a case to check whether a is zero or non-zero.
if(a)
printf("%d is non-zero",a);
else
printf("%d is zero",a);
Everything is fine using if-else and I also know the other variations of if-else to achieve this . But problem comes with the switch-case as it says that we can implement everything in switch-case which we can do in if-else. But the following code fails.
switch(a)
{
case a:
printf("%d is non-zero",a);
break;
default:
printf("%d is zero",a);
break;
}
Also I know to reverse the case in the above code like this below will work and I will have my answer.
switch(a)
{
case 0:
printf("%d is zero",a);
break;
default :
printf("%d is non-zero",a);
break;
}
But the question is, Why ? Why if(a) is valid while case a: is not ? Is switch-case a compile time operation and if() run-time ?
The reason is that switch cases can be implemented as jump tables (typically using unconditional branch instructions). So they have to be resolved at compile time.
This makes them faster than ifs so it is better to use them when possible.
Case expressions must be constants. a is a variable, so it is not allowed. 0 is a constant, so that's fine. Only allowing constant expressions means that it is easier for the compiler to optimize the code.
The expression for the condition of an if statement has no such constraint.
As others have said, it's the way the language was defined.
If you have
int x, y, z;
int a;
... some code calculates x, y, z and a ...
switch(a)
{
case x:
.. do stuff here ...
break;
case y:
.. some more stuff ...
break;
case z:
... another bit of code ....
break;
}
the compiler can not figure out beforehand, at time of compilation where a should go if it's 1, 2, 3, 99, 465 or 5113212. So the code here is no more efficient than if we did
if (a == x) ... do stuff here ...
else if (a == y) ... some more stuff
else if (a == z) ... another bit of code
Further, what if x and y are the same value. Do we want BOTH do stuff and some more stuff to be executed, or just the one - and which one, the first or the second. What if the compiler re-orders the compares so that they are in a different order, because it's more efficient?
Switch is mainly intended for when you have a lot of choices of something, but each choice is known when you build the code. If that's not the case, you need something else.
Additional Information want to share Wiki
If the range of input values is identifiably 'small' and has only a few gaps, some
compilers that incorporate an optimizer may actually implement the switch statement as
a jump table or an array of indexed function pointers instead of a lengthy series of
conditional instructions. This allows the switch statement to determine instantly what
branch to execute without having to go through a list of comparisons.
It's a design decision by the creators of the language. IF case labels are constant, the compiler can optimize some cases by using a jump table. If they are not, the code will be equivalent to the multi-way if statement anyway, and the potential improvement goes away.
There is no problem defining a switch statement with variable case labels, or even different conditions for each branch, it is just that the designers of C didn't do that. Likely because they didn't see that as an advantage for the code they were writing.
The construct exists in other languages, like the COBOL I sometimes use. There it is not unusual to have a degenerated version like:
EVALUATE TRUE
WHEN x IS EQUAL TO 7
Do something
WHEN y IS LESS THAN 12
Do something else
WHEN z
Do yet another thing
END-EVALUATE
Here we have the if-else if-else chain masked as a switch (EVALUATE), which works by evaluating the conditions in order until it matches the first value.
In C the designers didn't want this, because it offers absolutely no performance advantage over the chained if-statements. On the other hand, if we require that all the conditions are constants...
Beyond the compile-time/jump-table problem, if and switch are not the same and even if case would accept a variable those two codes wouldn't have the same behavior. if body is evaluated if and only if the condition expression results in a non-zero value, while a case is entered if and only if the controlling expression and the label have the same value.
There is a big difference between if-then-else and switch statements, remember that breaks are not mandatory and execution falls through all the cases if nothing stops it. This behavior is really similar to a jump table, since inside a switch execution simply jump somewhere and goes on until it finds a break. However this use is rare, but it could be useful and easier to do than the if-then-else version.
The standard requires labels to be compile-time constants, and as other people already say, the idea behind it is a jump table for performance. Even if it's not mandatory (the C standard needs to be flexible), C99 rationale document seems to confirm this:
Case ranges of the form, lo .. hi, were seriously considered, but ultimately not adopted in the Standard on the grounds that it added no new capability, just a problematic coding convenience. The construct seems to promise more than it could be mandated to deliver:
A great deal of code or jump table space might be generated for an innocent-looking case range such as 0 .. 65535.
The range 'A' .. 'Z' would specify all the integers between the character code for
“upper-case-A” and that for “upper-case-Z”. In some common character sets this range
would include non-alphabetic characters, and in others it might not include all the
alphabetic characters, especially in non-English character sets.
Wikipedia has an article about jump tables.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I was hesitating to ask this, since it seems very easy.
What is wrong in this pseudocode?
In the switching software (written in C), there was;
a long "do... while" construct, which contained
a "switch" statement, which contained
an "if' clause, which contained
a "break," which was intended for the "if" clause
but instead broke from the "switch" statement.
This caused a crash of the telephone system in 1990
(See: http://users.csc.calpoly.edu/~jdalbey/SWE/Papers/att_collapse.html).
I need a very simple, explanation, why this code is wrong. I think the most simple answer is that within a if clause a break is not possible? So what statement needs to be written instead of a break within a if clause for getting the wanted effect, which is breaking the if clause?
I suspect that the description / pseudo-code is incorrect when it says:
a "break," which was intended for the "if" clause
It would make sense if that was meant to be:
a break, which was intended to terminate the do while loop
The problem description then makes sense.
do
{
...
switch (...)
{
case ...:
...
break;
...
case ...:
...
if (critical_condition())
break; // Intended to exit loop - actually exits switch only
...
break; // Terminates the case in the switch
}
} while (!time_to_stop());
Reading the URL referenced in the question, the pseudo-code there is:
In pseudocode, the program read as follows:
1 while (ring receive buffer not empty
and side buffer not empty) DO
2 Initialize pointer to first message in side buffer
or ring receive buffer
3 get copy of buffer
4 switch (message)
5 case (incoming_message):
6 if (sending switch is out of service) DO
7 if (ring write buffer is empty) DO
8 send "in service" to status map
9 else
10 break
END IF
11 process incoming message, set up pointers to
optional parameters
12 break
END SWITCH
13 do optional parameter work
When the destination switch received the second of the two closely timed messages while it was still busy with the first (buffer not empty, line 7), the program should have dropped out of the if clause (line 7), processed the incoming message, and set up the pointers to the database (line 11). Instead, because of the break statement in the else clause (line 10), the program dropped out of the case statement entirely and began doing optional parameter work which overwrote the data (line 13). Error correction software detected the overwrite and shut the switch down while it couls [sic] reset. Because every switch contained the same software, the resets cascaded down the network, incapacitating the system.
This agrees with my hypothesis - the pseudo-code in the question is an incorrect characterization of the pseudo-code in the paper.
Another reference on the same subject (found via a Google search 'att crash 1990 4ess') says:
Error Description
What was reported in ACM's Software Engineering Notes [Reference 2] is that the software defect was traced to an elementary programming error, which is described as follows:
In the offending "C" program text there was a construct of the form: [Erratic indentation as in original]
/* ``C'' Fragment to Illustrate AT&T Defect */
do {
switch expression {
...
case (value):
if (logical) {
sequence of statements
break
}
else
{
another sequence of statements
}
statements after if...else statement
}
statements after case statement
} while (expression)
statements after do...while statement
Programming Mistake Described
The mistake is that the programmer thought that the break statement applied to the if statement in the above passage, was clearly never exercised. If it had been, then the testers would have noticed the abnormal behavior and would have been able to corr [sic]
The only caveat to this statement is the following: it is possible that tests applied to the code contain information which would reveal the error; however, if the testers do not examine the output and notice the error, then the deficiency is not with th [sic]
In the case of a misplaced break statement, it is very likely that the error would have been detected.
References
"Can We Trust Our Software?", Newsweek, 29 January 1990.
ACM SIGSOFT, Software Engineering Notes, Vol. 15, No. 2, Page 11ff, April 1990.
Apparently, the programmer really did just think that break would end the if statement; it was a small mental blackout that led to a large real-world blackout.
If I understand it right, the else block where the incriminated break statement occurs is merely part of that "one line bug" as it's called before1. I don't see any good reason for that else to exist there, unless those "certain types of messages" that received optimization were thought be the only occurrence of a non-empty buffer while processing a message. The description you linked misses good deals of domain knowledge, without which I at least cannot fully understand that piece of code. I'll try anyway to give an explanation.
As break statements can only refer to a switch or a loop, I can assume that:
hypothesis #1
the original coder intended to "speed processing of certain types of messages" by cutting the while statement with such a break. However, the nesting misled the guy and let him oversee that the switch statement and not the while was to be affected by the break.
hypothesis #2
the original coder really intended to quickly end the switch statement, but put that break too early and forgot to eventually update pointers to optional parameters, e.g. marking somehow that no optional parameters were provided with the current message.
I would thus call it "two lines bug"