[英]how cases get evaluated in switch statements (C)
I'm corrently learning C, and I'm allready familiar with basic programming concepts I have a question about switch statement for ex in the following code我正在学习C,我已经熟悉基本的编程概念我有一个关于以下代码中ex的switch语句的问题
for(int i =0 ; i<20; i++){
switch(i){
case 0: i+=5; /*label 1*/
case 1: i+=2; /*label 2*/
case 5: i+=5; /*label 3*/
default : i+=4; /*label 4*/
}
printf("%d\t",i);
}
the output is 16 21输出是 16 21
that means that case at label 1 is executed first, then since there is no break, label 2, 3 and 4 are also executed, the question is that if label 1 is executed then value of i is updated to 5, does other cases check the condition first (if i =1 or 5 ) then execute or it just execute anything without checking?也就是说先执行label 1的case,然后因为没有break,label 2, 3, 4也被执行,问题是如果label 1被执行了,那么i的值更新为5,其他case会检查首先条件(如果 i =1 或 5 )然后执行或它只是执行任何东西而不检查?
In the labeled parts of the switch statement there are no break statements在 switch 语句的标记部分中没有 break 语句
for(int i =0 ; i<20; i++){
switch(i){
case 0: i+=5; /*label 1*/
case 1: i+=2; /*label 2*/
case 5: i+=5; /*label 3*/
default : i+=4; /*label 4*/
}
printf("%d\t",i);
}
So when i
is equal to 0
(in the first iteration of the loop) then all these statements因此,当
i
等于0
(在循环的第一次迭代中)时,所有这些语句
case 0: i+=5; /*label 1*/
case 1: i+=2; /*label 2*/
case 5: i+=5; /*label 3*/
default : i+=4; /*label 4*/
are executed sequentially after passing the control to the case label 0:
and i
becomes equal to 16
.在将控制权传递给 case 标签
0:
后依次执行,并且i
变得等于16
。
You can imagine the switch statement in the first iteration of the loop the following way您可以通过以下方式想象循环第一次迭代中的 switch 语句
goto Label0;
Label0: i+=5; /*label 1*/
Label1: i+=2; /*label 2*/
Label5: i+=5; /*label 3*/
Labeldefault : i+=4; /*label 4*/
Labels are just labels.标签只是标签。 They do not execute any code.
它们不执行任何代码。 After evaluating the expression in the switch statement
在对 switch 语句中的表达式求值之后
switch(i)
the control is passed to the corresponding labeled statement and all the following statements are executed sequentially if there is no jump statement.控制权传递给相应的标记语句,如果没有跳转语句,则按顺序执行以下所有语句。
Then in the third expression of the for loop i
is incremented and becomes equal to 17
.然后在 for 循环的第三个表达式中,
i
递增并等于17
。
So in the next iteration of the for loop the control at once is passed to the label default
and i
becomes equal to 21
.所以在 for 循环的下一次迭代中,控制立即传递给标签
default
并且i
变得等于21
。
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.这是一个很好的问题,实际上揭示了 C 和 C++ 中
switch
语句的内部结构,有时会与级联if-else
语句混淆。
The switch
statement in C/C++ works as follows: C/C++ 中的
switch
语句的工作原理如下:
switch
statement switch
语句中的条件的表达式 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.正是因为 (1) 和 (2),您创建的
switch
没有按照您预期的方式运行,并且它不会在case
语句的执行过程中重新计算初始表达式。
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).与级联
if-else
语句相比,您的case
语句本质上是按顺序编译的指令块,由 (3) 中提到的跳转表引用。 Once the execution reaches a case
statement, it will automatically cascade over the next case
statements if break
is not encountered.一旦执行到一个
case
语句,如果没有遇到break
,它将自动级联下一个case
语句。 The break
actually instructs the compiler to jump over the switch statement and stop executing the case
statements. break
实际上指示编译器跳过switch 语句并停止执行case
语句。
Check out this commented disassembly of your switch statement, just to have a better grip of what's happening under the hood:看看你的 switch 语句的这个评论反汇编,只是为了更好地掌握引擎盖下发生的事情:
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.注意:这是使用
-m32 -O0
gcc 选项构建的,以使用更易于阅读的 32 位代码,并禁用优化。
You can clearly see that after the jump is made (to any case statement) there is no further reevaluation of i
( -0x10(%ebp)
).您可以清楚地看到,在进行跳转后(对于任何 case 语句),不再对
i
( -0x10(%ebp)
) 进行重新评估。 Also, when the case is executed, it automatically cascades to the next one if no break
is used.此外,当案例被执行时,如果没有使用
break
,它会自动级联到下一个案例。
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 .现在,您可能会问自己为什么会出现这种奇怪的行为,答案是 (3):以尽可能少的比较跳转到相应的 case 语句。
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.当
case
语句的数量真正增加时,尤其是当用于case
语句的值之间的分布是恒定的时,C/C++ 中的switch
语句显示了它们的真正优势。
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
):例如,假设我们有一个包含 100 个
case
值的大型switch
语句,在 case 值之间具有1
的常数分布,并且 switch 表达式 ( i
) 的计算结果为100
( switch
最后一个case
):
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:如果您使用级联
if-else
语句,您将获得 100 次比较,但此switch
只需使用几条指令即可获得相同的结果,按以下顺序:
case
statements in a jump tablecase
语句switch
and store the result (ie: fetch i
)switch
的条件并存储结果(即: fetch i
)i
by 1
, the first case
statement, results in index 99)i
减1
,第一个case
语句,结果索引为99)case
without any further operationcase
不做任何进一步的操作The same will apply if your case values have a spread of 2
:如果您的案例值的传播为
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.您的编译器也应该检测到这种传播,在减去第一个 case 值(即
1
)后,将除以2
以获得跳转表的相同索引。
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.当您想根据只能在运行时计算的值对代码进行分支时,并且当该值属于均匀集合时,
switch
语句的这种复杂的内部工作方式使其成为 C/C++ 中非常强大的工具传播,或者至少是传播均匀的值组。
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.当
case
值没有均匀分布时, switch
效率会降低,并且它开始执行类似于我们使用级联if-else
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.