Chapter 3 - Control Flow(二)

3.4 Switch

The switch statement is a multi-way decision that tests whether an expression matches one of a number of constant integer values, and branches accordingly.

switch语句是一种多路判定语句,它测试表达式是否与一些常量整数值中的某一个值匹配,并执行相应的分支动作。

 

switch (expression) {

case const-expr: statements

case const-expr: statements

default: statements

}

Each case is labeled by one or more integer-valued constants or constant expressions. If a case matches the expression value, execution starts at that case. All case expressions must be different. The case labeled default is executed if none of the other cases are satisfied. A default is optional; if it isn't there and if none of the cases match, no action at all takes place. Cases and the default clause can occur in any order.

每一个分支都由一个或多个整数值常量或常量表达式标记。如果某个分支与表达式的值匹配,则从该分支开始执行。各分支表达式必须互不相同。如果没有哪一分支能匹配表达式,则执行标记为default的分支。default分支是可选的。如果没有default分支也没有其它分支与表达式的值匹配,则该switch语句不执行任何动作。各分支及default分支的排列次序是任意的。

 

In Chapter 1 we wrote a program to count the occurrences of each digit, white space, and all other characters, using a sequence of if ... else if ... else. Here is the same program with a switch:

我们在第1 章中曾用ifelse ifelse 结构编写过一个程序以统计各个数字、空白符及其它所有字符出现的次数。下面我们用switch语句改写该程序如下:


#include <stdio.h>

main() /* count digits, white space, others */

{

int c, i, nwhite, nother, ndigit[10];

nwhite = nother = 0;

for (i = 0; i < 10; i++)

ndigit[i] = 0;

while ((c = getchar()) != EOF) {

switch (c) {

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

ndigit[c-'0']++;

break;

case ' ':

case '\n':

case '\t':

nwhite++;

break;

default:

nother++;

break;

}

}

printf("digits =");

for (i = 0; i < 10; i++)

printf(" %d", ndigit[i]);

printf(", white space = %d, other = %d\n", nwhite, nother);

return 0;

}


The break statement causes an immediate exit from the switch. Because cases serve just as labels, after the code for one case is done, execution falls through to the next unless you take explicit action to escape. break and return are the most common ways to leave a switch. A break statement can also be used to force an immediate exit from while, for, and do loops, as will be discussed later in this chapter.

break 语句将导致程序的执行立即从switch 语句中退出。在switch 语句中,case的作用只是一个标号,因此,某个分支中的代码执行完后,程序将进入下一分支继续执行,除非在程序中显式地跳转。跳出switch语句最常用的方法是使用break语句与return句。break 语句还可强制控制从whilefor do 循环语句中立即退出,对于这一点,我们稍后还将做进一步介绍。


Falling through cases is a mixed blessing. On the positive side, it allows several cases to be attached to a single action, as with the digits in this example. But it also implies that normally each case must end with a break to prevent falling through to the next. Falling through from one case to another is not robust, being prone to disintegration when the program is modified. With the exception of multiple labels for a single computation, fall-throughs should be used sparingly, and commented.

依次执行各分支的做法有优点也有缺点。好的一面是它可以把若干个分支组合在一起完成一个任务,如上例中对数字的处理。但是,正常情况下为了防止直接进入下一个分支执行,每个分支后必须以一个break语句结束。从一个分支直接进入下一个分支执行的做法并不健全,这样做在程序修改时很容易出错。除了一个计算需要多个标号的情况外,应尽量减少从一个分支直接进入下一个分支执行这种用法,在不得不使用的情况下应该加上适当的程序注释。



As a matter of good form, put a break after the last case (the default here) even though it's logically unnecessary. Some day when another case gets added at the end, this bit of defensive programming will save you.

作为一种良好的程序设计风格,在switch语句最后一个分支(即default分支)的后面也加上一个break语句。这样做在逻辑上没有必要,但当我们需要向该switch语句后添加其它分支时,这种防范措施会降低犯错误的可能性。


3.5 Loops - While and For

We have already encountered the while and for loops. In

while (expression)

statement

the expression is evaluated. If it is non-zero, statement is executed and expression is re-evaluated. This cycle continues until expression becomes zero, at which point execution resumes after statement.

首先求表达式的值。如果其值非0,则执行语句,并再次求该表达式的值。这一循环过程一直进行下去,直到该表达式的值为0为止,随后继续执行语句后面的部分。

The for statement

for (expr; expr; expr3)

