In JavaScript the following statement is valid.
switch(true) {
case a > b:
max = a;
break;
case a < b:
max = b;
break;
default:
max = a;
}
But in the C/C++ programming languages, when I write this statement, the compiler gives me an error showing that case statement must consist of constant values. Sometimes in particular circumstances writing such switch-case statements would be very useful, but C/C++ will not allow me to do so.
Now I am curious to know what is the point behind this to not allowing variable values to be used in case statements?
C++ has evolved from C where switch statements were conceived as a Jump Table (Branch Table). To implement as jump tables, the switch conditions should be constant such that it can easily be translated to a label.
Though the standard never dictates how the switch statements should be implemented but, most importantly, the case labels should be such that it could be evaluated during compile time. In C and C++, the switch statement evaluates the expression and transfers control to one of the many case statement values that evaluates to the value of the conditional expression.
6.4.2 The switch statement [stmt.switch]
The switch statement causes control to be transferred to one of several statements depending on the value of a condition.
This behaviour makes it different from other languages which supports, conditions in case statements.
As for an instance, Javascript describes switch ... case statement as
MDN switch
The switch statement evaluates an expression, matching the expression's value to a case clause, and executes statements associated with that case.
So you are actually trying to compare two different construct and expecting the behaviour would be the same.
As to answer the point behind this to not allowing variable values to be used in case statements?, that would had made the switch .. case a less efficient construct where for every iteration/instance, the case labels should be re-evaluated to determine if it matches the conditional expression.
The C (rather than C++) Standard says:
C11: 6.8.4.2 The switch statement (p3)
The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion.[...]
If expressions were allowed in case then there are possibilities that two expressions could have same value.
The C++ (rather than C) Standard says the same:
C++11: 6.4.2 paragraph 2:
[...] No two of the case constants in the same switch shall have the same value after conversion to the promoted type of the switch condition.
The reason that switch/case exists (bearing in mind that if/else exists for everything else) is to provide an analogue for lower-level "jump" syntax, so that you can create fast, static "jump tables" for your code. That reason evaporates if you allow runtime expressions.
Asking why switch does this thing is akin to asking why if does the other thing, and is therefore vacuous.
Just because you can do something doesn't mean that makes sense. In particular, switch is not equivalent to if/else ladders.
if/else is more general than switch which is intended to do "select the thing to do based on the value of the switch expression".
Exercise - what would the following code do?
var a = 5;
var b = 0;
switch(a)
{
case 5: b = 1; break;
case 5: b = 2; break;
case 1: b = 3; break;
}
Is now b equal to 2 or 1? If it does both branches, it doesn't do "select one thing to do", if it does one but not the other, it's arbitrary decision.
Restricting the case values to be constant expressions allows compiler to issue error on this code. If the values could be different based on the value at runtime, the compiler couldn't be able to verify that two cases have identical values, as a consequence of Halting problem.
Related
I have a question for a quick workaround to enjoy the benefits of the non-standard gnu case ranges. For example, the non-standard:
case 1 ... 5:
Could be replaced by:
case 1:
case 2:
case 3:
case 4:
case 5:
Probably some macro solution might be in order. From my memory macro loops cannot loop for large numbers of iterations. For this reason, what if the range is "large", say in the thousands?
If you're talking preprocessor loops I guess that you're thinking of the preprocessor meta programming from boost. While it's probably quite portable, the loops seems to be limited to 255 "iterations". In fact the implementation is not a real loop, it's more like a hard coded loop-unroll (thereby the limitation). You could of course expand this to more iterations.
While the preprocessor trick could be tempting, I think you should consider using if-else if-else construct. What's actually (often) happens in a modern compiler regarding conditionals is that it boils down to the same construct that should generate the same code (unless you trick the compiler into evaluating the variable multiple times).
You could even combine the constructs, using a switch-case construct for all singular alternatives and then after the default label add an if-else if-else to handle all ranges.
A third solution would be to write a script that finds the case-ranges and replace them by a standard construct, this should be fairly straight forward in most cases as case can't appear in many places without being a keyword and then it should be followed by an expression which can't contain ... in that way. The only problematic situation (that I can think of) would be when the case-range is a result of preprocessor expansion.
The best alternative would be to re-factor the code to use if/else. If there truly are thousands of cases it may or may not be very efficient to have a giant case statement in the first place.
However, it because cases could "fall-through" or other odd flow control like Duff's device (I hope not), it may not be completely a straightforward conversion.
It is not likely to be a very good implementation to abuse the preprocessor to "loop". See Writing a while loop in the C preprocessor for sample of what this might look like.
It may be best to write a simple python or awk script. However this approach may also be flawed if the keyword case appears somewhere like a string or if labels the preprocessor changes anything. This may work very well for a narrow one-off conversion though, but without seeing the code in question it is hard to say.
There is a serious problem with either preprocessing approach if the case labels use enumerations. Since enums are still just text strings at the time the preprocessor runs (or an external script), how can it iterate from STATE_10 to STATE_20 without knowing what integers they represent? It can't - the GNU extension really requires compiler support.
If a one-time wholesale replacement of the case statement is too invasive or irregular to manage, you could probably utilize a hybrid approach:
Assuming you have a (notional) example like:
switch(state)
{
case STATE_1:
xxx; break;
case STATE_2 ... STATE_10:
yyy; break;
}
Allocate a previously unused range of indexes. Add one new special index for each existing range label. Use if/else logic to detect the ranges first and then replace the range case with a new standard one. This allows the control flow structure to remain essentially unmodified:
#if !defined(__GNUC__)
#define STATE_RANGE_2_10 101
if(state >= 2 && state <= 10)
state2 = STATE_RANGE_2_10
else if(...)
state2 = STATE_RANGE_x_y
else
state2 = state;
#else /* GNU */
#define STATE_RANGE_2_10 STATE_2 ... STATE_10
state2 = state;
#endif
switch(state2)
{
case STATE_1:
xxx; break;
case STATE_RANGE_2_10:
yyy; break;
}
With some suitable macros this could even be made portable between GNUC and real C if you really wanted GNUC to still use the extension for some reason. Note: I introduced the state2 variable in case it is stored or used outside the local scope. If not, you can skip that.
Regarding switch the standard states the following. "When the switch statement is executed, its condition is evaluated and compared with each case constant."
Does it mean that the condition expression evaluated once and once only, and it is guaranteed by the standard for each compiler?
For example, when a function is used in the switch statement head, with a side effect.
int f() { ... }
switch (f())
{
case ...;
case ...;
}
I think it is guaranteed that f is only called once.
First we have
The condition shall be of integral type, enumeration type, or class type.
[6.4.2 (1)] (the non-integral stuff does not apply here), and
The value of a condition that is an expression is the value of the
expression
[6.4 (4)]. Furthermore,
The value of the condition will be referred to as simply “the condition” where the
usage is unambiguous.
[6.4 (4)] That means in our case, the "condition" is just a plain value of type int, not f. f is only used to find the value for the condition. Now when control reaches the switch statement
its condition is evaluated
[6.4.2 (5)], i.e. we use the value of the int that is returned by f as our "condition". Then finally the condition (which is a value of type int, not f), is
compared with each case constant
[6.4.2 (5)]. This will not trigger side effects from f again.
All quotes from N3797. (Also checked N4140, no difference)
Reading N4296
Page 10 para 14:
Every value computation and side effect associated with a full-expression is sequenced before every value
computation and side effect associated with the next full-expression to be evaluated.
When I read the first line of para. 10 (above that):
A full-expression is an expression that is not a sub-expression of
another expression.
I have to believe that the condition of a switch statement is a full-expression and each condition expression is a full expression (albeit trivial at execution).
A switch is a statement not an expression (see 6.4.2 and many other places).
So by that reading the evaluation of the switch must take place before the evaluation of the case constants.
As ever many points boil down to tortuous reading of the specification to come to an obvious conclusion.
If I peer reviewed that sentence I would propose the following amendment (in bold):
When the switch statement is executed, its condition is evaluated
once per execution of the switch statement and compared with each case constant.
Yes the expression is evaluated only once when the switch statement is executed:
§ 6.4 Selection statements
4 [...] The value of a condition that is an expression is the value of the
expression [...] The value of the condition will be referred to as simply “the condition” where the usage is unambiguous.
This means that the expression is evaluated and its value is considered the condition to be evaluated against each case statement.
Section 6.4.4:
...The value of a condition that is an expression is the value of the
expression, contextually converted to bool for statements other than
switch;...The value of the condition will be referred to as simply “the condition” where the
usage is unambiguous
In my understanding, the quote above is equivalent to the following pseudo-code:
switchCondition := evaluate(expression)
Now add your quote
...its condition is evaluated and compared with each case constant.
Which should be translated to:
foreach case in cases
if case.constant == switchCondition
goto case.block
So yeah, it looks like this is the case.
Does this code print hello once or twice?
int main() {
printf("hello\n");
}
Well, I think the answer is in the more general understanding of what the standard describes rather than in the specific switch statement wording.
As per Program execution [intro.execution] the standard describes the behaviour of some abstract machine that executes the program parsed according to the C++ grammar. It does not really define what 'abstract machine' or 'executes' mean, but they are assumed to mean their obvious computer science concepts, i.e. a computer that goes through the abstract syntax tree and evaluates every part of it according to the semantics described by the standard. This implies that if you wrote something once, then when the execution gets to that point, it is evaluated only once.
The more relevant question is "when the implementation may evaluate something not the way written in the program"? For this there is the as-if rule and a bunch of undefined behaviours which permit the implementation to deviate from this abstract interpretation.
This issue was clarified for C++ '20 making it clear that the condition is evaluated once:
When the switch statement is executed, its condition is evaluated. If one of the case constants has the same value as the condition, control is passed to the statement following the matched case label.
The commit message for the change acknowledges that it was potentially confusing before:
[stmt.switch] Clarify comparison for case labels
The expression is guaranteed that is evaluated only once by the flow of control. This is justified in the standard N4431 §6.4.2/6 The switch statement [stmt.switch] (Emphasis mine):
case and default labels in themselves do not alter the flow of
control, which continues unimpeded across such labels. To exit from a
switch, see break, 6.6.1. [ Note: Usually, the substatement that is
the subject of a switch is compound and case and default labels appear
on the top-level statements contained within the (compound)
substatement, but this is not required. Declarations can appear in the
substatement of a switch-statement. — end note ]
I was asked this question in an interview.
I replied that it was a conditional construct because
It executes once, unlike a loop which has the capability to execute multiple times.
There is no loop control mechanisms, there is only conditional switching based on different cases.
So is my answer right or wrong, is there a better answer?
Also he asked me the reason why break; statements work with switch-case since, break; only works with loops.
This question I could not answer.
In C++
switch is selection-statement
n3376 6.4/1 and 6.4.2 is about switch
selection-statement:
...
switch ( condition ) statement
break is jump-statement
n3376 6.6.1/1
The break statement shall occur only in an iteration-statement or a switch statement and causes termination
of the smallest enclosing iteration-statement or switch statement; control passes to the statement following
the terminated statement, if any.
C answer
There is no formal term called "conditional construct". The C standard uses the term selection statement. The C language has three different selection statements: if, if-else and switch (C11 6.8.4). Loops sort under the category of iteration statements (6.8.5).
The break statement is a jump statement, just like goto. It has some restrictions of where it is allowed to appear:
C11 6.8.6.3
A break statement shall appear only in or as a switch body or loop
body.
So to answer the interview questions:
Is switch case a loop or a conditional construct?
If you by conditional construct mean a selection statement, then yes, switch is a conditional construct.
why break; statements work with switch-case since, break; only works with loops
No, the question is incorrect, it does not only work with loops. It works with switch and loops. This is because the C language is defined in that way (6.8.6.3).
A switch case is a way of wrapping a block of instructions and saying execute (part of) it, beginning here and ending here. The matching case marks the beginning and the following break marks the end.
The block could be a few instructions:
{
instruction_A;
instruction_B;
instruction_C;
instruction_D;
}
The case statements say where to dynamically start based upon the switch value:
switch(value)
{
case one:
instruction_A;
instruction_B;
case two:
instruction_C;
case three:
instruction_D;
}
In case one, all the instructions will be called, as there is no break. Case two will execute C and D, if there are no exceptions (c;.
The break statements say where to stop, and mean it's possible to drop through a number of case statements:
switch(value)
{
case one:
instruction_A;
instruction_B;
case two:
instruction_C;
break;
case three:
instruction_D;
}
Case one will now execute A, B, and C, but not D.
I'm wondering if all compilers would silently ignore code before labels in a switch statement, as VS2005 one does.
Here's what I'm after:
#define CASE break; case
So,
switch (i) {
CASE 0: print("0");
CASE 1: print("1");
}
would turn into
switch (i) {
break;
case 0: print("0"); break;
case 1: print("1");
}
From the standard it seems clear that the first "break" (and any other code there if it existed) wouldn't be executed. The standard doesn't deny existence of such code, but I can't be sure about actual compilers.
Yes, this is implied by §6.4.2¶5 (specifically the ignoring part):
When the switch statement is executed, its condition is evaluated and compared with each case constant. If
one of the case constants is equal to the value of the condition, control is passed to the statement following
the matched case label. If no case constant matches the condition, and if there is a default label, control
passes to the statement labeled by the default label. If no case matches and if there is no default then
none of the statements in the switch is executed.
I don't think your macro is a good idea, however.
Personally, I would be "unable" to write such code, as all warnings are turned into errors, and an unreachable code warning is likely... and in fact, a quick test on clang++ outputs such a warning.
This question in mainly pointed at C/C++, but I guess other languages are relevant as well.
I can't understand why is switch/case still being used instead of if/else if. It seems to me much like using goto's, and results in the same sort of messy code, while the same results could be acheived with if/else if's in a much more organized manner.
Still, I see these blocks around quite often. A common place to find them is near a message-loop (WndProc...), whereas these are among the places when they raise the heaviest havoc: variables are shared along the entire block, even when not propriate (and can't be initialized inside it). Extra attention has to be put on not dropping break's, and so on...
Personally, I avoid using them, and I wonder wether I'm missing something?
Are they more efficient than if/else's?
Are they carried on by tradition?
Summarising my initial post and comments - there are several advantages of switch statement over if/else statement:
Cleaner code. Code with multiple chained if/else if ... looks messy and is difficult to maintain - switch gives cleaner structure.
Performance. For dense case values compiler generates jump table, for sparse - binary search or series of if/else, so in worst case switch is as fast as if/else, but typically faster. Although some compilers can similarly optimise if/else.
Test order doesn't matter. To speed up series of if/else tests one needs to put more likely cases first. With switch/case programmer doesn't need to think about this.
Default can be anywhere. With if/else default case must be at the very end - after last else. In switch - default can be anywhere, wherever programmer finds it more appropriate.
Common code. If you need to execute common code for several cases, you may omit break and the execution will "fall through" - something you cannot achieve with if/else. (There is a good practice to place a special comment /* FALLTHROUGH */ for such cases - lint recognises it and doesn't complain, without this comment it does complain as it is common error to forgot break).
Thanks to all commenters.
Well, one reason is clarity....
if you have a switch/case, then the expression can't change....
i.e.
switch (foo[bar][baz]) {
case 'a':
...
break;
case 'b':
...
break;
}
whereas with if/else, if you write by mistake (or intent):
if (foo[bar][baz] == 'a') {
....
}
else if (foo[bar][baz+1] == 'b') {
....
}
people reading your code will wonder "were the foo expressions supposed to be the same", or "why are they different"?
please remember that case/select provides additional flexibility:
condition is evaluated once
is flexible enough to build things like the Duff's device
fallthrough (aka case without break)
as well as it executes much faster (via jump/lookup table) * historically
Also remember that switch statements allows the flow of control to continue, which allows you to nicely combine conditions while allowing you to add additional code for certain conditions, such as in the following piece of code:
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
Using if/else statements here instead would not be anywhere as nice.
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}
If there are lots of cases, the switch statement seems cleaner.
It's also nice when you have multiple values for which you want the same behavior - just using multiple "case" statements that fall through to a single implementation is much easier to read than a if( this || that || someotherthing || ... )
It might also depend on your language -- For example, some languages switch only works with numeric types, so it saves you some typing when you're working with an enumerated value, numeric constants... etc...
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
Or slightly easier to read...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}
Switch/case is usually optimized more efficiently than if/else if/else, but is occasionally (depending on language and compiler) translated to simple if/else if/else statements.
I personally think switch statements makes code more readable than a bunch of if statements; provided that you follow a few simple rules. Rules you should probably follow even for your if/else if/else situations, but that's again my opinion.
Those rules:
Never, ever, have more than one line on your switch block. Call a method or function and do your work there.
Always check for break/ case fallthrough.
Bubble up exceptions.
Clarity. As I said here, a clue that else if is problematic is
the frequency with which ELSE IF is
used in a far more constrained way
than is allowed by the syntax. It is a
sledgehammer of flexibility,
permitting entirely unrelated
conditions to be tested. But it is
routinely used to swat the flies of
CASE, comparing the same expression
with alternate values...
This reduces the readability of the
code. Since the structure permits a
universe of conditional complexity,
the reader needs to keep more
possibilities in mind when parsing
ELSE IF than when parsing CASE.
Actually a switch statement implies that you are working off of something that is more or less an enum which gives you an instant clue what's going on.
That said, a switch on an enum in any OO language could probably be coded better--and a series of if/else's on the same "enum" style value would be at least as bad and even worse at conveying meaning.
addressing the concern that everything inside the switch has equivalent scope, you can always throw your case logic into another { } block, like so ..
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
.. now I'm not saying that's pretty. Just putting it out there as something that's possible if you absolutely have to isolate something in one case from another.
one other thought on the scope issue - it seems like a good practice to only put one switch inside a function, and not a lot else. Under those circumstances, variable scope isn't as much of a concern, since that way you're generally only dealing with one case of execution on any given invocation of the function.
ok, one last thought on switches: if a function contains more than a couple of switches, it's probably time to refactor your code. If a function contains nested switches, it's probably a clue to rethink your design a bit =)
switch case is mainly used to have the choice to made in the programming .This is not related the conditional statement as :
if your program only require the choice to make then why you use the if/else block and increase the programming effort plus it reduce the execution speed of the program .
Switch statements can be optimized for speed, but can take up more memory if the case values are spread out over large numbers of values.
if/else are generally slow, as each value needs to be checked.
A Smalltalker might reject both switch and if-then-else's and might write something like:-
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: 'Mon' put: 'Monday';
at: 'Tue' put: 'Tuesday';
at: 'Wed' put: 'Wednesday'
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
This is a trivial example but I hope you can see how this technique scales for large numbers of cases.
Note the second argument to at:IfAbsent: is similar to the default clause of a case statement.
The main reason behind this is Maintainability and readability. Its easy to make code more readable and maintainable with Switch/case statement then if/else. Because you have many if/else then code become so much messy like nest and its very hard to maintain it.
And some how execution time is another reason.
Pretty sure they compile to the same things as if/else if, but I find the switch/case easier to read when there are more than 2 or 3 elses.