校长讲堂第六讲

2020-04-10 10:18:45 浏览数 (2)

句法缺陷

要理解 C 语言程序,仅了解构成它的关键字是不够的。还要理解这些关键字是如何构成声明、表达式、语句和程序的。尽管我们可以很清楚的找到这些关键字的定义以及用法,但这些定义有时候是有悖于直觉的。 在这一节中,我们将着眼于一些不明显句法构造。

2.4 switch 语句

通常 C 中的 switch 语句中的 case 段可以进入下一个。例如,考虑下面的 C 和 Pascal 程序片断:

代码语言:javascript复制

switch(color) {
case 1: printf ("red");
break;
case 2: printf ("yellow");
break;
case 3: printf ("blue");
break;
}
case color of
1: write ('red');
2: write ('yellow');
3: write ('blue');
end

这两个程序片断都作相同的事情:根据变量 color 的值是 1、2 还是 3 打印 red、yellow 或 blue(没有新行符)。这两个程序片断非常相似,只有一点不同:Pascal 程序中没有 C 中相应的 break 语句。

C 中的 case 标签是真正的标签:控制流程可以无限制地进入到一个 case 标签中。看看另一种形式,假设 C 程序段看起来更像 Pascal:

代码语言:javascript复制

switch(color) {
case 1: printf ("red");
case 2: printf ("yellow");
case 3: printf ("blue");
}

并且假设 color 的值是 2。则该程序将打印 yellowblue,因为控制自然地转入到下一个 printf()的调用。

这既是 C 语言 switch 语句的优点又是它的弱点。说它是弱点,是因为很容易忘记一个 break 语句,从而导致程序出现隐晦的异常行为。说它是优点,是因为通过故意去掉 break 语句,可以很容易实现其他方法难以实现的控制结构。尤其是在一个大型的 switch 语句中,我们经常发现对一个 case 的处理可以简化其他一些特殊的处理。

例如,设想有一个程序是一台假想的机器的翻译器。这样的一个程序可能包含一个 switch 语句来处理各种操作码。在这样一台机器上,通常减法在对其第二个运算数进行变号后就变成和加法一样了。因此,最好可以写出这样的语句:

代码语言:javascript复制

case SUBTRACT:
opnd2 = -opnd2;
/* no break; */
case ADD:
...

另外一个例子,考虑编译器通过跳过空白字符来查找一个记号。这里,我们将空格、制表符和新行符视为是相同的,除了新行符还要引起行计数器的增长外:

代码语言:javascript复制

case 'n':
linecount  ;
/* no break */
case 't':
case ' ':
...

2.5 函数调用

和其他程序设计语言不同,C 要求一个函数调用必须有一个参数列表,但可以没有参数。因此,如果f 是一个函数,

f();

就是对该函数进行调用的语句,而

f;

什么也不做。它会作为函数地址被求值,但不会调用它。

2.6 悬挂 else 问题

在讨论任何语法缺陷时我们都不会忘记提到这个问题。尽管这一问题不是 C 语言所独有的,但它仍然伤害着那些有着多年经验的 C 程序员。考虑下面的程序片断:

代码语言:javascript复制

if(x == 0)
if(y == 0) error();
else {
z = x   y;
f(&z);
}
。

希望这段程序能够按照实际的情况运行,应该这样写:

代码语言:javascript复制

if(x == 0) {
        if(y == 0)
        error();
    else {
            z = x   y;
            f(&z);
            }
}

发生时什么也不做。如果要达到第一个例子的效果,应该写:

代码语言:javascript复制

if(x == 0) {
        if(y ==0)
        error();
        }
else {
        z = z   y;
        f(&z);
}

0 人点赞