This question already has answers here:
Variable definition inside switch statement
(5 answers)
Closed 5 years ago.
Code:
int main()
{
int a=1;
switch(a)
{
int b=20;
case 1:
printf("b is %d\n",b);
break;
default:
printf("b is %d\n",b);
break;
}
return 0;
}
Output:
It prints some garbage value for b
when does the declaration of b takes place here
Why b is not initialized with 20 here???
Because memory will be allocated for int b but when the application is run "b = 20" will never be evaluated.
This is because your switch-statement will jump down to either case 1:1 or default:, skipping the statement in question - thus b will be uninitialized and undefined behavior is invoked.
The following two questions (with their accepted answers) will be of even further aid in your quest searching for answers:
How can a variable be used when it's definition is bypassed? 2
Why can't variables be declared in a switch statement?
Turning your compiler warnings/errors to a higher level will hopefully provide you with this information when trying to compile your source.
Below is what gcc says about the matter;
foo.cpp:6:10: error: jump to case label [-fpermissive]
foo.cpp:5:9: error: crosses initialization of 'int b'
1 since int a will always be 1 (one) it will always jump here.
2 most relevant out of the two links, answered by me.
Switch statements only evaluate portions of the code inside them, and you can't put code at the top and expect it to get evaluated by every case component. You need to put the b initialization higher in the program above the switch statement. If you really need to do it locally, do it in a separate set of braces:
Code:
int main()
{
int a=1;
/* other stuff */
{
int b=20;
switch(a)
{
case 1:
printf("b is %d\n",b);
break;
default:
printf("b is %d\n",b);
break;
}
}
/* other stuff... */
return 0;
}
The switch directly jumps to case 1:, never executing the assignment.
Presumably because switch functions like a goto - if a == 1, it jumps straight to case 1: and bypasses initialization of b.
That is: I know switch jumps straight to the case label, but I'm very surprised the compiler doesn't complain about the missed initialization.
It's a pretty bad idea to initialize B under the switch statement and outside a case statement. To understand what's going on here, you have to know that the switch makes a jump to the correct case/default statement.
because when switch(a) statement executes control goes directly to the statement case 1: without executing statement int b=20, taht's why it gives garbage value as answer. If u want to print a then either u have to initialise in case 1: block or u have to initialise to before switch(a) statement.
Because that line is never reached. When C hits a switch(a) statement, it branches to the case that matches the condition of the variable you're switching on. The statement that initialises b isn't in any of the cases. I suppose the compiler is free to write 20 into the location, but the language doesn't require it to do so and in this case it doesn't: it's also free to leave initialisation until it actually executes an assignment.
Related
I recently came across the code snippet shown below, I was expecting it to be a syntax error but to my surprise, the code produces valid output.
#include <stdio.h>
int main(void) {
int x = 2;
switch(x) {
case 1: printf("1"); break;
do {
case 2: printf("2 "); break;
case 3: printf("3 "); break;
} while(++x < 4);
case 4: printf("4"); break;
}
return 0;
}
output: 2 4
Compiler: GCC 6.3
I found a similar problem but it is not justifying above condition completely,
Mixed 'switch' and 'while' in C
Can anyone explain,
What exactly happening here?
Why isn't it a syntax error?
Why case '3' is skipped?
case X: some_statement; is a labeled statement (6.8.1) just like goto_label: some_statement; with the only caveat that case/default labels may only appear inside the body of a switch (possibly in an arbitrarily nested compound statement). That makes case statements only very loosely coupled with switches, syntactically.
Semantically, switches are implementable as computed gotos and like regular gotos, they may jump pretty much anywhere (in C11, you can't jump past a VLA declaration) including inside of a loop (see https://en.wikipedia.org/wiki/Duff%27s_device#Mechanism for another description).
In your example, case 3: is skipped because of the break, but case 4: does follow because the break after case 3: is a loop-breaking break, not a switch-breaking break (break/continue always apply to the nearest construct they can apply to).
The official C grammar for a switch statement is:
switch ( expression ) statement
Any statement can be a labeled-statement, for which the grammar includes:
case constant-expression : statement
This means you can put pretty much whatever you want inside the body of the switch: It can be a compound statement which includes multiple statements and a do-while statement that includes multiple statements, and case labels can be prefixed to any of those statements.
The compiler merely implements the switch using jump instructions (inside its abstract machine; they may end up as other instructions after optimization). Loops statements such as do-while are implement with code that tests the controlling expression and jumps conditionally. So, in spite of the nice structure you think of in structured languages, it boils down to jump instructions, and those can be interwoven as desired.
The jumps are not the most disconcerting part of this. Object initialization and lifetime is more of a concern. Switch statements can unintentionally skip the initialization of objects if care is not taken.
The printf of “3 ” is never executed because control jumps from the switch to case 2, then breaks, which exits the do-while. This leaves code at the case 4 statement, which prints “4 ” and then breaks out of the switch statement.
Through a little typo, I accidentally found this construct:
int main(void) {
char foo = 'c';
switch(foo)
{
printf("Cant Touch This\n"); // This line is Unreachable
case 'a': printf("A\n"); break;
case 'b': printf("B\n"); break;
case 'c': printf("C\n"); break;
case 'd': printf("D\n"); break;
}
return 0;
}
It seems that the printf at the top of the switch statement is valid, but also completely unreachable.
I got a clean compile, without even a warning about unreachable code, but this seems pointless.
Should a compiler flag this as unreachable code?
Does this serve any purpose at all?
Perhaps not the most useful, but not completely worthless. You may use it to declare a local variable available within switch scope.
switch (foo)
{
int i;
case 0:
i = 0;
//....
case 1:
i = 1;
//....
}
The standard (N1579 6.8.4.2/7) has the following sample:
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero value, the call to the printf function will
access an indeterminate value. Similarly, the call to the function f cannot be reached.
P.S. BTW, the sample is not valid C++ code. In that case (N4140 6.7/3, emphasis mine):
A program that jumps90 from a point where a variable with automatic storage duration is not in scope to a
point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default
constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the
preceding types and is declared without an initializer (8.5).
90) The transfer from the condition of a switch statement to a case label is considered a jump in this respect.
So replacing int i = 4; with int i; makes it a valid C++.
Does this serve any purpose at all?
Yes. If instead of a statement, you put a declaration before the first label, this can make perfect sense:
switch (a) {
int i;
case 0:
i = f(); g(); h(i);
break;
case 1:
i = g(); f(); h(i);
break;
}
The rules for declarations and statements are shared for blocks in general, so it's the same rule that allows that that also allows statements there.
Worth mentioning as well is also that if the first statement is a loop construct, case labels may appear in the loop body:
switch (i) {
for (;;) {
f();
case 1:
g();
case 2:
if (h()) break;
}
}
Please don't write code like this if there is a more readable way of writing it, but it's perfectly valid, and the f() call is reachable.
There is a famous use of this called Duff's Device.
int n = (count+3)/4;
switch (count % 4) {
do {
case 0: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
Here we copy a buffer pointed to by from to a buffer pointed to by to. We copy count instances of data.
The do{}while() statement starts before the first case label, and the case labels are embedded within the do{}while().
This reduces the number of conditional branches at the end of the do{}while() loop encountered by roughly a factor of 4 (in this example; the constant can be tweaked to whatever value you want).
Now, optimizers can sometimes do this for you (especially if they are optimizing streaming/vectorized instructions), but without profile guided optimization they cannot know if you expect the loop to be large or not.
In general, variable declarations can occur there and be used in every case, but be out of scope after the switch ends. (note any initialization will be skipped)
In addition, control flow that isn't switch-specific can get you into that section of the switch block, as illustrated above, or with a goto.
Assuming you are using gcc on Linux, it would have given you a warning if you're using 4.4 or earlier version.
The -Wunreachable-code option was removed in gcc 4.4 onward.
Not only for variable declaration but advanced jumping as well. You can utilize it well if and only if you're not prone to spaghetti code.
int main()
{
int i = 1;
switch(i)
{
nocase:
printf("no case\n");
case 0: printf("0\n"); break;
case 1: printf("1\n"); goto nocase;
}
return 0;
}
Prints
1
no case
0 /* Notice how "0" prints even though i = 1 */
It should be noted that switch-case is one of the fastest control flow clauses. So it must be very flexible to the programmer, which sometimes involves cases like this.
It should be noted, that there are virtually no structural restrictions on the code within the switch statement, or on where the case *: labels are placed within this code*. This makes programming tricks like duff's device possible, one possible implementation of which looks like this:
int n = ...;
int iterations = n/8;
switch(n%8) {
while(iterations--) {
sum += *ptr++;
case 7: sum += *ptr++;
case 6: sum += *ptr++;
case 5: sum += *ptr++;
case 4: sum += *ptr++;
case 3: sum += *ptr++;
case 2: sum += *ptr++;
case 1: sum += *ptr++;
case 0: ;
}
}
You see, the code between the switch(n%8) { and the case 7: label is definitely reachable...
* As supercat thankfully pointed out in a comment: Since C99, neither a goto nor a label (be it a case *: label or not) may appear within the scope of a declaration that contains a VLA declaration. So it's not correct to say that there are no structural restrictions on the placement of the case *: labels. However, duff's device predates the C99 standard, and it does not depend on VLA's anyway. Nevertheless, I felt compelled to insert a "virtually" into my first sentence due to this.
You got your answer related to the required gcc option -Wswitch-unreachable to generate the warning, this answer is to elaborate on the usability / worthyness part.
Quoting straight out of C11, chapter §6.8.4.2, (emphasis mine)
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with automatic storage
duration (within the block) but is never initialized, and thus if the
controlling expression has a nonzero value, the call to the printf
function will access an indeterminate value. Similarly, the call to
the function f cannot be reached.
Which is very self-explanatory. You can use this to define a locally scoped variable which is available only within the switch statement scope.
It is possible to implement a "loop and a half" with it, although it might not be the best way to do it:
char password[100];
switch(0) do
{
printf("Invalid password, try again.\n");
default:
read_password(password, sizeof(password));
} while (!is_valid_password(password));
I stumbled upon this code, which works as expected:
switch (ev->deviceType) {
break;
case DEVICE_TS1E0:
//some code
break;
case DEVICE_TS1E3:
//some code
break;
default:
//some logging
break;
}
Now, there's a lonesome break; at the start of the switch, which appears to have no effect.
Is there any circumstances where that break; would have an effect ?
TL;DR That break statement is ineffective and a dead-code. Control will never reach there.
C11 standard has a pretty good example of a similar case, let me quote that straight.
From Chapter §6.8.4.2/7, (emphasis mine)
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero value, the call to the printf function will
access an indeterminate value. Similarly, the call to the function f cannot be reached.
That statement, along any other statements that is not in a case clause in switch statement are unreachable code, aka dead code. That means they will not be run anyway. It is not recommended to use them.
how can we use the changeable variable as a switch case label.
in other words,
I have a macro defined. But I need to change this value at run time depending on a condition. How can I implement this?
example is given below,
Here , case "FOO" will work?
#define CONDITION (strcmp(str, "hello") == 0)
#define FOO1 (10)
#define FOO2 (20)
#define FOO ((CONDITION) ? (FOO1) : (FOO2))
char *var="hello";
int main()
{
int p = 20;
switch(p) {
case FOO:
printf("\n case FOO");
break;
case 30:
printf("\n case 30");
break;
default:
printf("\n case default");
break;
}
return(0);
}
The switch condition needs to be resolved at compile-time. The case values need to be compile time constant expressions
From your question, you want to use run time condition to change the value of the case, so that is not possible.
One way to achieve run time check is to use if condition.
Your macro #define CONDITION (strcmp(str, "hello") == 0) isn't complete. It doesn't take in any argument.
The compiler will simply say str isn't defined in this scope.
Regardless, the case values are constants, so you won't be able to achieve this since your condition depends on run-time inputs.
It is important to know that most compilers implement the cases via a branch table. This is possible only because the case values are compile-time known (i.e. constants). The compiler will generate code to use your input as an index into this branch table to get to the logic for a particular case.
tl;dr - You can't use switch. Use if-elseif-else instead
Normally when using a switch statement, you cannot define and initialize variables local to the compound statement, like
switch (a)
{
int b = 5; /* Initialization is skipped, no matter what a is */
case 1:
/* Do something */
break;
default:
/* Do something */
break;
}
However, since the switch statement is a statement like for or while, there is no rule against not using a compound statement, look here for examples. But this would mean, that a label may be used between the closing parenthesis after the switch keyword and the opening brace.
So in my opinion, it would be possible and allowed to use a switch statement like this:
switch (a)
default:
{
int b = 5; /* Is the initialization skipped when a != 1? */
/* Do something for the default case using 'b' */
break;
case 1: // if a == 1, then the initialization of b is skipped.
/* Do something */
break;
}
My question: Is the initialization necessarily performed in this case (a != 1)? From what I know of the standards, yes, it should be, but I cannot find it directly in any of the documents I have available. Can anyone provide a conclusive answer?
And before I get comments to that effect, yes, I know this is not a way to program in the real world. But, as always, I'm interested in the boundaries of the language specification. I'd never tolerate such a style in my programming team!
Most people think of a switch as a mutiple if, but it is technically a calculated goto. And the case <cte>: and default: are actually labels. So the rules of goto apply in these cases.
Your both your examples are syntactically legal, but in the second one, when a==1 the b initialization will be skipped and its value will be undefined. No problem as long as you don't use it.
REFERENCE:
According to C99 standard, 6.2.4.5, regarding automatic variables:
If an initialization is specified for the object, it is performed each time the declaration is reached in the execution of the block;
So the variable is initialized each time the execution flow reaches the initialization, just as it were an assignment. And if you jump over the initialization the first time, then the variable is left uninitialized.