考虑下面的switch语句:
switch( value )
{
case 1:
return 1;
default:
value++;
// fall-through
case 2:
return value * 2;
}
此代码编译,但它是有效的(=定义的行为)C90/C99?我从未见过默认情况不是最后一个情况的代码。
编辑:
正如Jon Cage和KillianDS所写的:这真的是丑陋而令人困惑的代码,我很清楚这一点。我只对通用语法(有定义吗?)和预期的输出感兴趣。
switch语句中没有定义的顺序。你可以把这些情况看作一个命名标签,一个goto标签。与人们在这里的想法相反,在值2的情况下,默认标签不会跳转到。为了用一个经典的例子来说明,这里是Duff的设备,它是C语言中switch/case的极端代表。
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);
}
}
我有一个有趣的例子,把默认值放在顶部保存的程序空间。它是为Arduino Nano设计的,节省了8字节的闪存(RAM是一样的)。
供你参考,这两组代码是
#if 1 // toggle this 0 or 1
// 3138/265 bytes
uint8_t g_BuiltinLedGlowState = 0; // dropping '= 0' saves nothing
void AdvanceBuiltinLedGlow_3Ph(){
switch(++g_BuiltinLedGlowState){
default:
g_BuiltinLedGlowState = 0;
// drop through // break;
case 0: // bright
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
break;
case 1: // dim
pinMode(LED_BUILTIN, INPUT_PULLUP);
break;
case 2: // off
pinMode(LED_BUILTIN, INPUT);
break;
}
}
#elif 1
// 3146/265 bytes
uint8_t g_BuiltinLedGlowState = 0; // dropping '= 0' saves nothing
void AdvanceBuiltinLedGlow_3Ph(){
switch(++g_BuiltinLedGlowState){
case 1: // bright
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
break;
case 2: // dim
pinMode(LED_BUILTIN, INPUT_PULLUP);
break;
case 3: // off
pinMode(LED_BUILTIN, INPUT);
// drop through // break;
default:
g_BuiltinLedGlowState = 0;
break;
}
}
#endif
// the loop function runs over and over again forever
void loop() {
Serial.println(g_BuiltinLedGlowState, DEC);
AdvanceBuiltinLedGlow_3Ph();
delay(1000);
}
在一种情况下,我认为将默认情况设置在switch语句的末尾以外的地方是合适的,即在状态机中,无效状态应该重置机器并继续执行,就像它是初始状态一样。例如:
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启用。