3.2 流程控制
C语言是一种结构化编程语言,用户可采用结构化方式编写相关源程序。采用结构化设计的程序具有结构清晰、层次分明、易于阅读修改和维护等特点。
结构化程序由若干个模块组成,每个模块中包含若干个基本结构,而每个基本结构中可有若干条语句。在C语言中,有3种基本结构:顺序结构、选择结构和循环结构。
1)顺序结构顺序结构是一种最基本、最简单的编程结构。在这种结构中,程序由低地址向高地址顺序执行指令代码。如图3-2所示,程序要先执行A,然后再执行B,两者是顺序执行的关系。
图3-2 顺序结构
2)选择结构选择结构是先对给定的条件进行判断,再根据判断的结果决定执行哪一个分支。如图3-3所示,图中的P代表一个条件,当P条件成立(或称为“真”)时,执行A,否则执行B。注意,只能执行A或B之一,两条路径汇合在一起,然后从一个出口退出。
图3-3 选择结构
3)循环结构循环结构是指在给定条件成立时,反复执行某段程序。在C语言中,循环结构又分成“当”(while)型循环结构和“直到”(do while)型循环结构。
“while”型循环结构如图3-4(a)所示。当P条件成立(或称为“真”)时,反复执行A操作;直到P为“假”时,才停止循环。
“do while”直到型循环结构如图3-4(b)所示。先执行A操作,再判断P是否为“假”,若P为“假”,再执行A,如此反复,直到P为“真”为止。
图3-4 循环结构
1.条件语句与控制结构
编程解决实际问题时,通常需要先根据某些条件进行判断,然后决定执行哪些语句,这就是条件选择语句。在C语言中提供了3种形式的if条件选择语句和switch多分支选择语句。
1)if语句
(1)if语句的结构形式:if语句是C语言中的一个基本判断语句,它的3种结构形式语句如下所示。
①形式一:
if(表达式) {语句};
在这种结构形式中,如果括号中的表达式成立,则程序执行“{}”中的语句;否则程序将跳过“{}”中的语句部分,顺序执行其他语句。例如:
if (PINB.0==0) { //如果PB.0端口为低电平,则执行下述语句 PORTB.4=~PORTB.4; //PB.4端口输出相反的状态 PORTB.5=0; //PB.5端口输出为低电平 }
②形式二:
if(表达式) {语句1;} else {语句2;}
在这种结构形式中,如果括号中的表达式成立,则程序执行“{语句1;}”中的语句;否则程序执行“{语句2;}”中的语句。例如:
if (PINB.0==0) { //如果PB0端口为低电平,则执行下述语句 PORTB.4=~PORTB.4; //PB4端口输出相反的状态 PORTB.5=0; //PB5端口输出为低电平 } else { //如果PB0端口不是低电平,则执行下述语句 PORTB.7=~PORTB.7; //PB7端口输出相反的状态 PORTB.5=1; //PB5端口输出为高电平 }
③形式三:
if(表达式1) {语句1;} else if(表达式2) {语句2;} else if(表达式3) {语句3;} …… else if(表达式m) {语句m;} else {语句n;}
在这种结构形式中,如果括号中的表达式1成立,则程序先执行“{语句1;}”中的语句,然后退出if选择语句,不执行下面的语句;如果表达式2成立,则程序先执行“{语句2;}”中的语句,然后退出if选择语句,不执行下面的语句;如果表达式3成立,则程序先执行“{语句3;}”中的语句,然后退出if选择语句,不执行下面的语句;……;如果表达式m成立,则程序先执行“{语句m;}”中的语句,然后退出if选择语句,不执行下面的语句;如果上述表达式均不成立,则程序执行“{语句n;}”中的语句。
例如:根据a值的大小决定numb系数,编写的程序段如下所示。
if (a>6500) {numb=1;} else if (a>6000) {numb=0.8;} else if (a>5800) {numb=0.6;} else if (a>5600) {numb=0.4} else {numb=0;}
(2)if语句的嵌套:如果if语句中又包含1个或多个if语句时,这种情况称为if语句的嵌套。if语句的嵌套基本形式如下:
下面以ATmega16单片机为例说明if条件语句,其发光二极管控制电路原理如图3-5所示。在ATmega16单片机的PC0和PC4端口分别接D1和D2这两个发光二极管,该实例的控制任务是当开关K1闭合时,发光二极管D1点亮,D2熄灭;当开关K1断开时,发光二极管D1熄灭,而D2点亮。编写的程序如下:
图3-5 发光二极管控制电路原理
/**************************************************** File name: if语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" #define uchar unsigned char //定义开关及LED与端口的连接 #define redLED PORTC.0 #define greenLED PORTC.4 void main(void) { DDRC=0xFF; PORTC=0xFF; DDRD=0x00; PORTD=0xff; while(1) {if(PIND&0x01) //检测PD0端口上的开关为断开状态 { redLED=0x1; //发光二极管D1熄灭(低电平有效) greenLED=0x0; //发光二极管D2点亮 } else //检测PD0端口上的开关为闭合状态 { redLED=0x0; //发光二极管D1点亮 greenLED=0x1; //发光二极管D2熄灭 } } }
2)switch语句在实际使用中,通常会碰到多分支选择问题,此时可以使用if嵌套语句来实现,但是如果分支很多,if语句的层数太多、程序冗长,则可读性会降低,而且很容易出错。基于此,在C语言中使用switch语句可以很好地解决多重if嵌套容易出现的问题。switch语句是另一种多分支选择语句,是用来实现多方向条件分支的语句。
(1)switch语句格式:
switch(表达式) case 常量表达式1: {语句1;}break; case 常量表达式2: {语句2;}break; case 常量表达式3: {语句3;}break; ︙ case 常量表达式m: {语句m;}break; default: {语句n;}break;
(2)switch语句使用说明。
① switch后面括号内的“表达式”既可以是整型表达式或字符型表达式,也可以是枚举型数据。
②当switch后面表达式的值与某一“case”后面的常量表达式相等时,就执行该“case”后面的语句,如果后面遇到break语句就退出switch语句。若所有“case”中常量表达式的值都没有与表达式的值相匹配,就执行default后面的语句。
③每一个case的常量表达式的值必须互不相同,否则就会出现互相矛盾的现象(对同一个值,提供有两种或者多种解决方案)。
④每个case和default的出现次序不影响执行结果,可先出现“default”再出现其他的“case”。
⑤假如在case语句的最后没有“break;”,则流程控制将转移到下一个case继续执行。因此,在执行完一个case分支后,应使流程跳出switch结构,即终止switch语句的执行,这可使用一个break语句来完成。
下面仍以图3-5的电路原理图为例说明switch的用法。此时该例的任务是当按下开关K1时,发光二极管D1亮;当按下开关K2时,发光二极管D2亮。编写的程序如下:
/**************************************************** File name: switch语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" #define uchar unsigned char //定义开关及LED与端口的连接 #define redLED PORTC.0 #define greenLED PORTC.4 void main(void) { DDRC=0xFF; PORTC=0xFF; DDRD=0x00; PORTD=0xff; while(1) {switch(PIND&0x03) //该表达式判断K1和K2是否闭合,如果闭合则与之相连 //的引脚为低电平 { case 0x02: //表达式的值等于0x02,表示K1开关处于闭合状态 redLED=0x0; //发光二极管D1亮 break; //跳出switch结构,不执行下面的语句 case 0x01: //表达式的值等于0x01,表示K2开关处于闭合状态 greenLED=0x0; //发光二极管D2亮 break; default: //表达式的值既不等于0x01,又不等于0x02表示两个开关 //均未闭合 redLED=0x1; //两个发光二极管均不亮 greenLED=0x1; break; } } }
2.循环语句与控制结构
在许多实际问题中,需要程序进行具有规律的重复执行,此时可以采用一些循环语句来实现。在C语言中,用来实现循环的语句有goto语句、while语句、do-while语句和for语句等。
1)goto语句 goto语句为无条件转向语句,该语句可以实现循环。goto语句的一般格式如下:
goto 语句标号;
其中,语句标号不必特殊加以定义,它是一个任意合法的标识符,其命名规则与变量名相同,由字母、数字和下画线组成,并且第一个字符必须为字母或下画线,不能用整数作为标号。当这个标识符加上一个“:”一起出现在函数内某处时,则执行goto语句后,程序将跳转到该标号处并执行其后的语句。标号必须与goto语句同处于一个函数中,但可以不在一个循环层中。
结构化程序设计主张限制使用goto语句,主要是因为它将使程序层次不清,且不易读,但也并不绝对禁止使用goto语句。在多层嵌套退出时,用goto语句则比较合理。一般来说,使用goto语句可以有以下两种用途:与if语句一起构成循环结构;从循环体中跳转到循环体外。
(1)与if语句一起构成循环结构。例如:用if语句和goto语句构成循环结构,求,编写的程序如下所示。
/**************************************************** File name: goto语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" void main(void) { int i=0,sum=0; loop:if(i<=50) { sum=sum+i; i++; goto loop; } }
该程序的运行结果为1275(十六进制为04FB),在AVR Studio或者在Proteus中可观察到R19中的内容为04,R18中的内容为FB。
(2)从循环体中跳转到循环体外。在C语言中,如果要跳出本层循环和结束本次循环,可以使用break语句和continue语句。goto语句的使用机会已大大减少,只是需从多层循环的内层跳到多层循环体外时才用到goto语句。但是这种用法不符合结构化原则,一般不宜采用,只有在特殊情况(如需要大大提高生成代码的效率)时才使用。
2)while语句 while语句很早就出现在C语言编程的描述中了,它是最基本的控制元素之一,用来实现“当型”循环结构。while语句的一般格式如下:
while(表达式) {语句;}
若程序的执行进入while循环的顶部时,将对表达式求值。如果该表达式为“真”(非零),则执行while循环内的语句。当执行到循环底端时,马上返回到while循环的顶部,再次对表达式进行求值。如果值仍为“真”,则继续循环,否则完全绕过该循环,而继续执行紧跟在while循环之后的语句,其流程如图3-6所示。
图3-6 while语句的流程图
例如:用while语句,求,编写的程序如下所示。
/**************************************************** File name: while语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" void main(void) { int n=0,sum=0; while(n<=50) { sum=sum+n; n++; } }
该程序的运行结果为1275(十六进制为04FB),在AVR Studio或者在Proteus中可观察到R19中的内容为04,R18中的内容仍为FB。
3)do-while语句 do-while循环与while循环十分相似,这两者的区别在于:do-while语句是先执行循环后判断,即循环内的语句至少执行一次,然后再判断是否继续循环,其流程如图3-7所示;while语句是在每次执行的指令前先判断。do-while语句的一般格式如下:
图3-7 do-while语句的流程图
do {语句;} While(条件表达式);
例如:用do-while语句,求,编写的程序如下所示。
/************************************************ *File name: do-while语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" void main(void) { int n=0,sum=0; do { sum=sum+n; n++; } while(n<=50); }
4)for语句在C语言中,for语句使用得最为灵活,完全可以取代while语句或者do-while语句。它不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况。for语句的一般格式如下:
for(表达式1;表达式2;表达式3) {语句;}
for语句的流程如图3-8所示,其执行过程如下所示。
图3-8 for语句的流程图
①先对表达式1赋初值,进行初始化。
②判断表达式2是否满足给定的循环条件,若满足循环条件,则执行循环体内语句,然后执行第③步;若不满足循环条件,则结束循环,转到第⑤步。
③若表达式2为“真”,则在执行指定的循环语句后,求解表达式3。
④回到第②步继续执行。
⑤退出for循环,执行下面的语句。
for语句最简单的应用形式也就是最易理解的形式如下:
for(循环变量赋初值;循环条件;循环变量增值) {语句;}
例如:用for语句,求,编写的程序如下所示。
/**************************************************** File name: while语句应用.C Chip type: ATmega16 Clock frequency: 8.0MHz ****************************************************/ #include"mega16.h" void main(void) { int n,sum=0; for(n=0;n<=50;n++) { sum=sum+n; } }
显然,用for语句简单、方便。for语句的一般形式还可以用相应的while循环形式来表示:
表达式1; while(表达式2) { 语句; 表达式3; }
同样,for语句的一般形式还可以用相应的do-while循环形式来表示:
表达式1; do { 语句; 表达式3; } while(表达式2)
for语句使用得最为灵活,除了可以取代while语句或者do-while语句外,还在结构形式上体现了其灵活性,下面对for循环语句的几种特例进行说明。
(1)for语句中小括号内的表达式1缺省时,应在for语句之前给循环变量赋初值。注意,虽然表达式1省略了,但是表达式1后面的分号不能省略。例如:
int n,sum=0; for(;n<=50;n++) { sum=sum+n; }
该程序段执行时,不对n设置初值,直接跳过“求解表达式1”这一步,而其他不变。
(2)for语句中小括号内的表达式2缺省时,不判断循环条件,默认循环条件始终为“真”,使循环无终止地进行下去。例如:
int n,sum=0; for(n=0;;n++) { sum=sum+n; } 它相当于: int n,sum=0; while(1) { sum=sum+n; n++; }
(3)for语句中小括号内的表达式3缺省时,在程序中应书写相关语句以保证循环能正常结束。例如:
int n,sum=0; for(n=0;n<=50;) { sum=sum+n; n++; }
在此程序段中,将n++的操作不放在for语句的表达式3的位置处,而作为循环体的一部分,其效果是一样的,都能使循环正常结束。
(4)for语句中小括号内的表达式1和表达式3缺省,而只给出循环条件时,在此种情况下,完全等效于while语句。例如:
int n,sum=0; for(;n<=50;) { sum=sum+n; n++; }
它相当于:
int n,sum=0; while (n<=50) { sum=sum+n; n++; }
(5)for语句中小括号内的3个表达式都缺省时,既不设置初值,也不判断条件,而循环变量也不增值,则会使程序无终止地执行循环体。例如:
for(;;) {…… /*循环体*/} 它相当于: while(1) {…… /*循环体*/}
(6)for语句中没有循环体。例如:
int n; for(n=0;n<1000;n++) {;}
此例在程序段中起延时作用。
5)break和continue语句
(1)break:break语句通常可以用在switch语句或者循环语句中。当break语句用于switch语句中时,可使程序跳出switch而执行switch以后的语句;当break语句用于while、do-while、for循环语句中时,可使程序提前终止循环而执行循环后面的语句,通常break语句总是与if语句连在一起的,即满足条件时便跳出循环。break语句的一般格式如下:
break;
注意:①break语句不能用在循环语句和switch语句之外的任何其他语句中;②break语句只能跳出它所处的那一层循环,而不像goto语句那样可以直接从最内层循环中跳出来。因此,要退出多重循环时,采用goto语句比较方便。
(2)continue语句:continue语句一般用在while、do-while、for循环语句中,其功能是跳过循环体中剩余的语句而强行执行下一次循环。通常continue语句总是与if语句连在一起的,用来加速循环。continue语句的一般格式如下:
continue;
continue语句和break语句的区别:break语句结束循环时,不再进行条件判断;continue语句只能结束本次循环,不终止整个循环。