In a switch statement, why are all the cases being executed?
It's a bug in your code. You forgot to put in a break
after each case
:
switch (day % 10) {
case 1: ordinalBuilder.append("st"); break;
case 2: ordinalBuilder.append("nd"); break;
case 3: ordinalBuilder.append("rd"); break;
default: ordinalBuilder.append("th"); break;
}
how cases get evaluated in switch statements (C)
It's a very good question, and actually reveals the internals of the switch
statement in C and C++, which can sometimes be confused with cascading if-else
statements.
The switch
statement in C/C++ works as follows:
- (1) first it evaluates the expression presented as a condition in the
switch
statement - (2) stores the result on the stack or using a general-purpose register
- (3) using that result it attempts to jump to the corresponding case statement with the minimum comparisons possible by using a jump-table (when one can be built).
It is because of (1) and (2) that the switch
you created is not behaving the way you may expect, and it doesn't reevaluate the initial expression during the execution of the case
statements.
In contrast with cascading if-else
statements, your case
statements are essentially blocks of instructions compiled in sequential order, referenced by a jump table as mentioned at (3). Once the execution reaches a case
statement, it will automatically cascade over the next case
statements if break
is not encountered. The break
actually instructs the compiler to jump over the switch statement and stop executing the case
statements.
Check out this commented disassembly of your switch statement, just to have a better grip of what's happening under the hood:
0x56555585 <+56>: mov -0x10(%ebp),%eax ;<--- store "i" (the switch condition) into EAX
0x56555588 <+59>: cmp $0x1,%eax ;<--- check "case 1"
0x5655558b <+62>: je 0x5655559a <main+77> ;<--- jump if equal to "case 1"
0x5655558d <+64>: cmp $0x5,%eax ;<--- check "case 5"
0x56555590 <+67>: je 0x5655559e <main+81> ;<--- jump if equal to "case 5"
0x56555592 <+69>: test %eax,%eax ;<--- check "case 0"
0x56555594 <+71>: jne 0x565555a2 <main+85> ;<--- jump if not equal to "default"
0x56555596 <+73>: addl $0x5,-0x10(%ebp) ;<--- case 0
0x5655559a <+77>: addl $0x2,-0x10(%ebp) ;<--- case 1
0x5655559e <+81>: addl $0x5,-0x10(%ebp) ;<--- case 5
0x565555a2 <+85>: addl $0x4,-0x10(%ebp) ;<--- default
Note: this is built with -m32 -O0
gcc options to use 32bit code which is much easier to read, and disable optimizations.
You can clearly see that after the jump is made (to any case statement) there is no further reevaluation of i
(-0x10(%ebp)
). Also, when the case is executed, it automatically cascades to the next one if no break
is used.
Now, you may ask yourself why this odd behavior and the answer is at (3): to jump to the corresponding case statement with the minimum comparisons possible.
The switch
statements in C/C++ show their true strength when the number of case
statements really scales up and especially when the spread between the values used for the case
statements is constant.
For example, let's assume we have a large switch
statement with 100 case
values, with a constant spread of 1
between case values and that the switch expression (i
) evaluates to 100
(last case
in the switch
):
switch (i) {
case 1: /*code for case 1*/ break;
case 2: /*code for case 2*/ break;
[...]
case 99: /*code for case 99*/ break;
case 100: /*code for case 100*/ break;
}
If you used cascading if-else
statements you would get 100 comparisons, but this switch
can obtain the same result using just a couple of instructions, in this order:
- first: the compiler will index all the
case
statements in a jump table - second: it will evaluate the condition in the
switch
and store the result (i.e.: fetchi
) - third: it calculates the corresponding index in the jump table based on the result (i.e.: decrement
i
by1
, the firstcase
statement, results in index 99) - fourth, it jumps directly to the corresponding
case
without any further operation
The same will apply if your case values have a spread of 2
:
switch (i) {
case 1: /*code for case 1*/ break;
case 3: /*code for case 3*/ break;
[...]
case 99: /*code for case 99*/ break;
case 101: /*code for case 101*/ break;
}
Your compiler should detect this spread too and after subtracting the first case value (which is 1
) will divide by 2
to obtain the same index for the jump table.
This complicated inner-workings of the switch
statement makes it a very powerful tool in C/C++ when you want to branch your code based on a value you can only evaluate at run-time, and when that value belongs to a set that is evenly spread, or at least, groups of values with an even spread.
When the case
values don't have an even spread, the switch
becomes less efficient and it starts to perform similarly to if we have used cascading if-else
instead.
Is a switch executing all the cases without stopping?
The switch statements jump to the right value, and continue up to the end of other cases.
If you like to exit the switch statement you have to use a break (or return in some situations).
This is useful to handle situations in wich many values can be handled at the same manner:
switch (x) {
case 0:
case 1:
case 2:
System.out.println("X is smaller than 3");
break;
case 3:
System.out.println("X is 3");
case 4:
System.out.println("X is 3 or 4");
break;
}
If the case selection is also a final condition for a method you can return from it.
public String checkX(int x) {
switch (x) {
case 0:
case 1:
case 2:
return "X is smaller than 3";
case 3:
return "X is 3";
case 4:
return ("X is necessary 4");
default:
return null;
}
}
}
In switch statements, why do all statements get executed after a true statement if break is not given?
No, a case label (which isn't a statement) doesn't check any condition. In fact it doesn't do anything at all (notice how you can have multiple cases in a row?) and doesn't produce any code. Upon entering the switch, execution jumps to the matching case. And that's it. Once you're in the block, the labels don't do anything, but a break
will get you out.
Why is my switch statement running multiple cases?
It's because you don't have a break
statement. Without it, the code will "fall through".
var x = 10;switch (x) { case 10: console.log('With break'); break; case 20: console.log('I never run'); break;}
console.log('-------');
switch (x) { case 10: console.log('Without'); case 20: console.log('a break'); case 30: console.log('it just'); case 40: console.log('keeps going');}
Switch statement only executes last case?
You need to break properly.
switch (n) {
case 1:
/* code here */
break;
case 2:
/* code here */
break;
case 3:
/* code here */
break;
default:
/* code here */
break;
}
If you forget a break statement, it will fall through to the next case.
Execute all cases in switch statement - specific issue
Then use if
statements:
if (obj.nObjType == eElephant || obj.nObjType == eAllTypes)
{
// Elephant code
}
if (obj.nObjType == eFoo || obj.nObjType == eAllTypes)
{
// Foo code
}
// etc.
why is the wrong case being executed after default in a switch statement
FALLTHROUGH:
Another point of interest is the break statement. Each break statement
terminates the enclosing switch statement. Control flow continues with
the first statement following the switch block. The break statements
are necessary because without them, statements in switch blocks fall
through: All statements after the matching case label are executed in
sequence, regardless of the expression of subsequent case labels,
until a break statement is encountered.
Your code should be:
case 2 : j++; break;
case 4: j+=10; break;
default: j+=2; break;
case 15: j+=1000;
}
FROM DOCS
public class Example{
public static void main(String[] args) {
java.util.ArrayList<String> futureMonths =
new java.util.ArrayList<String>();
int month = 8;
switch (month) {
case 1: futureMonths.add("January");
case 2: futureMonths.add("February");
case 3: futureMonths.add("March");
case 4: futureMonths.add("April");
case 5: futureMonths.add("May");
case 6: futureMonths.add("June");
case 7: futureMonths.add("July");
case 8: futureMonths.add("August");
case 9: futureMonths.add("September");
case 10: futureMonths.add("October");
case 11: futureMonths.add("November");
case 12: futureMonths.add("December");
default: break;
}
if (futureMonths.isEmpty()) {
System.out.println("Invalid month number");
} else {
for (String monthName : futureMonths) {
System.out.println(monthName);
}
}
}
}
This is the output from the code:
August
September
October
November
December
Related Topics
Why Does This Generic Code Compile in Java 8
Jpanel Which One of Listeners Is Proper for Visibility Is Changed
Registering and Using a Custom Java.Net.Url Protocol
Java.Lang.Outofmemoryerror: Java Heap Space in Maven
Java, List Only Subdirectories from a Directory, Not Files
How to Dynamically Add Items to a Java Array
Javabean Wrapping with Javafx Properties
How to Read Request Body Multiple Times in Spring 'Handlermethodargumentresolver'
Getting the Name of a Method Parameter
JSONmanagedreference VS JSONbackreference
Wrap the String After a Number of Characters Word-Wise in Java
How Does the Java Cast Operator Work
How to Get the Session Object If I Have the Entity-Manager
Deserialize JSON to Arraylist<Pojo> Using Jackson
JPA Query Selecting Only Specific Columns Without Using Criteria Query