statement

is equivalent to

expr;

while (expr2) {

statement

expr;

}

except for the behaviour of continue, which is described in Section 3.7.

Grammatically, the three components of a for loop are expressions. Most commonly, expr1 and expr3 are assignments or function calls and expr2 is a relational expression. Any of the three parts can be omitted, although the semicolons must remain. If expr1 or expr3 is omitted, it is simply dropped from the expansion. If the test, expr2, is not present, it is taken as permanently true, so

从语法角度看,for 循环语句的3 个组成部分都是表达式。最常见的情况是,表达式1与表达式3 是赋值表达式或函数调用,表达式2 是关系表达式。这3 个组成部分中的任何部分都可以省略,但分号必须保留。如果在for 语句中省略表达式1 与表达式3,它就退化成while 循环语句。如果省略测试条件,即表达式2,则认为其值永远是真值,因此,下列for循环语句:

for (;;) {

...

}

is an ``infinite'' loop, presumably to be broken by other means, such as a break or return.

是一个“无限”循环语句,这种语句需要借助其它手段(如break语句或return语句)才能终止执行。

Whether to use while or for is largely a matter of personal preference. For example, in

在设计程序时到底选用while循环语句还是for循环语句,主要取决于程序设计人员的个人偏好。例如,在下列语句中:

while ((c = getchar()) == ' ' || c == '\n' || c = '\t')

; /* skip white space characters */

there is no initialization or re-initialization, so the while is most natural.

因为其中没有初始化或重新初始化的操作,所以使用whi1e循环语句更自然一些。

The for is preferable when there is a simple initialization and increment since it keeps the loop control statements close together and visible at the top of the loop. This is most obvious in

如果语句中需要执行简单的初始化和变量递增,使用for 语句更合适一些,它将循环控制语句集中放在循环的开头,结构更紧凑、更清晰。通过下列语句可以很明显地看出这一点

for (i = 0; i < n; i++)

...

which is the C idiom for processing the first n elements of an array, the analog of the Fortran DO loop or the Pascal for. The analogy is not perfect, however, since the index variable i retains its value when the loop terminates for any reason. Because the components of the for are arbitrary expressions, for loops are not restricted to arithmetic progressions. Nonetheless, it is bad style to force unrelated computations into the initialization and increment of a for, which are better reserved for loop control operations.

这是C 语言处理数组前n 个元素的一种习惯性用法,它类似于Fortran 语言中的DO 循环或Pascal 语言中的for 循环。但是,这种类比并不完全准确,因为在C 语言中,for 循环语句的循环变量和上限在循环体内可以修改,并且当循环因某种原因终止后循环变量i 的值仍然保留。因为for语句的各组成部分可以是任何表达式,所以for语句并不限于通过算术级数进行循环控制。尽管如此,牵强地把一些无关的计算放到for 语句的初始化和变量递增部分是一种不好的程序设计风格,该部分放置循环控制运算更合适。

As a larger example, here is another version of atoi for converting a string to its numeric equivalent. This one is slightly more general than the one in Chapter 2; it copes with optional leading white space and an optional + or - sign. (Chapter 4 shows atof, which does the same conversion for floating-point numbers.)

