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.
Related
This question already has answers here:
Two semicolons inside a for-loop parentheses
(4 answers)
Endless loop in C/C++ [closed]
(12 answers)
Closed 4 years ago.
I was searching for an explanation for the "double semicolon" in the code:
for(;;){}
Original question.
I do not have enough reputation to even leave a comment so I need to create a new question.
My question is,
What does an "extra" semicolon means. Is this "double semicolon" or "extra semicolon" used for "something else"?
The question comes from a person with no knowledge of programing, just powered on my first arduino kit and feel like a child when LED blinked.
But it is the reality that the questions, like general occurence, are radiating the actual knowledge of the person asking the question.
And beyond "personal preference" in using while(1) or for(;;) is it helpful in using for(;;) where you do not have enough room for the code itself?
I have updated the question.
Thank you for the quick explanation. You opened my eyes with the idea of not using anything in for loop = ). From basic high school knowledge I am aware of for loop so thank you for that.
So for(;;) returns TRUE "by default"?
And my last line about the size of the code?
Or it is compiled and using for or while actually does not affect the compiled code size?
A for loop is often written
int i;
for (i = 0; i < 6; i++) {
...
}
Before the first semicolon, there is code that is run once, before the loop starts. By omitting this, you just have nothing happen before the loop starts.
Between the semicolons, there is the condition that is checked after every loop, and the for loop end if this evaluates to false. By omitting this, you have no check, and it always continues.
After the second semicolon is code that is run after every cycle, before that condition is evaluated. Omitting this just runs additional code at that time.
The semicolons are always there, there is nothing special about the face that they are adjacent. You could replace that with
for ( ; ; )
and have the same effect.
While for (;;) itself does not return true (It doesn't return anything), the second field in the parentheses of the for always causes the loop to continue if it is empty. There's no reason for this other than that someone thought it would be convenient. See https://stackoverflow.com/a/13147054/5567382. It states that an empty condition is always replaced by a non-zero constant, which is the case where the loop continues.
As for size and efficiency, it may vary depending on the compiler, but I don't see any reason that one would be more efficient than the other, though it's really up to the compiler. It could be that a compiler would allow the program to evaluate as non-zero a constant in the condition of a while loop, but recognise an empty for loop condition and skip comparison, though I would expect the compiler to optimize the while loop better. (A long way of saying that I don't know, but it depends on a lot.)
I have seen code (in C) which contains something similar to:
for(;;){
}
How would this work, and why is it used in any instance?
It is the idiomatic way to have an infinite loop.
In the C Programming Language book, by Kernighan & Ritchie book it was introduced in section 3.5:
for (;;) {
...
}
is an infinite loop, presumably to be broken by other means, such as a break or return.
is an infinite loop something like
while(true)
{}
This is equivalent to an infinite loop, as many other have explained. However, few of them explained why this executes as an infinite loop.
A for loop can be broken down into three parts:
for(initialization(s); condition(s); looping command(s))
None of these fields are actually required. Without a condition provided, there's nothing to stop the command from running. This is because the for loop looks for a false statement. Without conditions provided, nothing is false, therefore the loop runs indefinitely.
Therefore to cause a for loop to be infinite, all you need is to not provide a condition. This means that this is also a valid infinite loop:
for(int i = 0;; i++)
printf("Iteration: %i\n", i);
For readability, and to make sure that the second semi-colon isn't a typo, some programmers might put a space between them, or put true as the condition.
Honestly, I prefer while(true), as this is a clear infinite loop. Using while(1) works as well, but '1' is an integer, not a boolean. While it is equivalent to true, it does not always mean true.
Between these three types of infinite loops, for(;;) has the fewest characters (7). while(1) comes second at 8 characters, and while(true) at 11.
I suppose that certain programmers prefer a low byte count over a readable program, but I wouldn't recommend using for(;;). While equivalent, I believe that using while(true) is better practice.
A for loop needs three expressions, which are separated by semicolons, and are completely optional:
An initialization (e.g. int i=0)
A condition (e.g. i < 10)
An afterthought (e.g. i++)
In this case, the three expressions are empty, and thus there's no condition that will make the loop stop, thus creating an infinite loop, unless a flow control instruction like break (which will exit the loop) or return is used.
Its an infinite loop. Its equivalent to:
while (true) {
}
The C# compiler directly translates for (; ;) into the exact same construct as while (true).
Infinite loop
same as
while(true){}
the code for(;;){} or while(true){} or while(1){} all represent infinite loops.
An infinite loop is something to be expected in a software system that is expected to run and "unlimited" amount of time. Every OS has at least one - it's how a background task or idle task is implemented.
Real Time systems use infinite loops as well because the system has to handle events which are asynchronous;
Basically anything that runs software uses infinite loops in one way or another.
I don't know why no one answered why people do this instead of while(true): It's because while(true) will often generate a compilation warning that the condition of the loop is constant.
This kind of infinite loop can be used in microcontroleurs. In your main function, you initialize the basic functions of your microcontroleur, then put a while (1).
void MAIN(void)
{
Init_Device();
while(1);
}
The microcontroleur will then do nothing but wait for interrupts of internal or external devices (like a timer overflow, or UART data ready to be read), and react to these interrupts.
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.
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.
I'm using PC Lint for the first time. I was "linting" my code, when PC Lint warns me about my while(TRUE).
This is what it says:
716: while(1) ... -- A construct of the form while(1) ... was found.
Whereas this represents a constant in a context expecting a Boolean,
it may reflect a programming policy whereby infinite loops are
prefixed with this construct. Hence it is given a separate number and
has been placed in the informational category. The more conventional
form of infinite loop prefix is for(;;).
I didn't understand this statement. Can anyone help me to understand it?
The text says that although while(TRUE) (which gets preprocessed into while(1)) is a perfectly valid infinite loop, the more conventional form of writing an infinite loop is
for(;;)
{
...
}
because it doesn't use any values at all and therefore is less error-prone.
It says that the more conventional infinite loop is for(;;), which I'd say is an arguable assertion, and that it's categorized this construct as an "informational category" finding - I suspect that if you used the for(;;) instead it would go away. I've always written these as while(1) and never for(;;) myself. If it does what you expect, I'd ignore the findings of PC LINT on this one or switch it if you're worried about someone redefining TRUE because if someone redefined TRUE your loop wouldn't run at all.