循环结构又称重复结构,是程序的 3 种基本结构之一。它反复执行循环体内的代码,解决需要大量重复处理的问题。循环结构由循环控制语句实现,其中内建有条件控制语句,用来判读是否继续执行循环操作。C 语言提供了 while 语句、do-while 语句、for 语句 3 种基本的循环控制语句,并且可以相互嵌套使用。
3.4.1 while 语句
while 语句是“当”型循环控制语句,即在条件满足时执行循环体,否则跳过或跳出循环体。while 语句的一般形式为:
while (条件表达式) { 循环体; }
如下例所示,求阶乘 n! 的结果:
int n = 1, p; // 声明整型变量 n 和 p,n 用于控制步进,p 用于保存操作数 double s = 1; // 声明双精度变量 s, s用于保存计算结果 printf("请输入操作数 (1 - 170):"); // 输出操作提示信息 scanf("%d", &p); // 获取操作数,保存到变量 p 中 while (n <= p) // 当步进值不大于操作数时,执行循环体 { s *= n++; // 将上一次的计算结果 s 与 步长值 n 相乘,并保存新的计算结果到 s,计算完成后步进值 n 自增 } printf(" 阶乘 n = %d 的结果为:%fd ", p, s); // 输出操作结果
上例使用循环控制语句进行阶乘操作,while语句中的表达式用于判断当前阶乘的步进值是否大于输入的操作数。如果不大于则继续进行运算,否则结束循环。
提示:阶乘(factorial)是基斯顿.卡曼(1760 - 1826)于1808年发明的运算符号。阶乘指从 1 乘以 2 乘以 3 乘以 4 一直乘到所要求的数。例如操作数是 4,则阶乘式是 1 x 2 x 3 x4,得到的结果是 24,24就是 4 的阶乘。阶乘的计算会产生相当巨大的结果,在 C 语言的基本数据类型中字长最大的 double 型,也只能保存 170!的运算结果。当然,有很多方法可以保存更大的阶乘结果,有兴趣的可以探索这一问题。
3.4.2 do-while 语句
在 C语言中,“直到” 型循环是 do-while 语句,一般的形式为:
do { 循环体 } while { 条件表达式 }
与 while 语句的区别是,while 是先判读条件表达式再去执行循环体,而 do-whie 语句是先执行循环体再判读条件表达式。也就是说,do-while 语句会首先将循环体执行一次,再判断是否应该结束循环。例如计算 sin(x) 的值算法是“x - x3/3! + x5/5! - x7/7(......)”直到最后一项小于 1e-7 时为止,可用下例代码描述:
#include <stuio.h> #include <math.h> // 加入数学函数库,以提供幂运算函数 fabs() int main() { double s, t, x; // 定义双精度型变量 s、t、x,其中 s 保存计算结果,t 表示下一项的结果,x 表示操作数 int n; // n表示公式中的 幂 printf("请输入 x 的值:"); scanf("%lf", &x); // 获取操作数 t = x; // 使 t 的值等于 x,得到公式中的第一项的值 n = 1; // 初始化幂数 n 为 1 s = x; // 将第一项的结果保存到结果 s 中 do { n += 2; t *= (-x * x) / ((float)(n) - 1) / (float) (n); s += t; } while (fabs(t) >= le-7); printf(" sin(%f) = %lf ", x, s); return 0; }
上例中,使用循环控制语句依次将公式中的每一项加入结果中,从上一项推算当前项的结果只用将上一项乘以因子(x2)/((n-1) * n),即代码第 16 行所示。函数fabs()由头文件 math.h提供,作用是取绝对值。当输入 x 的值为 1.5753时,运算结果为 0.999 990。
3.4.3 for 语句
for 语句使用于可预知执行次数的循环控制结构,是 C 语言中最常用的循环控制语句。for 语句的一般形式为:
for (<表达式1>; <表达式2>; <表达式3>) { 循环体 }
表达式1 用于为控制变量赋初始值,表达式2用于放置循环控制条件的逻辑表达式,表达式3用于改变变量的值。这种结构能明确地展现循环控制结构中的 3 个重要组成部分,即控制变量的初始值、循环条件 和 控制变量 的改变。如计算自然数数列 1 至 n 的平方和,可用下列代码描述:
int i, p; double s = 0; printf("请输入操作数: "); scanf("%d", &p); for (i = 1; i <= p; i++) { s += (double) i * (double) i; } printf("自然数 1 至 %d 平方和为:%lf ", p, s);
for语句的 3个表达式都可以省略,或者放置在 for 语句的参数集合外,但不能省略表达式之间的分号,否则会造成语法错误。
注意:在编写循环结构的程序时,如果循环控制条件不能有效地结束,就会出现死循环,这是程序员经常会碰到的问题。含有死循环的代码能被编译器编译,但运行该程序会造成循环体无休止地被执行,消耗系统资源,严重的会造成系统死机。Linux系统针对死循环有良好的控制机制,不会让一个进程无休止地占据所有CPU资源,从而避免死机现象。当然,死循环在某些情况下存在是合理的,例如硬件驱动程序,通信程序和设备控制程序中,都需要用死循环反复无休止地处理某些问题。while(1) 和 for(;;) 常被生成死循环。
3.4.4 break 与 continue 语句
如果需要在循环体的执行过程中结束某一轮循环,或者直接跳出循环,可以使用 break 或 continue 语句。
break 语句的作用是立即结束当前循环并跳出循环体。下例用一个有趣的计算题说明该语句的使用方法,一只蜗牛顺着葡萄架向上爬,葡萄架高 11 米,蜗牛每天白天可以向上爬 3 米,但晚上会滑下来 1 米,问该蜗牛几天能够爬到葡萄架的顶端,程序代码如下:
int i = 1, s = 0; // 定义变量 i 和 s,表示天数,s 表示已爬上的高度 const int h = 11; // 定义常量 h,h 表示总高 const int up_move = 3; // 定义常量 up_move,up_move 表示每天爬上的高度 const int down_move = 1; // 定义常量 down_move,down_move 表示每天滑下的高度 while (s <= h) // 当 s 不大于 h 时,执行循环体 { s += up_move; // 将已爬上的高度加上当天爬上的高度 if (s >= h) // 判读已爬上的高度是否不小于总高度 { break; // 结束当前循环,并跳出循环体 } s -= down_move; // 将已爬上的高度减去当天滑下的高度 i++; // 天数自增 } printf("蜗牛需要 %d 天爬到葡萄架顶 ", i); // 输出结果
上例的计算结果是 5,每天蜗牛实际向上爬上了 2 米。但是到了第 5 天,距离葡萄架顶只有 3 米,所以蜗牛可以一次爬上去。当前循环在 break 语句后的循环体代码都不会被执行。
continue 语句的作用是结束本轮循环,当前循环的循环体在 continue 后的代码不被执行,但并不跳出循环结构,而是立即开始新一轮的循环。如下例所示:
int n1 = 1, nm = 100; // 定义查找范围 int i, j, flag; // 定义循环控制变量 if (n1 == 1 || n1 == 2) // 处理素数 2 { printf("%4d", 2); // 输出素数 2 n1 = 3; // 将查找范围移动到 2 以后 } for (i = n1; i <= nm; i++) { if (!(i % 2)) { continue; // 如果当前的数能被 2 整除,则结束该轮循环,该数不是素数 } for (flag = 1, j = 3; flag && j < i / 2; j += 2) // 从 3 开始遍历至 i x 1/2,每次步进 2 { if (!(i % j)) { flag = 0; // 若能整除,则不是素数 } } if (flag) { printf("%4d", i); // 如果是素数,则输出该数 } }
上例使用了双层循环结构来解决问题,第一层循环用于遍历定义域内的每个数,第二层循环判读该数是否是素数。continue 和 break 一样,只能影响所处的那层循环。