考虑下面的switch语句:

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

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

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


当前回答

另一个例子:如果“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
  }

其他回答

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

在一种情况下,我认为将默认情况设置在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启用。

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.

默认条件可以是开关中case子句可以存在的任何位置。它不需要是最后一个子句。我曾见过将默认值作为第一个子句的代码。情况2:正常执行,即使默认子句在它上面。

作为测试,我把示例代码放在一个名为test(int value){}的函数中,并运行:

  printf("0=%d\n", test(0));
  printf("1=%d\n", test(1));
  printf("2=%d\n", test(2));
  printf("3=%d\n", test(3));
  printf("4=%d\n", test(4));

输出结果为:

0=2
1=1
2=4
3=8
4=10

另一个例子:如果“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
  }