1

我正在阅读 KN King 的A Modern Approach to C Programming,第 2 版。

它说,除了一般的 switch 语句(带有 case 关键字)之外,还有其他形式的 switch 语句。switch 语句的一般形式是

switch (exp)
{
 case constant-exp:
  statement;
  break;
 case constant-exp:
  statement;
  break;
 ...
 ...
 default:
  statement;
  break;
}

它还说(在 Q&A 中)switch 语句可以有没有 case 关键字的形式。我尝试运行一个没有 case 关键字的示例,但它没有运行(在 std=-c99 下)。

所以,我想知道在标准 C99 中有效的其他形式的 switch 语句是什么。

编辑:引自书

在最常见的形式中,switch 语句具有以下形式

switch ( expression ) {
case constant-expression : statements
...
case constant-expression : statements
default : statements
}

问答

**问:为 switch 语句提供的模板将其描述为“最常见的形式”。还有其他形式吗?

A**:switch 语句比本章中描述的更通用,尽管这里给出的描述对于几乎所有程序来说都足够通用。例如,switch 语句可以包含前面没有单词 case 的标签,这会导致有趣的 (?) 陷阱。假设我们不小心误卖了 default 这个词:

switch(...) {
...
defualt:  ...
}

编译器可能不会检测到错误,因为它假定defualt是一个普通标签。

4

4 回答 4

7

语句的语法switch是:

switch ( expression ) statement

其中该statement部分通常是包含以下形式的标记语句的块(复合语句):

case constant-expression : statement

或者

default : statement

switch声明不需要包含或标签,但如果您不打算拥有一个或多个此类标签,则使用 a 是没有意义的。例如,这个:casedefaultswitch

switch (42);

是一个完全合法switch的声明(受控声明是 null 声明;),但它也完全没用。

我怀疑你误解了这本书的内容。

你从书中引用说:

例如,switch 语句可以包含前面没有单词 case 的标签,这会导致有趣的 (?) 陷阱。假设我们不小心拼错了 default 这个词:
switch(...) { ... defualt: ... }

switch语句的内容应该是一个包含一系列标签的块casedefault每个标签都以 abreak或注释结尾,指示控制流进入下一个案例。关键是语言不需要这个。它指定语法的方式为您提供了很大的自由度(也许太多了!)。case唯一的限制是default标签不能出现在switch语句之外。

例如,假设您不小心写了:

enum blah { foo, bar, baz };

switch (expr) {
    case foo:
        /* ... */
        break;
    bar:           /* forgot the `case` keyword */
        /* ... */
        break;
    defualt:       /* misspelled "default" */
        /* ... */
        break;
}

既不bar:是也不defualt:是预期的——但它们都是完全合法的。它们是普通的标签,可以成为goto声明的目标。由于没有goto针对任一标签,因此将永远不会执行相应的代码块。如果expr等于foo,会跳转到case foo:; 对于任何其他值,它将跳转到switch语句的末尾。

而且因为它们是完全合法的,编译器不一定会警告你这个错误。

这是 C 语言中的一种常见现象。语法是如此“密集”,以至于一个看似轻微的拼写错误很容易给你一些语法上有效的东西,但其行为与你的意图完全不同。

提高编译器的警告级别,并注意您看到的所有警告。并且要小心;正确编写代码的责任最终是你的。编译器可以提供帮助,但它无法捕获所有错误。

于 2013-11-11T16:23:54.717 回答
2

关于您的观察除了一般的 switch 语句(带有 case 关键字)之外,还有其他形式的 switch 语句 通常,switch 语句有很好的文档记录,但是在使用 case 语句的方式上有一些有趣的变化......

尽管这里没有什么惊天动地的,但可能有用的是注意: Sun(一种 unix 风格)GNU C 编译器有一个扩展,它提供 了与语句一起使用的case 范围switch()。因此,例如,而不是使用经典语法

:
case 'A':
case 'B':
:
case 'Z':  
//do something here.
break;

等等...

案例范围语法可用于描述条件:

switch(input)  {

    case 'A' ... 'Z':
       printf("Upper case letter detected");
       break;
    case 'a' ... 'z':
       printf("Lower case letter has been detected");
       break;
};  

重要提示: case 范围不是 C 标准(C99 或 C11)的一部分,而只是我提到的环境的扩展,绝不应被视为可移植的。案例范围越来越受欢迎(或至少引起人们的兴趣),并且可能会在某些时候作为C 标准的一部分包含在内,但目前还没有(AFAIK)。

于 2013-11-11T20:08:41.037 回答
1

C-99 的首选来源是C-99 标准,当然 C-99 已被 C11 取代。switch 语句位于 C-99 标准的第 134 页。他们举了一个例子,说明你可以拥有的最不通用的 switch 语句:

EXAMPLE In the artificial program fragment
switch (expr)
{
    int i = 4;
    f(i);
case 0:
    i=17;
    /* falls through into default code */
default:
    printf("%d\n", i);
}

标识符为 i 的对象具有自动存储持续时间(在块内)但从未初始化,因此如果控制表达式具有非零值,则对 printf 函数的调用将访问一个不确定的值。同样,无法调用函数 f。

请注意这不是“标准”的方式(通常是错误的代码)。

  • 您的代码不在任何casedefault标签下。实际上,似乎只承认标识符声明,因此这i是 switch 语句范围内的有效变量,但是设置它或调用没有casedefault标签的函数会导致错误。
  • 我认为作者希望您注意的是,break每个标签下都没有 a 是有效的。在这个例子中,case 0一直到default标签,但如果在下面有其他case标签case 0,它会遍历每个标签,直到你确实遇到了一个 break 语句。
    • 就此而言,尽管不在此示例中,但您可以default先放置标签。同样,如果您没有在其后放置一个 break,您将在任何以下标签下执行代码。

正如我在评论中提到的,default如果需要,您可以只使用一个标签,但这实际上会使 switch 语句毫无意义:

switch (exp)
{
default:
    statement;
}

这实际上相当于{ statement; }.

c - '0'顺便说一句,您可以做一些巧妙的(但令人困惑的)技巧来避免使用 break 语句,例如,这是一种将数字字符转换c为整数的有效方法(尽管效率低于):

int i = 0;
switch (c) {
case '9': ++i;
case '8': ++i;
case '7': ++i;
case '6': ++i;
case '5': ++i;
case '4': ++i;
case '3': ++i;
case '2': ++i;
case '1': ++i;
case '0':
default:
}
于 2013-11-11T16:34:53.457 回答
0

Wiki 描述了如何考虑不同的编码语言定义如下 switch 语句,

在大多数语言中,使用一个或两个关键字在许多单独的行中定义 switch 语句。一个典型的语法是:

1. The first line contains the basic keyword, usually switch, case or select, 
   followed by an expression which is often referred to as the control expression
   or control variable of the switch statement.

2. Subsequent lines define the actual cases (the values) with corresponding 
   sequences of statements that should be executed when a match occurs.

每个备选方案都以控制变量可能匹配的特定值或值列表开始,这将导致控制转到相应的语句序列。值(或值的列表/范围)通常通过冒号或暗示箭头与相应的语句序列分开。在许多语言中,每个大小写之前还必须有一个关键字,例如 case 或 when。通常也允许使用可选的默认情况,由 default 或 else 关键字指定;当没有其他情况与控制表达式匹配时,将执行此操作。

于 2013-11-11T16:27:59.067 回答