Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 开关语句:默认值必须是最后一种情况?_C_Switch Statement - Fatal编程技术网

C 开关语句:默认值必须是最后一种情况?

C 开关语句:默认值必须是最后一种情况?,c,switch-statement,C,Switch Statement,考虑以下开关语句: switch( value ) { case 1: return 1; default: value++; // fall-through case 2: return value * 2; } 这段代码可以编译,但它对C90/C99有效吗?我从未见过默认情况不是最后一种情况的代码 编辑: 正如Jon Cage和KillianDS所写:这是一个非常丑陋和混乱的代码,我很清楚这一点。我只对一般语法(它定义了吗?)和预期的输出感兴趣。

考虑以下
开关
语句:

switch( value )
{
  case 1:
    return 1;
  default:
    value++;
    // fall-through
  case 2:
    return value * 2;
}
这段代码可以编译,但它对C90/C99有效吗?我从未见过默认情况不是最后一种情况的代码

编辑:

正如Jon Cage和KillianDS所写:这是一个非常丑陋和混乱的代码,我很清楚这一点。我只对一般语法(它定义了吗?)和预期的输出感兴趣。

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

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'

如果您希望案例在代码中以逻辑顺序呈现(如,不说案例1、案例3、案例2/默认),并且案例非常长,因此您不希望在默认情况下重复底部的整个案例代码,这非常有用。我认为,允许跳转通常是不好的,因为它会导致一些非常混乱的意大利面代码

几乎可以肯定,最好将这些情况分解为几个switch语句或更小的函数

[编辑]@三体:您的示例:

Example from UCS-2 to UTF-8 conversion 

r is the destination array, 
wc is the input wchar_t  

switch(utf8_length) 
{ 
    /* Note: code falls through cases! */ 
    case 3: r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
    case 2: r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 
    case 1: r[0] = wc;
}
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; } 如果它是这样写的,它的意图(我想)会更清楚:

if( utf8_length >= 1 )
{
    r[0] = wc;

    if( utf8_length >= 2 )
    {
        r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 

        if( utf8_length == 3 )
        {
            r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
        }
    }
}   
[edit2]@Tristopia:您的第二个示例可能是后续操作的最清晰示例:

for(i=0; s[i]; i++)
{
    switch(s[i])
    {
    case '"': 
    case '\'': 
    case '\\': 
        d[dlen++] = '\\'; 
        /* fall through */ 
    default: 
        d[dlen++] = s[i]; 
    } 
}
…但就我个人而言,我会将评论识别分为它自己的功能:

bool isComment(char charInQuestion)
{   
    bool charIsComment = false;
    switch(charInQuestion)
    {
    case '"': 
    case '\'': 
    case '\\': 
        charIsComment = true; 
    default: 
        charIsComment = false; 
    } 
    return charIsComment;
}

for(i=0; s[i]; i++)
{
    if( isComment(s[i]) )
    {
        d[dlen++] = '\\'; 
    }
    d[dlen++] = s[i]; 
}

default
条件可以是开关中存在case子句的任何位置。它不必是最后一条。我见过将默认值作为第一个子句的代码。
案例2:
正常执行,即使default子句在其上方

作为测试,我将示例代码放在一个名为
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

switch语句中没有定义顺序。您可以将案例视为一个命名标签,例如
goto
标签。与人们在这里的想法相反,在值2的情况下,默认标签不会跳到。用一个经典的例子来说明,这里是,这是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);
  }
}

在某些情况下,它是有效的并且非常有用

考虑以下代码:

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
}
关键是上面的代码比级联的
if
更具可读性和效率。您可以将
default
放在末尾,但这毫无意义,因为它会将您的注意力集中在错误案例上,而不是正常案例(这里是
default
案例)

实际上,这不是一个很好的例子,在
poll
中,您知道最多可能发生多少事件。我真正的观点是,有些情况下有一组定义的输入值,其中有“异常”和正常情况是否最好将异常或正常情况放在前面是一个选择问题。

在软件领域,我想到了另一种非常常见的情况:带有一些终端值的递归。如果您可以使用开关来表示它,
default
将是包含递归调用和可分辨元素(个别情况)终端值的常用值。通常不需要关注终端值

另一个原因是案例的顺序可能会改变编译后的代码行为,这对性能很重要。大多数编译器将按照代码在开关中出现的顺序生成编译后的汇编代码。这使得第一种情况与其他情况非常不同:除第一种情况外,所有情况都将涉及跳转,这将清空处理器管道。您可能会理解为branch predictor默认运行开关中第一个出现的情况。如果一个案例比其他案例更常见,那么你有很好的理由把它作为第一个案例

阅读评论这就是为什么原始海报在阅读了关于代码优化的内容后提出这个问题的具体原因


然后,它将成为代码可读性和代码性能之间的某种仲裁。也许最好给未来的读者写一条评论,解释为什么案例首先出现。

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的调用 联系不到

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

switch( value )
{
  case 1:
    return 1;
  default:
    value++;
    // fall-through
  case 2:
    return value * 2;
}
6.8.4.2.3每个箱子标签的表达式应为整数常量 表达和没有两种情况 同一格式中的常量表达式 switch语句应具有相同的属性 转换后的值。可能有 开关中最多有一个默认标签 状态
  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(textureMode)
{
case ModeTiled:
default:
    // write to a file "tiled"
    break;

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