作为一个较大的例子,我们来重新编写将字符串转换为对应数值的函数atoi。这里编写的函数比第2 章中的atoi函数更通用,它可以处理可选的前导空白符以及一个可选的加(+或减(-)号。(第4 章将介绍函数atof,它用于对浮点数执行同样的转换。)


The structure of the program reflects the form of the input:

skip white space,如果有空白符的话,则跳过

if any get sign,如果有符号的话,则读取符号

if any get integer part and convert it取整数部分,并执行转换

Each step does its part, and leaves things in a clean state for the next. The whole process terminates on the first character that could not be part of a number.

其中的每一步都对输入数据进行相应的处理,并为下一步的执行做好准备。当遇到第一个不能转换为数字的字符时,整个处理过程终止。

#include <ctype.h>

/* atoi: convert s to integer; version 2 */

int atoi(char s[])

{

int i, n, sign;

for (i = 0; isspace(s[i]); i++) /* skip white space */

;

sign = (s[i] == '-') ? -1 : 1;

if (s[i] == '+' || s[i] == '-') /* skip sign */

i++;

for (n = 0; isdigit(s[i]); i++)

n = 10 * n + (s[i] - '0');

return sign * n;

}

The standard library provides a more elaborate function strtol for conversion of strings to long integers; see Section 5 of Appendix B.


The advantages of keeping loop control centralized are even more obvious when there are several nested loops. The following function is a Shell sort for sorting an array of integers. The basic idea of this sorting algorithm, which was invented in 1959 by D. L. Shell, is that in early stages, far-apart elements are compared, rather than adjacent ones as in simpler interchange sorts. This tends to eliminate large amounts of disorder quickly, so later stages have less work to do. The interval between compared elements is gradually decreased to one, at which point the sort effectively becomes an adjacent interchange method.

把循环控制部分集中在一起,对于多重嵌套循环,优势更为明显。下面的函数是对整型数组进行排序的Shell排序算法。Shell排序算法是D. L. Shell1959 年发明的,其基本思想是:先比较距离远的元素,而不是像简单交换排序算法那样先比较相邻的元素。这样可以快速减少大量的无序情况,从而减轻后续的工作。被比较的元素之间的距离逐步减少,直到减少为1,这时排序变成了相邻元素的互换。


/* shellsort: sort v[0]...v[n-1] into increasing order */

void shellsort(int v[], int n)

{

int gap, i, j, temp;

for (gap = n/2; gap > 0; gap /= 2)

{

for (i = gap; i < n; i++)

{

for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) {

temp = v[j];

v[j] = v[j+gap];

v[j+gap] = temp;

     }

}

}

}

There are three nested loops. The outermost controls the gap between compared elements, shrinking it from n/2 by a factor of two each pass until it becomes zero. The middle loop steps along the elements. The innermost loop compares each pair of elements that is separated by gap and reverses any that are out of order. Since gap is eventually reduced to one, all elements are eventually ordered correctly. Notice how the generality of the for makes the outer loop fit in the same form as the others, even though it is not an arithmetic progression.

该函数中包含一个三重嵌套的for循环语句。最外层的for语句控制两个被比较元素之间的距离,从n/2 开始,逐步进行对折,直到距离为0。中间层的for 循环语句用于在元素间移动位置。最内层的for语句用于比较各对相距gap个位置的元素,当这两个元素逆序时把它们互换过来。由于gap的值最终要递减到1,因此所有元素最终都会位于正确的排序位置上。注意,即使最外层for 循环的控制变量不是算术级数,for 语句的书写形式仍然没有变,这说明for语句具有很强的通用性。


One final C operator is the comma ``,'', which most often finds use in the for statement. A pair of expressions separated by a comma is evaluated left to right, and the type and value of the result are the type and value of the right operand. Thus in a for statement, it is possible to place multiple expressions in the various parts, for example to process two indices in parallel. This is illustrated in the function reverse(s), which reverses the string s in place.

逗号运算符“,”也是C 语言优先级最低的运算符,在for 语句中经常会用到它。被逗号分隔的一对表达式将按照从左到右的顺序进行求值,表达式右边的操作数的类型和值即为其结果的类型和值。这样,在for 循环语句中,可以将多个表达式放在各个语句成分中,比如同时处理两个循环控制变晕。我们可以通过下面的函数reverse(s)来举例。该函数用于倒置字符串s中各个字符的位置。


#include <string.h>

/* reverse: reverse string s in place */

void reverse(char s[])

{

int c, i, j;

for (i = 0, j = strlen(s)-1; i < j; i++, j--) {

c = s[i];

s[i] = s[j];

s[j] = c;

}

}

The commas that separate function arguments, variables in declarations, etc., are not comma operators, and do not guarantee left to right evaluation.

某些情况下的逗号并不是逗号运算符,比如分隔函数参数的逗号,分隔声明中变量的逗号等,这些逗号并不保证各表达式按从左至右的顺序求值。


Comma operators should be used sparingly. The most suitable uses are for constructs strongly related to each other, as in the for loop in reverse, and in macros where a multistep computation has to be a single expression. A comma expression might also be appropriate for the exchange of elements in reverse, where the exchange can be thought of a single operation:

应该慎用逗号运算符。逗号运算符最适用于关系紧密的结构中,比如上面的reverse数内的for 语句,对于需要在单个表达式中进行多步计算的宏来说也很适合。逗号表达式还适用于reverse函数中元素的交换,这样,元素的交换过程便可以看成是一个单步操作。


for (i = 0, j = strlen(s)-1; i < j; i++, j--)

c = s[i], s[i] = s[j], s[j] = c;



robust  adj. 强健的;健康的;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值