考虑下面的switch语句:

switch( value )
{
  case 1:
    return 1;
  default:
    value++;
    // fall-through
  case 2:
    return value * 2;
}

此代码编译,但它是有效的(=定义的行为)C90/C99?我从未见过默认情况不是最后一个情况的代码。

编辑: 正如Jon Cage和KillianDS所写的:这真的是丑陋而令人困惑的代码,我很清楚这一点。我只对通用语法(有定义吗?)和预期的输出感兴趣。


当前回答

我有一个有趣的例子,把默认值放在顶部保存的程序空间。它是为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);
}

其他回答

C99标准没有明确说明这一点,但综合所有事实来看,它是完全有效的。

case和default标签等同于goto标签。参见6.8.1标记语句。特别有趣的是6.8.1.4,它启用了前面提到的达夫装置:

任何语句前都可以加上 声明标识符为的前缀 标签名称。标签本身 不改变流控制,其中 继续畅通无阻地穿过它们。

编辑:开关内的代码没有什么特别的;它是一个正常的if语句代码块,带有额外的跳转标签。这解释了跌倒行为,以及为什么休息是必要的。

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); 
} 

在人工程序片段中 存在标识符为I的对象 具有自动存储期限 (在块内)但从来没有 初始化,因此如果 控制表达式有一个非零 值,对printf函数的调用 将访问一个不确定值。 类似地,对函数f的调用 无法联系上。

case常量在switch语句中必须是唯一的:

6.8.4.2.3各大小写标号的表达式为整数常数 表达和没有两种的情况 常数表达式是一样的 开关语句也应相同 转换后的值。可能有 一个交换机最多只能有一个默认标签 声明。

所有的情况都被评估,然后它跳转到默认标签,如果给定:

6.8.4.2.5 The integer promotions are performed on the controlling expression. The constant expression in each case label is converted to the promoted type of the controlling expression. If a converted value matches that of the promoted controlling expression, control jumps to the statement following the matched case label. Otherwise, if there is a default label, control jumps to the labeled statement. If no converted case constant expression matches and there is no default label, no part of the switch body is executed.

是的,这是有效的,在某些情况下甚至是有用的。一般来说,如果你不需要它,就不要做。

有些情况下,当您正在将ENUM转换为字符串或将字符串转换为ENUM时,您正在写入/读取文件或从文件中读取文件。

有时需要将其中一个值设为默认值,以弥补手动编辑文件时所犯的错误。

switch(textureMode)
{
case ModeTiled:
default:
    // write to a file "tiled"
    break;

case ModeStretched:
    // write to a file "stretched"
    break;
}

我有一个有趣的例子,把默认值放在顶部保存的程序空间。它是为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);
}

case语句和default语句可以在switch语句中以任意顺序出现。default子句是一个可选子句,如果case语句中的常量都不能匹配,则匹配它。

很好的例子:

switch(5) {
  case 1:
    echo "1";
    break;
  case 2:
  default:
    echo "2, default";
    break;
  case 3;
    echo "3";
    break;
}

输出2,默认值。

如果你想让你的案例在代码中以逻辑顺序呈现(比如,不是说案例1,案例3,案例2/default),你的案例非常长,所以你不想在默认的底部重复整个案例代码,这非常有用。