tan*_*ius 168 c switch-statement
请考虑以下switch
声明:
switch( value )
{
case 1:
return 1;
default:
value++;
// fall-through
case 2:
return value * 2;
}
Run Code Online (Sandbox Code Playgroud)
此代码编译,但它对C90/C99有效(=已定义的行为)?我从未见过默认情况不是最后一种情况的代码.
编辑:
正如Jon Cage和KillianDS所写:这是非常丑陋和令人困惑的代码,我很清楚它.我只对一般语法(它定义了吗?)和预期输出感兴趣.
Sal*_*lil 83
case语句和default语句可以在switch语句中以任何顺序发生.default子句是一个可选子句,如果case语句中没有任何常量可以匹配,则匹配该子句.
好例子 :-
switch(5) {
case 1:
echo "1";
break;
case 2:
default:
echo "2, default";
break;
case 3;
echo "3";
break;
}
Outputs '2,default'
Run Code Online (Sandbox Code Playgroud)
非常有用,如果你希望你的案例在代码中以逻辑顺序呈现(如,不是说案例1,案例3,案例2 /默认),你的案件很长,所以你不想重复整个案例底部的代码为默认值
Sec*_*ure 78
C99标准没有明确说明这一点,但将所有事实放在一起,它是完全有效的.
A case
和default
标签等同于goto
标签.请参见6.8.1标记语句.特别有趣的是6.8.1.4,这使得已经提到过的Duff设备:
任何语句都可以在前缀之前声明标识符作为标签名称.标签本身并不会改变控制流程,而这种控制流程在它们之间继续畅通无阻.
编辑:开关内的代码没什么特别的; 它是一个正常的代码块,如在if
-statement中,带有额外的跳转标签.这解释了坠落行为以及为什么break
有必要.
6.8.4.2.7甚至给出了一个例子:
switch (expr)
{
int i = 4;
f(i);
case 0:
i=17;
/*falls through into default code */
default:
printf("%d\n", i);
}
Run Code Online (Sandbox Code Playgroud)
在人工程序片段中,标识符为i的对象存在自动存储持续时间(在块内)但从未初始化,因此如果控制表达式具有非零值,则对printf函数的调用将访问不确定的值.同样,无法访问函数f的调用.
case常量在switch语句中必须是唯一的:
6.8.4.2.3每个case标签的表达式应为整型常量表达式,同一switch语句中的两个case常量表达式在转换后不应具有相同的值.switch语句中最多可能有一个默认标签.
评估所有案例,然后跳转到默认标签,如果给定:
6.8.4.2.5对控制表达式执行整数提升.每个case标签中的常量表达式将转换为控制表达式的提升类型.如果转换后的值与提升的控制表达式的值匹配,则控制将跳转到匹配的案例标签后面的语句.否则,如果存在默认标签,则控制将跳转到带标签的语句.如果没有转换的大小写常量表达式匹配且没有默认标签,则不会执行开关主体的任何部分.
kri*_*iss 47
它在某些情况下是有效且非常有用的.
请考虑以下代码:
switch(poll(fds, 1, 1000000)){
default:
// here goes the normal case : some events occured
break;
case 0:
// here goes the timeout case
break;
case -1:
// some error occurred, you have to check errno
}
Run Code Online (Sandbox Code Playgroud)
关键是上面的代码比级联更具可读性和效率if
.您可以将默认设置放在最后,但它没有意义,因为它会将注意力集中在错误情况而不是正常情况(这是这种default
情况).
实际上,这不是一个很好的例子,在投票中你知道最多可能发生多少事件.我真正的问题是,有是有定义的一组输入值的情况下,也有"例外"和正常的情况下.如果最好将异常或正常情况放在前面是一个选择问题.
在软件领域,我想到了另一个非常常见的情况:带有一些终端值的递归.如果您可以使用开关表达它,default
将是包含递归调用和区分元素(个别情况)终端值的常用值.通常无需关注终端价值.
另一个原因是案例的顺序可能会改变编译的代码行为,这对性能很重要.大多数编译器将按照与交换机中出现的代码相同的顺序生成编译的汇编代码.这使得第一种情况与其他情况大不相同:除了第一种情况之外的所有情况都将涉及跳转并且将清空处理器流水线.你可能会理解它像分支预测器默认运行交换机中第一个出现的情况.如果一个案例比其他人更常见那么你有很好的理由把它作为第一个案例.
阅读评论这是在阅读英特尔编译器分支循环重组关于代码优化后原始海报提出问题的具体原因.
然后将成为代码可读性和代码性能之间的一些仲裁.可能更好地发表评论向未来的读者解释为什么案例首先出现.
Jen*_*edt 15
是的,这是有效的,在某些情况下它甚至是有用的.通常,如果您不需要它,请不要这样做.
switch语句中没有定义的顺序.您可以将案例视为命名标签,如goto
标签.与人们在这里想到的相反,在值为2的情况下,默认标签不会跳转到.用一个经典的例子来说明,这里是Duff的设备,它是switch/case
C中极端的典型代表.
send(to, from, count)
register short *to, *from;
register count;
{
register n=(count+7)/8;
switch(count%8){
case 0: do{ *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
}while(--n>0);
}
}
Run Code Online (Sandbox Code Playgroud)
我认为将一个'default'放在case语句结尾之外的某个地方的一个场景是在状态机中,其中一个无效状态应该重置机器并继续进行,就好像它是初始状态一样.例如:
switch(widget_state) { default: /* Fell off the rails--reset and continue */ widget_state = WIDGET_START; /* Fall through */ case WIDGET_START: ... break; case WIDGET_WHATEVER: ... break; }
另一种安排,如果无效状态不应重置机器但应易于识别为无效状态:
switch(widget_state) { case WIDGET_IDLE: widget_ready = 0; widget_hardware_off(); break; case WIDGET_START: ... break; case WIDGET_WHATEVER: ... break; default: widget_state = WIDGET_INVALID_STATE; /* Fall through */ case WIDGET_INVALID_STATE: widget_ready = 0; widget_hardware_off(); ... do whatever else is necessary to establish a "safe" condition }
然后,其他地方的代码可以检查(widget_state == WIDGET_INVALID_STATE)并提供任何错误报告或状态重置行为.例如,状态栏代码可能会显示错误图标,并且可以为WIDGET_INVALID_STATE和WIDGET_IDLE启用在大多数非空闲状态下禁用的"启动窗口小部件"菜单选项.
另一个示例:如果"default"是一个意外情况,并且您想记录错误但也做一些合理的事情,这可能很有用.我自己的一些代码示例:
switch (style)
{
default:
MSPUB_DEBUG_MSG(("Couldn't match dash style, using solid line.\n"));
case SOLID:
return Dash(0, RECT_DOT);
case DASH_SYS:
{
Dash ret(shapeLineWidth, dotStyle);
ret.m_dots.push_back(Dot(1, 3 * shapeLineWidth));
return ret;
}
// more cases follow
}
Run Code Online (Sandbox Code Playgroud)
在某些情况下,您要将ENUM转换为字符串或将字符串转换为枚举,以防您正在写入/读取文件.
您有时需要使其中一个值默认为覆盖手动编辑文件所产生的错误.
switch(textureMode)
{
case ModeTiled:
default:
// write to a file "tiled"
break;
case ModeStretched:
// write to a file "stretched"
break;
}
Run Code Online (Sandbox Code Playgroud)