目录
3.5 while 循环与 for 循环
while 循环语句
while (表达式)
语句
首先求表达式的值,如果其值非 0,则执行语句,并再次求该表达式的值
这一循环过程一直进行下去,直到该表达式的值为 0 为止,随后继续执行语句后面的部分
for 循环语句
for (表达式 1; 表达式 2; 表达式 3)
语句
它等价于下边的 while 语句:
表达式 1;
while (表达式 2) {
语句
表达式 3;
}
但当 while 或 for 循环语句中包含 continue 语句时,上述二者之间就不一定等价了
3.7 节介绍 continue 语句
从语法角度看,for 循环语句的 3 个组成部分都是表达式
最常见的情况是,表达式 1 与表达式 3 是赋值表达式或函数调用,表达式 2 是关系表达式
这 3 个组成部分中的任何部分都可以省略,但分号必须保留
如果在 for 语句中省略表达式 1 与表达式 3,它就退化成了 while 循环语句
如果省略测试条件,即表达式 2,则认为其值永远是真值
因此,下列 for 循环语句是一个 “ 无限 ” 循环语句:
for (;;) {
...
}
这种语句需要借助其它手段(如 break 语句或 return 语句)才能跳出循环
在设计程序时到底选用 while 循环语句还是 for 循环语句,主要取决于程序设计人员的个人偏好
例如,在下列语句中:
while ((c = getchar()) == ' ' || c == 'n' || c = 't')
;
因为其中没有初始化或重新初始化的操作,所以使用 whi1e 循环语句更自然一些
如果语句中需要执行简单的初始化和变量递增,使用 for 语句更合适一些
它将循环控制语句集中放在循环的开头,结构更紧凑、更清晰
通过下列语句可以看出这一点:
for (i = 0; i < n; i++)
...
这是 C 语言处理数组前 n 个元素的一种习惯性用法,它类似于 Fortran 语言中的 DO 循环或 Pascal 语言中的 for 循环
但是,这种类比并不完全准确,因为在 C 语言中 for 循环语句的循环变量和上限在循环体内可以修改
并且当循环因某种原因终止后循环变量 i 的值仍然保留
因为 for 语句的各组成部分可以是任何表达式,所以 for 语句并不限于通过等差数列进行循环控制
尽管如此,牵强地把一些无关的计算放到 for 语句的初始化和变量递增部分是一种不好的程序设计风格,该部分放置循环控制运算更合适
作为一个较大的例子,我们来重新编写将字符串转换为对应数值的函数 atoi
这里编写的函数比第 2 章中的 atoi 函数更通用,它可以处理可选的前导空白符以及一个可选的加(+)或减(-)号
第 4 章将介绍函数 atof,它用于对浮点数执行同样的转换
下面是程序的结构,从中可以看出输入的格式:
如果有空白符的话,则跳过
如果有符号的话,则读取符号
取整数部分,并执行转换
其中的每一步都对输入数据进行相应的处理,并为下一步的执行做好准备。当遇到第一个不 能转换为数字的字符时,整个处理过程终止
#includeint atoi(char s[]) { int i, n, sign; for (i = 0; isspace(s[i]); i++) ; sign = (s[i] == '-') ? -1 : 1; if (s[i] == '+' || s[i] == '-') i++; for (n = 0; isdigit(s[i]); i++) n = 10 * n + (s[i] - '0'); return sign * n; }
标准库中提供了一个更完善的函数 strtol,它将字符串转换为长整型数
有关函数 strtol 的详细信息,请参见附录 B.5 节
把循环控制部分集中在一起,对于多重嵌套循环,优势更为明显
下面的函数是对整型数组进行排序的 Shell 排序算法
Shell 排序算法是 D. L. Shell 于 1959 年发明的
其基本思想是:先比较距离远的元素,而不是像简单交换排序算法那样先比较相邻的元素
这样可以快速减少大量的无序情况,从而减轻后续的工作
被比较的元素之间的距离逐步减少,直到减少为 1,这时排序变成了相邻元素的互换
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;
}
}
该函数中包含一个三重嵌套的 for 循环语句
最外层的 for 语句控制两个被比较元素之间的距离,从 n/2 开始,逐步进行对折,直到距离为 0
中间层的 for 循环语句用于在元素间移动位置
最内层的 for 语句用于比较各对相距 gap 个位置的元素,当这两个元素逆序时把它们互换过来
由于 gap 的值最终要递减到 1,因此所有元素最终都会位于正确的排序位置上
注意,即使最外层 for 循环的控制变量不是等差数列,for 语句的书写形式仍然没有变,这就说明 for 语句具有很强的通用性
逗号运算符 , 也是 C 语言优先级最低的运算符,在 for 语句中经常会用到它
被逗号分隔的一对表达式将按照从左到右的顺序进行求值,表达式右边的操作数的类型和值即为其结果的类型和值
这样,在 for 循环语句中,可以将多个表达式放在各个语句成分中
比如同时处理两个循环控制变晕
我们可以通过下面的函数 reverse(s) 来举例,该函数用于倒置字符串 s 中各个字符的位置
#includevoid 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; } }
某些情况下的逗号并不是逗号运算符,比如分隔函数参数的逗号,分隔声明中变量的逗号等
这些逗号并不保证各表达式按从左至右的顺序求值
应该慎用逗号运算符
逗号运算符最适用于关系紧密的结构中,比如上面的 reverse 函数内的 for 语句
对于需要在单个表达式中进行多步计算的宏来说也很适合
逗号表达式还适用于 reverse 函数中元素的交换,这样,元素的交换过程便可以看成是一个单步操作
for (i = 0, j = strlen(s) - 1; i < j; i++, j--)
c = s[i], s[i] = s[j], s[j] = c;
目录



