我们知道每个程序都有它的一套流程结构,而一个经典的程序更是离不开流程控制。这节我来讲解流程控制的各种结构。
结构按一定的顺序执行的叫顺序结构如下所示,直到程序执行完毕退出。
,
语句1 |
语句2 |
语句3 |
... |
return 0语句 |
选择结构
(1)if ~ else 条件语句
(2)switch 多分支选择开关语句
(3)for 循环语句
(4)while 循环语句
(5)do ~ while 循环语句
(6)continue 结束执行循环中下面的语句,判断是否从头循环
(7)break 终止执行循环或语句
(8)goto 转向语句
(9)return 函数返回语句
if 语句用于实现条件分支结构,它在可选动作中作出选择,执行某个分支的程序段。if 语句有两种格式在使用中供选择。
if格式
if(表达式)
语句
图3. 2 单分支结构流程图
如果表达式的值为真(非零值),则执行if 语句中的语句;否则当表达式的值为假(零值)时将执行整个if 语句下面的其它语句。if 语句中的语句可以是一条语句,也可以是复合语句。
在流程图中,我们用菱形框(也称为判断框)来表示对表达式的求值及判断,它有两路出口分别对应菱形框中的表达式的值为真和假的两种情形。
[例3.2] 求输入整数的绝对值。
main()
{
int i;
printf("请输入一个整数:");
scanf("%d",&i);
if(i<0)
i=-i;
printf("绝对值为:%d ",i);
}
运行结果:
-36 /* 键盘输入 */
绝对值为:36
在上例中,当i为负数时才需要将它变号为正数,否则将不作任何处理,可见该语句实现的功能是在条件为真时执行某个动作,在条件为假时跳过这个动作。这种结构我们也称之为“单路选择结构”。
if ~ else 格式 流程图
if(表达式)
语句1
else
语句2 图3. 3 双路选择结构流程图
如果表达式的值为真,则执行语句体1,否则执行语句体2。然后继续执行整个if语句后边的语句。语句体1和语句体2可以是一条语句,也可以是复合语句。
[例3.3] 输入一门课程的成绩,判断是否及格。
main()
{
float x;
printf("请输入成绩:");
scanf("%f", &x);
if(x>=60)
printf("及格! ");
else
printf("不及格! ");
}
运行结果:
56 /* 键盘输入 */
不及格!
在上例中,对于输入的成绩x的处理有两种情况:当x大于等于60时将输出“及格!”,否则将输出“不及格!”。可见该语句实现的功能是在条件为真时执行某个动作,条件为假时执行另外的一个动作。这种结构我们也称之为“双路选择结构”。
if 语句可以嵌套,即在以上两种格式的if语句中的语句都可以是或者包含if语句。
[例3.4] 比较两个输入数的大小。
main()
{
int a,b;
printf("请输入两个整数:");
scanf("%d%d",&a,&b);
if(a!=b)
if(a>b)
printf("a>b ");
else
printf("a<b ");
else
printf("a=b ");
}
运行结果:
99 123 /* 键盘输入 */
a<b
此上中比较两个整数a、b的大小,有三种情形:a>b,a<b和a==b。若使用双路选择结构的if ~ else语句来实现就需要嵌套:① a!=b与a==b的双路选择;② 当a!=b时进行a>b和a<b的双路选择。
注意在if语句中常用到比较两个数是否相等的表达式如a==b,这应该是关系表达式而不是赋值表达式a=b,虽然写成a=b的程序不会产生语法错误,但显然程序在一般情况下不能产生正确的结果,这是初学者常犯的一个错误。
在嵌套if语句中,为了增强程序的可读性,通常把同层的if和else对齐书写,但特别需要注意的是并不是书写对齐了的if和else配对为一条if语句,C语言规定else总是与它上面的最近的if配对。当else需要与距它较远的if相配对时就得使用复合语句。
例如下面两条if语句:
if(x>0) if(x>0)
if(a>b) {
printf("a>b! "); if(a>b) printf("a>b! ");
else }
printf("a<=b!! "); else
printf("x<=0!! ");
if语句的嵌套的形式在程序设计中经常使用,它可以用来实现多路选择结构,例如:
if(表达式1)
语句1
else if(表达式2)
语句2
else if(表达式3)
语句3
.
.
.
else if(表达式n)
语句 n
else
语句 n+1
图3. 4 多路选择结构
上程序段执行过程中,从表达式1开始依次计算,当遇到某个表达式k的值为真,则执行相应的语句k ,然后执行整个嵌套if语句后面的语句。如果所有的表达式的值都为假,则只执行最后一个else后面的语句N+1。最后的else及语句N+1可以没有,此时若所有的表达式的值均为假的话,该if语句将不执行语句中的任何语句。语句1,…,语句N+1均可以是单个语句或是复合语句。
[例3.5] 判断从键盘输入字符的种类。将字符分为五类:
① 控制字符 (这类字符的ASCII码小于32)
② 数字字符
③ 大写字母字符
④ 小写字母字符
⑤ 其它字符
#include "stdio.h"
main()
{
char c;
cin>>"请输入一个字符:";
c=getche();
if(c<32)
cout<<":控制字符 ";
else if(c>='0' && c<='9')
cout<<":数字字符 ";
else if(c>='A' && c<='Z')
cout<<":大写字母字符 ";
else if(c>='a' && c<='z')
cout<<":小写字母字符 ";
else
cout<<":其它字符 ";
}
运行结果:
8 /* 键盘键入*/
:数字字符
I /* 键盘键入*/
:大写字母字符
上例中if语句含有五个语句,用于多路分支处理,对于适用于条件判断的多种平行情况的分支处理常使用if语句的这种形式。[例3.4]可以很方便地用这种形式改写。
对于多路分支处理还有一种情况,它对一个整型表达式的值进行计算,针对该表达式的不同取值决定执行多路分支程序段中的一支,这就是switch结构。
3.3.2 switch 结构
switch结构也称为“多路选择结构”,它在许多不同的语句组之间作出选择。switch语句用于实现该结构,它常与break语句联合使用,break语句用于转换程序的流程,在switch语句中使用break语句可以使程序立即退出该结构,转而执行该结构后面的第一条语句。
switch结构的一般格式如下:
switch(整型表达式)
{
case 整型常量表达式1:
语句组1
[break;]
case 整型常量表达式2:
语句组2
[break;]
。
。
。
case 整型常量表达式N:
语句组 N
[break;]
default :
语句组N+1
}
图3. 5 switch 结构流程图
在switch语句中,各语句组均可以由若干条语句组成。执行switch语句时,首先计算括号内的整型表达式的值,然后将其结果值按前后次序依次与各个case后面的整型常量表达式进行比较。当与整型常量表达式k的值一致时就执行该case后边的语句组k,如果遇到break语句时就退出switch语句,继续执行该switch语句后面的语句;如果遇不到break语句,则一直顺序往下执行下面其它case后边的语句,直到遇到break语句或最后退出switch语句为止。如果整型表达式的值与所有的case后边的整型常量表达式的值都不相等时,则执行default后边的语句组N+1;若无default项时(default项可缺省),则直接退出switch语句。
整型常量表达式可以是字符型常量,我们知道在计算机中字符型量是以ASCII码值表示的,它是一个整型值。同样在switch后边的整型表达式也可以是字符型变量组成的表达式,只要该表达式有一个整型值。
在switch语句中,语句组1,…,语句组N可以是一条语句也可以是多条语句,甚至可以没有语句。在使用switch语句实现多路选择结构时,通常需要适当地使用break语句来调整程序的流程。在某个case项的语句组中若需要break语句的话,break语句一般都放在该语句组的最后作为该语句组的最后一条语句,在上面switch语句的格式当中,为了清楚起见,我们标出了break语句,实际上它应该是相应语句组的一部分。
C语言规定,case项和default项在switch语句中能以任何顺序出现,但是把default项放在最后是一种良好的程序设计习惯。
在上面的流程图中,判断框“case k”(k = 1,2,…,N)表示整型表达式是否等于整型常量表达式的逻辑判断,它有真和假两个出口。判断框“break?”表示对在相应语句组中是否有break语句的两种情形的分支处理,若有该语句,则直接退出switch结构去执行switch语句后面的语句;否则程序将执行下一个语句组,进入到下一个语句组的流程。
[例3.6] 根据输入的字符A、B、C、D、F输出成绩“优”、“良”、“中”及“及格”
和“不及格”。当输入为F或R(表示缺考)时输出补考通知。(输入字符不区分大小写)
#include "stdio.h"
main()
{
char grade;
printf("请输入成绩(用字母A、B、C、D、F表示):");
grade=getchar();
switch(grade)
{
case ‘A‘: case ‘a‘:
cout<<"优! ";
break;
case ‘B‘: case ‘b‘:
cout<<"良! ";
break;
case ‘C‘: case ‘c‘:
printf("中! ");
break ;
case ‘D‘: case ‘d‘:
cout<<"及格! ";
break;
case ‘F‘: case ‘f‘:
cout<<"不及格! ";
case ‘R‘: case ‘r‘:
cout<<"请补考! ";
break;
default:
cout<<"成绩输入错误! ";
}
}
运行结果:
B /* 键盘键入 */
良!
f /* 键盘键入 */
不及格!
请补考!
在上例中,switch语句根据输入的字母是A、B、C、D、F、R或是其它字符进行七路分支分别进行处理。由题意不区分大小写字母,程序对case项进行了分组,程序中所有大写字母项后面的语句组为空,根据switch语句的流程,它与后面的小写字母项共享一个语句组,因而具有相同的动作。注意到在case ‘F’及case ‘f’项的语句组中没有break语句,因而该语句组执行完后将继续执行其后的case ‘R’和case ‘r’项的语句组。这样当输入字母为F或f时,将输出两条信息:“不及格!”和“请补考!”。当输入字符在大小写英文字母A、B、C、D、F、R之外时程序执行default项的语句组,将输出信息“成绩输入错误!”。
switch结构用于多路分支选择,他像多路开关一样,程序根据给定的整型表达式选择某一路程序段执行。因此我们也称它为开关分支结构。相应switch语句也称为开关语句。
switch语句也可以嵌套,即在语句中各case项的语句组可以包含另外的switch语句。此时switch结构中的break语句将只会使程序退出该语句所在那一层switch结构,并不会影响其它的switch结构的程序流程:
[例3.7] 嵌套switch语句程序示例。
main()
{
int a=0,b=1;
switch(a)
{
case 0: cout<<"a=0 ";
switch(b)
{
case 0: cout<<("b=0 "); break;
case 1: cout<<("b=1 "); break;
case 2: cout<<("b=2 ");
}
case 1: cout<<"a=1 ";
default: cout<<" ";
}
}
运行结果:
a=0 b=1 a=1
3.4 循环结构
循环结构是程序中的另一种重要结构,它和顺序结构、选择结构共同作为各种复杂程序的基本构造部件。循环结构的特点是在给定条件成立时,反复执行某个程序段。通常我们称给定条件为循环条件,称反复执行的程序段为循环体。循环体可以是复合语句、单个语句或空语句。在循环体中也可以包含循环语句,实现循环的嵌套。
根据判定循环条件和执行循环体的先后次序,循环结构可以分为以下两种形式:
·当型循环: 首先判定循环条件,为真时将执行循环体,进行循环;否则结束循环。
直到型循环:首先执行循环体,再判定循环条件,为真时继续循环;否则结束循环。
3.4.1 当型循环(前判定循环)
用于实现当型循环的C语句有for语句和while语句,它们都能实现结构化程序设计中的循环结构,但也各有特点,使用的场合有所不同。
1. for语句
for语句的一般格式:
for(表达式1;表达式2;表达式3)
循环体
图3.6 for当型循环流程图
在for语句中,表达式1、2、3可以都有,也可以都没有,还可以有其中某一个或某两个,但for语句中的两个分号(;)必不可少。循环体可以是一条语句或空语句,也可以是复合语句。
for语句的执行过程如下:
(1) 首先计算表达式1
(2) 计算表达式2,以决定是否执行循环体:若表达式2的值为真(非零),则执行(3);否则跳到第(5)步,退出循环
(3) 执行循环体
(4) 计算表达式3,并转向执行(2)
(5) 退出循环,继续执行循环体下面的语句
[例3.8] 求前100个自然数之和。
main( )
{
int i,sum;
sum=0;
for(i=1;i<=100;i++) sum += i;
cout<<"和为"<<sum;
}
运行结果:
5050
上例中,变量i在for循环中起到了决定什么时候退出循环,控制循环次数的作用,因此我们可以称它为循环控制变量,它出现在for语句的三个表达式当中。在这里三个表达式分别起到了不同的作用:表达式1用于循环之前进行循环初始化,特别是对循环控制变量赋初值;表达式2用于表明循环的条件,即在什么条件下进行循环;表达式3用于在执行循环体一次后的某些处理,如对循环控制变量进行增量计算。这是for语句最典型的用法,因此我们常把for语句写成如下形式:
for(初始化;条件;增量) 循环体
在这种形式下,for语句实现了自动计数器的功能。通常循环次数是确定的,它由循环控制变量的初值、增量以及循环条件给定,此时我们也称之为“定数循环”。
在C语言中,for语句的功能十分强大且非常灵活,它的表达式1、2、3均可以省略,也可以是逗号表达式,循环体也可以是空语句。因而[例3.7]程序中的for语句也就可以有如下几种形式:
(1) 省略表达式1:
i=1;
for(;i<=100;i++) sum += i;
(2) 省略表达式2:
for(i=1;;i++)
{
if(i>100) break;
sum += i;
}
(3) 省略表达式3:
for(i=1;i<=100;)
{
sum += i;
i++;
}
(4)省略循环体(即循环体为空语句),往往此时需要循环的处理在表达式3中完成:
for(i=1;i<=100;sum += i,i++);
对于典型的定数循环建议使用“for(初始化;条件;增量)循环体”的形式,很显然语句:
for(i=1;i<=100;i++) sum += i;
要比语句:
for(i=1;;)
if(i>100) break;
else
sum += i++;
清晰得多,要做什么和怎么做一目了然。随着程序复杂性的增加,不规范的程序设计风格将大大降低程序的可读性,并且很容易出错,如语句:
for(i=1;i<=100;i++,sum += i);
看上去与上面的第四种情况(省略循环体)差不多,但程序运行的结果却是不一样的。
根据循环的需要,循环变量的变化可以有多种形式,例如:
(1) 控制变量从100变化到1,增量为-1,循环了100次:
for(i=100;i>=1;i--)
(2) 控制变量从3变化到33,增量为3,循环了11次:
for(i=3;i<=33;i += 3)
(3) 控制变量从33变化到3,增量为-3,循环11次:
for(i=33;i>=3;i -= 3)
(4) 控制变量i从1开始变化,增量为j,循环时j从0开始变化,循环次数难以确定:
for(i=1,j=0;i<=k;i += j,j=i-j)
第(4)种形式可以用来打印Fibonacci数列,Fibonacci数列由下列递推公式给出:
fibonacci(1)=1
fibonacci(2)=1
fibonacci(n)=fibonacci(n-1)+fibonacci(n-2)
下面循环语句将顺序输出不大于k的Fibonacci数列:
for(i=1,j=0;i<=k;i += j,j=i-j)
printf(" %d ",i);
分析这条for语句不难看出在第一次循环时输出fibonacci(1),而在以后的各次循环中,i的值为fibonacci(n)而j的值为fibonacci(n-1),n=2,3,…。循环过程即为Fibonacci数列的递推求解过程。
2. while 语句
图3.7 while当型循环流程图
while循环语句的格式
while(表达式)
循环体
#include <stido.h>
using namespace std;
main()
{
int counter=0;
while(getchar()!=' ')
counter++;
cout<<"字符数位"<<counter;
}
程序运行:
5gf5er5793d456ggf123c4v56e9f /* 键盘键入 */
字符数为:28
在上例中,通过getchar()函数从键盘读入一个字符,判断这个字符是否为回车符,如果不是,则进行计数工作;否则结束循环。这种循环与“定数循环”不同,程序事先并不知道循环会执行多少次,而是在程序运行过程中确定什么时候结束循环,相应地我们可以称这种循环为“不定数循环”,这种循环通常都是通过在每次循环中获取或产生的数据与一个标记值(此例中为回车符’ ’)进行比较判断,以决定是否继续循环。
循环体可以是空语句,我们来看下面的例子:
[例3.10] 求键盘输入的一个大于9的整数的最高位数字。
#include "stdio.h"
main()
{
int d;
printf("请输入一个整数(>9):");
scanf("%d",&d);
while((d /= 10)>9);
printf("最高位数字为:%d ",d);
}
程序运行:
5678 /* 键盘键入 */
最高位数字为:5
上例中,while语句的循环体为空语句,此时需要循环的工作放在循环条件表达式中完成。该while语句与下面的while语句是等价的:
while(d>9) d /= 10;
事实上,可以用while语句来实现for语句的循环结构:
表达式1;
while(表达式2)
for(表达式1;表达式2;表达式3) {
循环体 循环体
表达式3;
}
也可以使用for语句替代while语句:
while(表达式) for(;表达式;)
循环体 循环体
只是在程序设计当中我们更多地使用for语句来实现定数循环而使用while语句来实现不定数循环,这样写的程序更清晰易懂。
3.4.2 直到型循环(后判定循环)
在当型循环中,首先判定循环的条件,若循环的条件不满足,循环体将一次也不执行。直到型循环与当型循环不同,它首先执行一次循环体,然后判定循环条件。在C语言中,直到型循环是由do ~ while语句来实现的。
do ~ while语句的格式如下:
do
循环体
while(表达式);
图3.8直到型循环流程图
do ~ while循环的执行过程是先执行一次循环体,然后判断表达式的值,如果是真(非零),则再执行循环体,继续循环;否则退出循环,执行下面的语句。循环体可以是单条语句或是复合语句,在语法上它也可以是空语句,但此时循环没有什么实在的意义。
为了避免与当型循环的“while(表达式);”(循环体是空语句)的形式混淆,不论循环体是否复合语句,do ~ while语句一般都书写成下面的形式,增加程序的清晰性和可读性。
do
{
循环体
}while(表达式);
[例3.11] 求整数i,它满足条件:1+2+…+(i-1)<100且1+2+…+i≥100。
main()
{
int i=0,sum=0;
do
{
sum += ++i;
}while(sum<100);
printf("这个整数是:%d ",i);
}
运行结果:
这个整数是:14
上例中,首先执行一遍循环体得到一个和数sum,再判断循环条件表达式sum<100,若其值为真则继续循环;否则退出循环。通过此例我们不难发现直到型循环通常应用于这样两种情形:① 事先我们知道初始时循环条件一定为真,循环体肯定会被执行一遍(此例中sum的初值为0,sum<100一定为真);② 循环条件在循环体被执行一遍后产生,即事先我们还不能方便地判定循环条件。请看下面例子:
[例3.12] 从键盘逐次输入一组整数对,遇到整数对中前一个数比后一个数小时,输出这是第几个整数对,结束程序。
main()
{
int counter=0,d1,d2;
printf("请输入整数对:");
do
{
scanf("%d%d",&d1,&d2);
counter++;
}while(d1>=d2);
printf("第%d个整数对! ",counter);
}
运行结果:
请输入整数对:86 78
123 96
4 1
15 20
第4个整数对!
上例中,数据d1和d2是在循环体中输入的,因此循环条件d1 >= d2的判定必须在执行一次循环后进行,这时我们用直到型循环来实现它是很方便的。
直到型循环与当型循环并没有绝对的界限,事实上我们可以用当型的while语句来替代直到型的do ~ while语句:
do 循环体(语句)
循环体 while(表达式)
while(表达式); 循环体
同样我们也可以用do ~ while语句结合if语句来实现当型循环的while语句:
if(表达式)
while(表达式) do
循环体 循环体
while(表达式);
针对不同的应用场合,根据程序实现的方便程度我们可以选择不同的循环结构以及不同的循环语句,甚至多种循环语句混合及嵌套使用,形成所谓的多重循环,结合前面介绍的顺序结构和选择结构,可以写出功能非常复杂的应用程序来。下面是一个多重循环的例子:
[例3.13] 从键盘循环输入数字n(0~9),若输入的n满足:0<n<10则屏幕输出一个n×n的该数字构成的方阵;若输入为数字0则结束程序。
main()
{
int n,i,j;
do
{
cout<<"请输入一个数字(0~9):";
cin>>n;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
cout<<n;
cout<<endl;
}
}while(n);
}
运行结果:
请输入一个数字(0~9):3
333
333
333
在上例中,do ~ while语句实现程序的第一重循环,在它的循环体中完成从键盘读入数字n,并输出n×n的数字n方阵的工作。输出方阵的工作由两条for语句构成的二重循环完成:内层的for语句实现方阵的列控制,它在方阵的每一行的n列个位置输出数字n;外层的for语句完成方阵的行控制,它保证输出n行,并在内层的for语句打印完一行后加上一个回车符。注意当输入的n为0时,当型循环的for语句并不执行,因为此时外层for语句的表达式2(i<n)的值为假。
另外在此例中,do ~ while语句中的表达式为n,一般在if结构及循环结构中的条件表达式多是关系与逻辑的混合表达式,其值为1或0代表真和假两个逻辑值,我们也可以用非零代表真,因此while(n!=0)与 while(n)是等价的,同样while(n==0)可以写成while(!n)这种简洁的形式。
3.4.3 break语句与continue语句
break语句和continue语句都是用来控制程序的流程转向的。适当地和灵活地使用它们可以更方便或更简洁地进行程序的设计。
1. break语句
在while、for、do ~ while或switch语句结构中循环体或语句组中使用break语句可以使程序立即退出该结构,转而执行该结构下面的第一条语句。break语句也称之为中断语句,它通常用来在适当的时候退出某个循环,或终止某个case并跳出switch结构。
[例3.14] 从键盘输入字符、数字串,统计其中数字的个数,若遇到字符串“bye”时输出统计结果,结束程序。
#include "stdio.h"
main()
{
int num=0;
char c;
while(1)
{
if((c=getche())=='b')
if((c=getche())=='y')
if((c=getche())=='e')
break;
if(c>='0' && c<='9')num++;
}
cout<<"数字个数为"<<num;
}
运行结果:
gf56694ewq2 87yr,jpaw2134598hfbye /* 键盘键入 */
数字个数为:15
在上例中,循环(结束)条件不能方便地用一个表达式表达出来,此时设计一个“无限循环”结构while(1)形式(或for(;;)等形式),在循环体中使用if语句构造循环结束条件,配合break语句来完成这个循环结构的出口。
对于循环结构中有多个出口的情形,也常使用break语句。
在一个switch结构中的break语句只会影响该switch结构的程序流程,而不会影响这条switch语句所在的任何循环,常见的形式如下:
while(1)
{
…
if(表达式1) break;
…
if(表达式2) break;
…
…
if(表达式N) break;
…
}
在这样的形式下循环可以有N个出口,它们分别在循环体中的不同位置书写,这样可以避免过大的if语句以及太深的if语句嵌套。
[例3.15] 对键盘输入的两个整数的加、减、乘、除算式进行计算,直到用户要求退出。
#include "stdio.h"
main()
{
int a,b;
char c;
do
{
cout<<" 请输入两个整数的一个算式: ";
cin>>a>>b>>c;
switch(c)
{
case '+': cout<<a+b; break;
case '-': cout<<a-b; break;
case '*': cout<<a*b; break;
case '/': if(b!=0)
{ cout<<a/b; break; }
default: cout<<"错误的算式! ";
}
printf("退出程序吗?(n或N):");
if(getche()=='n' || c=='N') break;
}while(1);
}
运行结果:
请输入两个整数的一个算式:34+56
=90
退出程序吗?(n或N):y
请输入两个整数的一个算式:3/0
错误的算式!
退出程序吗?(n或N):n
上例中,switch语句中的break语句将仅退出switch结构,循环的出口是由循环体中最后一条if语句中的break语句来实现。
在进行循环结构的程序设计时,应注意循环出口的检查,保证循环能在有限步内结束,否则程序将进入“死循环”,即在运行时程序无休止地执行循环体。循环的出口可由循环条件表达式为假来完成,此时需要确认当循环了有限步后该表达式的值为假;也可以在循环体中由if语句及break语句来构造,此时需要确认当循环了有限步后程序将到达break语句。
2. continue语句
在while和do ~ while语句的循环体中,执行continue语句将结束本次循环而立即测试循环的条件,以决定是否进行下一次循环。
[例3.16] 计算输入的10个整数中正数的个数及平均值。
main()
{
int i,n,a;
float s;
cout<<"请输入10个整数: ";
for(n=0,i=0;i<10;i++)
{
cin>>a;
if(a <= 0)continue;
s += a;
n++;
}
cout<<"共有"<<n<<"个正数,其平均值为。 "<<s/n;
}
运行结果:
请输入10个整数:-1 2 3 –34 1 5 –6 4 0 -88
共有5个正数,其平均值为3.000000。
上例中对于输入的整数a进行分支处理:当a>0时进行计数和求和的工作;当a<=0时则跳过这些处理,继续下一次循环。当然continue语句并不是必须的,将该例中for语句中的循环体改为:
cin>>a;
if(a>0)
{
s +=a;
n++;
}
一样可以实现上述功能。
若在for(表达式1;表达式2;表达式3)语句的循环体中使用continue语句,那么执行continue语句后程序将计算表达式3,然后测试循环的条件(计算表达式2),以决定是否进行下一轮循环,这样就使得以下两个循环不等价:
for(表达式1;表达式2;表达式3) 表达式1;
{ while(表达式2)
语句组1 {
continue; 语句组1
语句组2 continue;
} 语句组2
表达式3;
}
break和continue语句主要用于循环体内分支比较复杂的情形,用来简化分支语句的条件,可以减少条件分支语句if的嵌套深度及分支数,使程序更易于阅读和理解。
[例3.17] 从键盘输入字符、数字串,统计其中数字的个数(放在变量num中),若遇到字符串“reset”时将变量num置零,重新开始统计,遇到回车符时结束程序。
#include "stdio.h"
main()
{
int num=0;
char c;
for(;;)
{
if((c=getchar())=='r')
if((c=getchar())=='e')
if((c=getchar())=='s')
if((c=getchar())=='e')
if((c=getchar())=='t')
{
num=0;
continue;
}
if(c==' ') break;
if(c>='0' && c<='9') num++;
}
cout<<"数字个数为"<<num;
}
运行结果:
S32rr4098dfreseta94b87c372d615Z /* 键盘输入 */
数字个数为:10
有人认为break和continue语句破坏了结构化程序设计规范,事实上不使用break和continue语句(以及下一节介绍的goto语句)也能实现这些语句的效果,但若break和continue语句使用得当,并不会影响程序的可读性,并且程序的执行速度更快。
在多重循环中break和continue语句只影响该语句所在的最内层循环的程序流程,在下例中break语句只能跳出最内层循环。
[例3.18] 编写一个程序打印3到234之间的所有质数,每行打印10个。
程序的输出结果如下所示:
3 5 7 11 13 17 19 23 29 31
37 41 43 47 53 59 61 67 71 73
79 83 89 97 101 103 107 109 113 127
131 137 139 149 151 157 163 167 173 179
181 191 193 197 199 211 223 227 229 233
main()
{
int i,j,k=0;
for(i=3;i<234;i++)
{
for(j=2;j<i;j++)
if(i%j==0)break;
if(i==j)
printf("%6d%c",i,++k%10==0?' ':' ');
}
printf(' ');
}
此例中有两重循环,外层循环遍历了3至999之间的整数。内层循环遍历了3至i-1之间的整数。内层循环体测试数字i是否为一质数,在这里使用break语句可以在i不是一个质数时(j可以整除i)及早地中断内层循环。程序中变量k用于控制输出质数时每行10个。程序设计时利用更多的数学知识可以大大提高该程序的效率,如已知大于3的质数一定是奇数,因此程序中两条for语句可以改写为:
for(i=3;i<234;i += 2) 和 for(j=3;j<i;j += 2)
这样大约可以减少四分之三的循环次数,这在编写程序时是应该注意的。
3.5 goto语句与标号
goto语句配合语句标号可以实现无条件转向,进而控制程序流向。语句标号由一个有效标识符(标号名)加冒号(:)组成,放在某个语句之前或单独一行。在同一个函数中的语句标号不能重名,同一条语句可以有几个不同的语句标号。语句标号仅对goto语句有意义,执行goto语句后程序将跳转至标号后边的语句去运行。
goto语句的一般格式如下:
goto 标号名;
标号名: 语句
图3.9 goto语句流程图
当程序到达“goto 标号名;”语句时,控制就转到具有该标号名的语句去执行。程序中“goto 标号名;”的位置可以在“标号名: 语句”的前面也可以在其后面,但二者必须在同一个函数体内;在一个函数体内还可以有多个goto语句使用同一个语句标号。goto语句最常见的用法是用来直接退出多重循环,使用break语句则只能退出一层循环。另外goto语句也常用于程序流程转移到某个特定的地方进行特定的处理(如错误处理等)。
[例3.19] 用goto语句构造循环计算1到100的整数和。
main()
{
int i=1,sum=0;
loop: sum += i++;
if(i<=100)goto loop;
printf("1到100的和为:%d ",sum);
}
运行结果:
1到100的和为:5050
上例中,使用goto语句构造的直到型循环结构,运行结果同[例3.8]。
[例3.20] 这是一个智力游戏:公鸡五元一只,母鸡三元一只,小鸡一元三只,花了100元买了100只鸡,问公鸡、母鸡和小鸡各有多少只?
把公鸡、母鸡和小鸡的个数记为变量a、b和c,此题为求不定方程:
① a+b+c=100
② 5a+3b+c/3=100
的一组整数解。
main()
{
int a,b,c;
for(a=0;a<=100/5;a++) /* 遍历公鸡可能的个数 */
for(b=0;b<=100/3;b++) /* 遍历母鸡可能的个数 */
{
c=100-a-b; /* a、b、c满足不定方程① */
if(c%3==0 && 5*a+3*b+c/3==100) /* 测试a、b、c是否满足不定方程② */
goto end;
}
end: printf("公鸡=%d 母鸡=%d 小鸡=%d ",a,b,c);
}
运行结果:
公鸡=0 母鸡=25 小鸡=75
上例中goto语句用于在找到一组解后直接跳出二重循环而到程序的出口。在程序的出口处输出这组结果。
goto语句并不是一种必须的语言成分,不用goto语句一样可以实现使用goto语句的程序功能。不适当地使用goto语句往往会破坏程序的结构化风格,大大降低程序的可读性,通常情况下在程序中应尽量避免使用goto语句。