循环语句
在 C 语言中,每个循环语句都有一个控制表达式。每次执行循环体时,都要对控制表达式求值。如果表达式为真,那么继续执行循环语句;否则执行循环语句的下一条语句。
C 语言提供了 3 种循环语句,即 while
语句, do...while
语句和 for
语句。
while 语句在循环体执行之前测试控制表达式, do...while
循环在循环体执行之后测试控制表达式, for
语句则非常适合那些递增或递减计数变量的循环。
while语句
在所有循环语句中, while
语句是最简单也是最基本的。
while
语句的格式如下:
while (expr) statment
其中圆括号内的表达式称为控制表达式,圆括号后面的语句是循环体。下面是一个示例:
i = 1;
while (i < n)
i = i * 2;
虽然循环体必须是单独的一条语句,但这只是个技术问题:如果需要多条语句,那么只要用一对花括号构造一条复合语句就可以了:
i = 10;
while (i > 0) {
printf("Counting down: %d\n", i);
--i;
}
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 20;
while (a > 10) {
scanf("%d", &a);
printf("a = %d\n", a);
}
return 0;
}
程序输出:
20↙️
a = 20
30↙️
a = 30
10↙️
a = 10
无限循环
如果控制表达式的值始终非零,那么 while
语句将永远执行下去。事实上,有时候我们会故意用非零的常量表达式作为控制表达式,以此来构造无限循环。
// idiom
while (1) ...
除非循环体内含有跳出循环的控制语句 (break
, goto
, return
) 或者调用了导致程序终止的函数,否则上述形式的 while
语句将永远执行下去。
do…while语句
do...while
语句和 while
语句关系紧密。事实上, do...while
语句本质上就是 while
语句,只不过其控制表达式是在每次执行完循环体之后进行判定的。
do...while
语句的格式如下:
do statment while (expr);
执行 do...while
语句时,先执行循环体,再计算控制表达式的值。如果表达式的值非零,那么继续执行循环体,然后再计算表达式的值;如果表达式的值为零,则终止 do...while
语句的执行。如:
i = 10;
do {
printf("Counting down: %d\n", i);
--i;
} while (i > 0);
TIP
do...while
语句和 while
语句的唯一区别是:do...while
语句的循环体至少会执行一次,而 while
语句在控制表达式的值初始为 0 时,一次都不会执行。
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 1;
do {
a++;
printf("a = %d\n", a);
} while (a < 10);
return 0;
}
程序输出:
a = 2
a = 3
a = 4
a = 5
a = 6
a = 7
a = 8
a = 9
a = 10
小练习
编写一个程序计算一个整数的位数。
TODO
for语句
现在介绍 C 语言中最后一种循环,也是功能最强大的一种循环: for
语句。for
语句非常适合那些递增或递减计数变量的循环,当然它也可以灵活地应用在许多其他类型的循环中。for
语句的格式如下:
for (expr1; expr2; expr3) statement
下面是一个例子:
for(i = 10; i > 0; i--)
printf("Counting down: %d\n"; i);
执行 for
语句时,变量 i
先初始化为 10,接着判定 i
是否大于 0。因为 10 > 0,因此 打印 Counting down: 10
,然后变量 i
自减。随后再次对条件 i > 0
进行判定...
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int i;
int sum = 0;
for (i = 0; i <= 100; i++) {
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
程序输出:
sum = 5050
从上面的例子,我们可以看到 for
语句和 while
语句关系非常紧密。事实上,除了一些极少数的情况以外(你能举出例子吗?), for
循环总可以用等价的 while
循环替换:
expr1;
while (expr2) {
statement
expr3;
}
从等价的 while
循环可以看出,expr1
是循环开始执行前的初始化步骤,只执行一次;expr2
是控制循环终止的;expr3
是每次循环中最后被执行的一个操作。
省略for语句中的表达式
for
语句远比现在看到的更加灵活,C 语言允许 for
语句省略一些或者是全部的表达式。
如果省略 expr1
,那么在执行循环前没有初始化的操作:
i = 10;
for (; i > 0; --i)
printf("Counting down: %d\n", i);
如果省略 expr3
,那么循环体需要确保 expr2
的值最终会变成 0:
for (i = 10; i > 0; )
printf("Counting down: %d\n", i--);
当同时省略 expr1
和 expr3
时,那么 for
语句和 while
语句就没有任何分别,如:
i = 10;
for (; i > 0; )
printf("Counting down: %d\n", --i);
等价于
i = 10;
while (i > 0)
printf("Counting down: %d\n", --i);
如果省略 expr2
,那么它的默认值为真。例如,某些程序员利用下列的 for
语句建立无限循环:
// idiom
for ( ; ; ) ...
C99中的for语句
在 C99 中,for
语句的第一个表达式可以替换为一个声明,这一特性使得程序员可以声明一个用于循环的变量:
for (int i = 0; i < n; ++i)
...
变量 i
不需要在该语句前进行声明。如果变量 i
在之前已经进行了声明,这个语句将创建一个新的 i
,且该变量只能在循环内使用。
for
语句声明的变量在循环外是不可见的:
for (int i = 0; i < n; ++i) {
...
printf("%d", i); /* legal */
...
}
printf("%d", i); /* Wrong */
TIP
让 for
语句自己声明循环控制变量通常是一个好办法:这样很方便,而且程序的可读性更强。
顺便提一下,for
语句可以声明多个变量,只要它们的类型相同:
for (int i = 0, j = 0; i < n; i++)
...
逗号表达式
有时候,我们可能需要编写有两个 (或更多个) 初始表达式的 for
语句,或者希望在每次循环时对几个变量进行自增操作。这时我们就需要使用逗号表达式了。
逗号表达式的格式如下:
expr1, expr2
逗号表达式的求值分为两步:第一步,计算 expr1
并且扔掉计算出的值;第二步,计 算 expr2
,把这个值作为整个表达式的值。对 expr1
的计算应该产生一些副作用,否 则 expr1
就没有存在的必要了。举个例子:
i = 1;
j = 5;
k = (++i, i + j);
逗号表达式是左结合的,因此编译器会把表达式
i = 1, j = 2, k = i + j
解释为
(i = 1, j = 2), k = i + j
C/C++ 之所以提供逗号运算符,是为了在只能有一个表达式的地方可以使用两个甚至是多个表达式。换句话说,逗号运算符允许将两个表达式"粘"在一起,构成一个表达式 (和复合语句类似,复合语句可以把一组语句当成一条语句来使用)。
例如,我们可以把原来的程序
sum = 0;
for (i = 1; i <= N; i++)
sum += i;
改写成这样:
for (sum = 0, i = 1; i <= N; i++)
sum += i;
嵌套循环
循环语句之间可以相互嵌套:
#include <stdio.h>
#include <stdlib.h>
int main() {
int num = 0;
int i, j, k;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
for (k = 0; k < 10; k++) {
printf("hello world\n");
num++;
}
}
}
printf("num = %d\n", num);
return 0;
}
程序输出:
hello world
hello world
...
hello world
num = 1000