Although it is possible to chain many if-else statements together, this is both difficult to read and inefficient. Consider the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> void printDigitName(int x) { if (x == 1) std::cout << "One"; else if (x == 2) std::cout << "Two"; else if (x == 3) std::cout << "Three"; else std::cout << "Unknown"; } int main() { printDigitName(2); return 0; } |
While this example isn’t too complex, x
is evaluated three times, and the reader has to be sure that it is x
being evaluated each time (not some other variable).
Because testing a variable or expression for equality against a set of different values is common, C++ provides an alternative conditional statement called a switch statement that is specialized for this purpose. Here is the same program as above using a switch:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> void printDigitName(int x) { switch (x) { case 1: std::cout << "One"; return; case 2: std::cout << "Two"; return; case 3: std::cout << "Three"; return; default: std::cout << "Unknown"; return; } } int main() { printDigitName(2); return 0; } |
The idea behind a switch statement is simple: an expression (sometimes called the condition
) is evaluated to produce a value. If the expression’s value is equal to the value after any of the case labels
, the statements after the matching case label
are executed. If no matching value can be found and a default label
exists, the statements after the default label
are executed instead.
Compared to the original if statement
, the switch statement
has the advantage of only evaluating the expression once (making it more efficient), and the switch statement
also makes it clearer to the reader that it is the same expression being tested for equality in each case.
Best practice
Prefer switch statements
over if-else chains when there is a choice.
Let’s examine each of these concepts in more detail.
Starting a switch
We start a switch statement
by using the switch
keyword, followed by parenthesis with the conditional expression that we would like to evaluate inside. Often the expression is just a single variable, but it can be any valid expression.
The one restriction is that the condition must evaluate to an integral type (see lesson 4.1 -- Introduction to fundamental data types if you need a reminder which fundamental types are considered integral types). Non-fundamental types that are convertible to an integer (e.g. enumerated types and some classes) are also valid. Expressions that evaluate to floating point types, strings, and other non-integral types may not be used here.
Following the conditional expression, we declare a block. Inside the block, we use labels to define all of the values we want to test for equality. There are two kinds of labels.
Case labels
The first kind of label is the case label, which is declared using the case
keyword and followed by a constant expression. The constant expression must either match the type of the condition or must be convertible to that type.
If the value of the conditional expression equals the expression after a case label
, execution begins at the first statement after that case label
and then continues sequentially.
Here’s an example of the condition matching a case label
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> void printDigitName(int x) { switch (x) // x is evaluated to produce value 2 { case 1: std::cout << "One"; return; case 2: // which matches the case statement here std::cout << "Two"; // so execution starts here return; // and then we return to the caller case 3: std::cout << "Three"; return; default: std::cout << "Unknown"; return; } } int main() { printDigitName(2); return 0; } |
This code prints:
Two
In the above program, x
is evaluated to produce value 2
. Because there is a case label with value 2
, execution jumps to the statement underneath that matching case label. The program prints Two
, and then the return statement
is exited, which returns back to the caller.
There is no practical limit to the number of case labels you can have, but all case labels in a switch must be unique. That is, you can not do this:
1 2 3 4 5 6 |
switch (x) { case 54: case 54: // error: already used value 54! case '6': // error: '6' converts to integer value 54, which is already used } |
The default label
The second kind of label is the default label (often called the default case), which is declared using the default
keyword. If the conditional expression does not match any case label and a default label exists, execution begins at the first statement after the default label.
Here’s an example of the condition matching the default label:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> void printDigitName(int x) { switch (x) // x is evaluated to produce value 5 { case 1: std::cout << "One"; return; case 2: std::cout << "Two"; return; case 3: std::cout << "Three"; return; default: // which does not match to any case labels std::cout << "Unknown"; // so execution starts here return; // and then we return to the caller } } int main() { printDigitName(5); return 0; } |
This code prints:
Unknown
The default label is optional, and there can only be one default label per switch statement. By convention, the default case
is placed last in the switch block.
Best practice
Place the default case last in the switch block.
Taking a break
In the above examples, we used return statements
to stop execution of the statements after our labels. However, this also exits the entire function.
A break statement (declared using the break
keyword) tells the compiler that we are done executing statements within the switch, and that execution should continue with the statement after the end of the switch block. This allows us to exit a switch statement
without exiting the entire function.
Here’s a slightly modified example rewritten using break
instead of return
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> void printDigitName(int x) { switch (x) // x evaluates to 3 { case 1: std::cout << "One"; break; case 2: std::cout << "Two"; break; case 3: std::cout << "Three"; // execution starts here break; // jump to the end of the switch block default: std::cout << "Unknown"; break; } // execution continues here std::cout << " Ah-Ah-Ah!"; } int main() { printDigitName(3); return 0; } |
The above example prints:
Three Ah-Ah-Ah!
Best practice
Each set of statements underneath a label should end in a break statement
or a return statement
.
So what happens if you don’t end a set of statements under a label with a break
or return
? We’ll explore that topic, and others, in the next lesson.
![]() |
![]() |
![]() |
On the second quiz question, I tried using enum class instead of an enum. My compiler is C++11 compatible. It wasn't working for me, so I just decided to copy and paste the whole solution into my program to see if I could change it to enum class and see what would happen. Every subsequent use of something from the Animal enum class is "undefined" according to my compiler. How did changing it from enum to enum class make it undefined in all subsequent uses in the program? Did I just forget how enum classes work?
Enum classes put enumerators in the scope of the enumeration. So if you change enum to enum class, then all enumerators need to be updated to use an Animal:: prefix.
Hi, could someone tell me, if this code is allright? I used a struct for all user inputs and an enumeration for input states (good input / wrong input).
It worked for me.
Hi!
You have repeated code in line 39, 45, ...
You can fix that by doing only the calculation in the switch, then print after the switch. You can't use temp.baseOperator to print the operator. The rest looks good :)
Isn't working but I don't know why
Which errors/warnings are you getting?
What's happening?
What did you expect to happen?
What have you done to try to solve it?
Friedrich if you're still wondering why it doesn't work its because you have the y and the z in the wrong place you have it like this
calculateResult(x, y, z)
while it should be like this
calculateResult(x, z, y)
you're passing off the wrong values to the variables in the function
You mention this:
"However, initialization of variables directly underneath a case label is disallowed and will cause a compile error. This is because initializing a variable does require execution, and the case statement containing the initialization may not be executed!"
This implies that there is a situation inside a switch statement's block where initialization of variables is allowed, but i can't seem to find information on where it is allowed.
Sorry if im misinterpreting it.
> This implies
Because of the "directly underneath"?
The last example in this lesson shows a legal initialization in a case.
Ah sorry, i seem to have missed that in the last example. Thanks for the clarification.
I tried doing the calculator problem given in this tutorial using struct.The program is running but not giving expected results by operators, Eg.It may show 2+3=1589432.I tried a lot but could not spot the problem in the code . could you please help me out.Here is my code
thanks in advance.
Line 12, 14: Those '\n' don't do anything, remove them.
Line 25 and 27 never run, you're using uninitialized variables. Define `x` and `y` before the `switch` or use `values.integer` directly.
Make sure you enabled compiler warnings. You compiler should have warned you about both issues.
yeah it worked! thanks. But I could still not understand what was the problem if i am defining the variables inside switch .
Nothing outside of cases gets executed.
This program doesn't print anything. Line 24-27 in your code never ran. You are allowed to declare variables outside of cases, but they won't be initialized.
Thanks a lot ! You guys are really doing great work.
Is it bad practice to put the case conditions in brackets like a so:
switch(type)
{
case(ANIMAL_PIG):
case(ANIMAL_CAT):
case(ANIMAL_DOG):
}
I don't see a problem with it.
but, since variables are created when the block begins why can not be intialized then only and be in scope after its declaration ..
Hi there,
In the examle concerning case labels I spotted a semicolon at the end:
switch (x)
{
case 4:
case 4: // illegal -- already used value 4!
case COLOR_BLUE: // illegal, COLOR_BLUE evaluates to 4!
}; <-- this one
Is it necessary?
Well spotted, thanks. It's not necessary. I removed it.
The "Case" statement makes the programs really easy!! I performed such a program of "Weekdays" by using CASE!
Why is this a compile error? I don't see a reason why C++ doesn't allow this? I mean the 'int k' is in the switch's block right?
`k` is visible in `case 1`, but the initialization of `k` is never performed. Control flow jumps from line 1 to line 8, ignoring everything in between. If you were to access `k` in `case 1`, it'd cause undefined behavior, because `k` is uninitialized.
`a` is legal, because it doesn't have an initializer. It's immediately obvious that accessing `a` without writing first causes undefined behavior.
`y` is legal for the same reason as `a`. `y` is accessible to all cases after `case 1`, but it's uninitialized. It's only `4` if `case 1` was matched and fell through.
I'm just confused as to why C++ would allow declaration but not definition in the block. I assumed that since the compiler can 'allocate space', why can't it just assign the value in the binary (I tried it with uniform initialization too)?
> I'm just confused as to why C++ would allow declaration but not definition in the block.
Prints "3", right? No. This would be extremely confusing, so it's illegal. If a variable has an initializer, we should be safe to assume that the variable gets initialized. Variables get initialized when their definition is reached. `switch` is just a bunch of labels, all in the same scope. `x` is visible in the scope of the case, but control flow never reached `x` so it was never initialized.
> why can't it just assign the value in the binary
The value might be unknown at compile-time.
The initialization might depend on something else to run first.
If you need a variable in the `switch`'s scope, define it in the `switch`'s header.
Could you elucidate on the 'confusing' part? I'm just curious of this mechanics, nothing more!
Sorry, I don't understand what you want me to elaborate.
Do you want to know why I think it's confusing or do you want to know what causes the behavior?
Yes! Why it might be confusing...
I see that the variable `x` is used somewhere. I want to know what it prints, so I look at all previous uses of `x`. There are no previous uses, but `x` is initialized at its declaration. Surely, it has the value it was initialized with. But that's not the case if control flow jumped over the line of definition of `x`, which is what's happening in a `switch`-statement.
`x` would be uninitialized even though it has an initializer, that's the confusing part.
Ahh I see, makes sense. Thank you!
Why is Using range in switch case in C/C++ not mentioned here. We can easily rewrite isDigit(char c) code above using ' ... '.
That's not standard C++, it's a compiler extension. Make sure you followed lesson 0.10.
Thanks, I will try not to use them again.
Maybe this is already in the lesson but if not. You should include that switch statements can't be used with strings. Tried it on question2 and realised it didn't work so had to look at the answer to question 2.
Lesson amended to note explicitly that strings can't be used in this context. Thanks!
Thanks for sharing.
Just the function (practicing using headers and .cpp files)
What would be the difference in declaring/initializing int x, y and char z in the function parameters versus within the function itself, like I have it?
You decreased reusability. Now you can't use `calculate` with values that came from somewhere else, they'll always be requested from the user.
- Line 16 is useless, you're validating the operator in the `switch`-statement.
- `calculate`'s return value is unused. Declare it `void`.
Thank you for the prompt reply. I was guessing it was something along the lines of re-application of the calculate statement. So my 'if' statement is just doing what the default statement within the switch statement is doing? Okay gotcha. Thank you for the help.
Any suggestions? (Question #1):
And this is for question #2:
- If your program prints anything, the last thing it prints should be a line feed.
- Use single quotation marks for characters ('2' instead of "2").
Both solutions looks good otherwise, I like your name style :-)
Here is my solution for Question #2.
- `main`: return.
- `getAnimalName::animal` should be const (`const Animal &animal`).
- If your program prints anything, the last thing it prints should be a line feed ('\n').
What about using break; in the default case?
The break; statement isn't required to be used in the default case. You can use it if you feel satisfied doing so, but it isn't required.
The `default` case doesn't have to be the last case.
The last case doesn't need a `break`, but it should be added to allow the cases to be shuffled.
When using the modulus operator, both the operands must be an integral type.
So I used static_cast to turn the operands from a double to an int (they were a double) but the compiler still gives me an error and tells me that the operands are still of type double.
`static_cast` doesn't modify its argument, it returns a copy which is of the new type. A variable's type cannot change, you can only create a new variable based on the old one.
So static_cast is only temporary?
No, it just doesn't modify the argument. You can store the return value in a new variable if you want to, like you'd do with a function.
Hello!
I have a question in your code. I don't think the command line std :: cout in main () can be used to export the command line std :: cout in calculator (), which will cause an error, but I don't understand why on my machine at times Show this error, sometimes not, please help me?
/#include <iostream>
int calculate(int x, int y, char op)
{
switch (op)
{
case '+':
return x + y;
case '-':
return x - y;
case '*':
return x * y;
case '/':
return x / y;
case '%':
return x % y;
default:
std::cout << "calculate(): Unhandled case\n";
return 0;
}
}
int main()
{
std::cout << "Enter an integer: ";
int x;
std::cin >> x;
std::cout << "Enter another integer: ";
int y;
std::cin >> y;
std::cout << "Enter a mathematical operator (+, -, *, /, or %): ";
char op;
std::cin >> op;
std::cout << x << " " << op << " " << y << " is " << calculate(x, y, op) << "\n";
return 0;
}
I don't understand what you're having trouble with, can you post the error you're getting?
Please use code tags when posting code.
Hi, Alex and Nascardriver! This is my code for quiz no.2.
I think it's more readable if getAnimalName() is void type than Alex did. What do you think?
Second, I think function call getAnimalName() inside printNumberOfLegs() make function less modular and less independent. What do you think?
`getAnimalName` doesn't get an animal's name anymore, it prints it. The function name should be adjusted, eg. to `printAnimalName`. Same for `printNumberOfLegs`.
You're right, your code is less modular.
The code looks good otherwise!
Hi,
I have a question about the first solution, or rather a question about my solution.
The way I'm getting the inputs I know can enter the sum direct as 1+1 or 34%6 and it takes the seperate number/chars to the right variable.
I was just wondering if this is the smart thing to do, are there downsides with this take?
Are there better ways to do this?
Thanks!
It's fine. You could run into problems if you're using different types or extraction rules. Adding spaces between the operator and numbers would fix those.
Ok, thanks!
Hi!
- You'll learn about good alternatives to `exit` later, until then, use `std::exit`.
- Inconsistent formatting. Use your editor's auto-formatting feature.
- "printNumberOfLegs" is a bad name, the function doesn't print the number of legs.
- There's no need to manually set `Animal::COW`.
In your example of multiple cases
no break after return true?
You don't need to break if your return. Return stops everything.
oh okay thanks!
my issue is whenever I execute my program with num2=0 and choose division option, i will get the statement " cant do the operation because num2 = 0\n" but after that I also get a sequence of number like 26900(something else). Can anyone one explain why to me ? thank you
check if this comment will notify there is a comment to my post to my email address
Make sure you followed lesson 0.10 and 0.11.
@execute is missing a return-statement if @operation is '/' and @num2 is 0.
This is a nice post. I have no idea how to create the swich statement in c++ but after reading this post I really got the solution. Thanks for this post. Thanks!!
Thank's.
it was very interesting!! static_cast....
hi.
the default row of switch in both of two functions, not possible to execute. I have to send wrong animal but compiler has stop me. so how to send wrong animal to active default of switch?
it is possible if we have a wrong member in enum. like car.
Is that right?
You can create an invalid animal by casting from an int.
Q#1
hello and thank you.
please your advice.
thank's again.
* Line 21, 31: Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().
* Your forward declarations can be prevented by moving @main below the other functions.
* Declare 1 variable per line.
* @retCode should be a bool.
* Line 17+: Should be do-while.
This post is really good I also beginner in the coding world so I have no idea about c++, I also tried to create switch statement but after reading this post I get an idea how to write switch statement without facing an error.
I confused myself so much, finally realised in the Animals struct I had put string name;
instead of using AnimalType Type;
After 30 mins of swearing at myself, I realised and felt so dumb ha ha.
Is this okay?
Sorry the formatting messed up while copying it over, but I'm too lazy to adjust it , it's not too bad haha.
* Don't use "using namespace", it can lead to name collisions.
* Line 36, 37: Initialize your variables with brace initializers.
> realised in the Animals struct I had put string name
Nope, there's not supposed to be a struct in this quiz.