第4章 程序的控制结构
语句及程序框架
语句的基本分类
语句是程序的基本组成部分,一段程序或者一个函数
就是由若干语句按照算法规定的逻辑关系组成的。各种表
达式都要以语句的形式出现在程序中。C语言中的语句有
如下类型:
(1)变量定义语句;
(2)表达式语句;
(3)复合语句;
(4)函数调用语句;
(5)控制语句;
1.变量定义语句
语法格式如下:
<类型标识符> <变量表>;
其含义是:告诉系统,变量表中列出的一个或多个变量的
存在,同时要求系统为每个变量分配存储空间,其大小和类
型与<类型标识符>所规定的一致。变量定义语句不需要与操
作步骤对应。例如:
char aChar;
int total = 0; //用于累计素数的总数
int score;
double x, y;
2.表达式语句
语法格式如下:
<表达式>;
从上述语法格式可以看出,表达式语句是由前面章节提
及的各种表达式及后缀的分号构成。其作用是:一旦表达式
语句被执行时,就要按照表达式的求值顺序计算出表达式的
值。
例如:
i++;
3. 复合语句
语法格式如下:
{ <语句1> <语句2> … <语句n> }
其作用是:告诉编译系统,将多个语句看成是一个整体,在语
法要求上相当于一个语句。因此,在分支和循环结构中经常使
用,函数的定义体也是一个复合语句。
例如:
{
int x, y = 20;
x = y + 10;
printf( “%d; %d”, x, y );
}
4. 函数调用语句
语法格式如下:
<函数名>( <实际参数表> );
函数调用语句的作用是:将执行控制流程转移到与此语
句完全匹配的函数定义体的第一条语句处,开始执行;函数
体执行结束后,返回到此语句的下一条语句处继续执行。执
行流程如教材图4-1.
5. 控制语句
就是用于控制程序执行路径的相关语句,可以实现程序的
各种控制结构。C语言中包含有九种控制语句。分成三类介
绍如下:
(1)分支语句:if语句、switch语句;
(2)循环语句:do-while语句、while语句、for语句;
(3)跳转语句:break语句、continue语句、goto语句、
return语句。
分支和循环语句是程序中经常使用的,灵活熟练掌握其用
法是本章的主要任务,后面会详细讲解。
C程序框架
一个C程序由若干源文件和头文件组成,称之为一个工
程。头文件包括用户自定义的和系统定义的,这两种都是由
若干函数及全局量的声明和编译预处理组成,文件名一般是
*.h。系统定义的如, , ,
,
等,其内容可以在本地安装有C编译器的机器中找
到。一般在安装路径的包含文件夹内,如:
\Program Files\Microsoft Visual Studio\VC98\Include。
分支结构
分支结构与顺序结构相对应,可以称之为判断结构或选
择结构,也就是有选择地执行某些语句,改变了完全顺序执
行的程序结构。在日常生活中的表达方式是:如果…;那
么…。在计算机领域的基本表达方式是:如果条件成立,则
执行操作1;否则条件不成立,则执行操作2。其中,操作
1,操作2可以是一条或多条语句。
分支结构包含if和switch两个语句.
if语句的基本格式
语法格式如下:
if ( <条件表达式> )
复合语句1
else
复合语句2
if 语句基本格式的语义是:如果条件表达式的值为真,
则执行复合语句1,否则,即条件表达式的值不为真,则执
行复合语句2。格式中“( <条件表达式> )”写法与“( <条
件表达式> != 0 )”写法等价。
【例】 任意输入两个不同的整数,输出其中较大的一个。
#include<>
void main( )
{
c4: int x, y;
c5: printf( "\n Input two numbers: \n" );
c6 : scanf( "%d%d", &x, &y );
c7 : if ( x > y )
c8 : { printf( "max = %d \n", x ); }
else
c10: { printf( "max = %d \n", y ); }
return ;
}
使用if语句需要注意如下内容:
(1)从if 关键词开始到复合语句2结束,在语法上就是一条
语句,虽然其中包含着两个复合语句,这并不矛盾。
(2) if语句中的两个复合语句都可以由空语句,一个语句或
者多个语句构成。仅包含空语句或一个语句时,一对大括号
可以省略。此处增加大括号的好处是可以减少不必要的语法
错误。
(3) if语句中的条件表达式必需使用一对匹配的圆括号括起
来。
(4) 条件表达式可以是逻辑表达式,关系表达式,算术表达
式及赋值表达式等,单个变量构成的表达式也是正确的。只
要表达式的值不是0值,条件即为成立。
【例】 判断某年份是否是闰年。
满足以下两个条件之一的年份均是闰年:一是年份能够被4
整除,但不能被100整除;二是能被400整除的年份。
if语句的第二种格式
语法格式如下:
if ( <条件表达式> )
复合语句
if 语句第二种格式的语义是:如果条件表达式的值不为
真,则直接执行此if语句的下一条语句,否则,条件表达式
的值为真,则执行其后的复合语句,复合语句执行完成后同
样要执行if语句的下一条语句。
【例】 任意输入两个不同的整数,输出其中较大的一个。
#include <>
void main( )
{
c4: int x, y, max;
c5: printf( "\n Input two numbers: \n" );
c6: scanf( "%d%d", &x, &y );
c7: max = x;
c8: if ( y > x )
c9: { max = y; }
c10: printf( "max = %d \n", max );
c11: return ;
}
if语句的嵌套形式
可以写出如下几种嵌套形式的if语句:
(1)在第二种格式的复合语句处嵌套第二种格式,形如:
if ( <条件表达式1> )
{ 其它语句1
if ( <条件表达式2> )
复合语句2
其它语句2
}
(2) 第二种格式嵌套第一种格式,形如:
if ( <条件表达式1> )
{
if ( <条件表达式2> )
复合语句21
else
复合语句22
}
(3) 第一种格式嵌套第二种格式,形如:
if ( <条件表达式1> )
{
if ( <条件表达式2> )
复合语句2
}
else
{
if ( <条件表达式3> )
复合语句3
}
(4) 第一种格式嵌套第一种格式,形如:
if ( <条件表达式1> )
{
if ( <条件表达式2> )
复合语句21
else
复合语句22
}
else
{
if ( <条件表达式3> )
复合语句31
else
复合语句32
}
【例】 要求按任意顺序从键盘输入三个整数,编写程序
完成输出最大值和最小值的算法。
基本思路:首先任取其中两个数,判断最大和最小;使
用例的方法。其次,用第三个数分别与刚刚得到的最大
和最小数再次比较,就可以得到三个数中的最大和最小。
if语句的第三种格式
其语法格式如下:
if ( <条件表达式1> )
复合语句1
else if ( <条件表达式2> )
复合语句2
else if ( <条件表达式3> )
复合语句3
……
else if ( <条件表达式m> )
复合语句m
else
复合语句m+1
【例】设计程序完成将百分制成绩转换成五分制表示。
#include <>
void main( )
{
char chscore;
int nscore;
printf( "\n please input Score ( 0 ~~ 100 ): \n" );
scanf( "%d", &nscore );
if ( nscore < 60 )
{ chscore = 'E'; }
else if ( nscore < 70 )
{ chscore = 'D'; }
else if ( nscore < 80 )
{ chscore = 'C'; }
else if ( nscore < 90 )
{ chscore = 'B'; }
else { chscore = 'A'; }
printf( "\n Your Score is %c ! ! ! \n", chscore );
return ;
}
【例】从键盘读取一个字符数据,判断其类型是:控制
类字符,数字字符,大写字符,小写字符,其他字符等类别
中的哪一类?设计程序完成。
基本思路:读入字符数据,依据字符的ASCII值判断所属
范围。
switch语句及break语句
switch语句的语法格式:
switch ( <表达式> )
{
case <常值1>: 复合语句1
case <常值2> : 复合语句2
……
case <常值n>: 复合语句n
default: 复合语句n+1
}
下面使用switch语句改写例题【例4. 5】。
【例】设计程序完成将百分制成绩转换成五分制表示。
#include <>
void main( )
{
c4: int nscore;
c5: char chscore;
c6: printf( "\n input Score ( 0 ~~ 100 ): \n" );
c7: scanf( "%d", &nscore );
c8: nscore /= 10; // 变换后,nscore 的值是0~10
之间的整数
c9: switch ( nscore )
c10: {
c11: case 0:
c12: case 1:
c13: case 2:
c14: case 3:
c15: case 4:
c16: case 5: { chscore = 'E'; }
c17: case 6: { chscore = 'D'; }
c18: case 7: { chscore = 'C'; }
c19: case 8: { chscore = 'B'; }
c20: case 9:
c21: case 10: { chscore = 'A'; }
}
c23: printf( "\n Your Score is %c ! ! ! \n", chscore );
c24: return ;
}
c23句输出chscore变量的当前值。编译运行此段程序应
该是正确的。但是程序运行后,不管百分制成绩是多少,输
出结果总是如下:
Your Score is A ! ! !
程序一定是存在问题。
这里说的流程控制语句,就是指跳转语句break。break语
句的作用之一就是从某个case子句处跳出switch语句,继
续执行switch的下一条语句。break语句还用于循环语句
当中,后面介绍。
例的正确写法需要break与switch语句配合使用,程序
如下:
#include <>
void main( )
{
c4: int nscore;
c5: char chscore;
c6: printf( "\n input Score ( 0 ~~ 100 ): \n" );
c7: scanf( "%d", &nscore );
c8: nscore /= 10; // 变换后,nscore 的值是0~10之间的整数
c9: switch ( nscore )
c10: {
c11: case 0:
c12: case 1:
c13: case 2:
c14: case 3:
c15: case 4:
c16: case 5: { chscore = 'E'; }
c16_1: break;
c17: case 6: { chscore = 'D'; }
c17_1: break;
c18: case 7: { chscore = 'C'; }
c18_1: break;
c19: case 8: { chscore = 'B'; }
c19_1: break;
c20: case 9:
c21: case 10: { chscore = 'A'; }
c21_1: break;
}
c23: printf( "\n Your Score is %c ! ! ! \n", chscore );
return ;
}
【例】设计能够完成单步四则运算的简易运算器,输
出运算结果。
循环结构
C语言中的循环结构主要包括三个语句,分别是:while语
句,do-while语句,for语句。每个语句各有自身的特点,
学习时注意它们在构成循环结构时的异同。
while语句
while语句也可以称之为“当语句”。
表达的含义是:当条件表达式的值为真(非0值)时,就
执行循环体,之后重新判断条件表达式的值;如此重复多次
执行循环体,直到条件表达式的值为假(0值)时,while语
句结束,顺序执行其后的下一条语句。使用流程框图表示如
教材图4-8 。
while语句的语法格式如下:
while ( <条件表达式> )
复合语句
其语义是:
1. 计算条件表达式的值;
2. 如果值为真(或者称为成立,或者非0),则按顺序执行
复合语句;返回步骤1;
3. 如果值为假(或者称为不成立,或者0值),则执行while
语句的下一条语句。
【例】输出ASCII表的可见字符,字符之间用逗号分隔。
可见字符的ASCII值在32~126之间。
#include <>
void main( )
{
c4: int ch = 32;
c5: while ( ch < 127 )
c6: {
c7: printf( "%c,", ch );
//输出字符的同时,用逗号分隔不同字符
c8: ch++;
}
c10: return ;
}
【例】复制输入的一行字符串,并统计字符个数。
可以使用getchar( )函数获得键盘输入的一个字符。一行
字符串的结尾是回车符(‘\n’)。由于系统提供了键盘缓冲
区,所以能够实现复制字符串的效果。
#include <>
void main( )
{
int nCh = 0;
char ch;
printf( "input a string : \n" );
while ( ( ch = getchar( ) ) != '\n' )
{ putchar( ch );
nCh++;
}
printf( "\n The number of char is : %d ! \n", nCh);
}
【例】对例改进,每行输出十个字符。
#include <>
void main( )
{
c4: int ch = 32, i=1;
c5: while ( ch < 127 )
c6: {
c7: printf( "%c,", ch );
c8: if ( i % 10 == 0 )
c9: { printf( "\n" ); }
c10: i++;
c11: ch++;
}
return ;
}
do - while语句
do-while语句与while语句的区别之一是,条件表达式的
计算与判断时机不同。首先看语法格式:
do
复合语句
while ( <条件表达式> );
其执行过程是:首先执行复合语句,然后判断条件表达式
是否成立,如若成立则再次执行复合语句,否则不成立时,
do-while语句结束,执行下一条语句。其流程描述如教材图
4-9.
【例】求两个正整数的最大公因子。
此问题最著名的解法是欧几里德Euclid算法,也称之为
辗转相除法。用自然语言描述如下:
1. 任意输入两个正整数m和n;
2. 求 r = m % n;
3. 如果r 等于0,则说明n是m的最大公因子,算法结束;
4. 否则 r不等于0,则将n赋值给m,r赋值给n;转2处继续。
#include <>
void main( )
{
int m, n, r, m1, n1;
printf( "\n input m, n : \n" );
scanf( "%d%d", &m, &n );
m1 = m; n1 = n;
do {
r = m % n;
m = n; //这里做辗转赋值
n = r;
} while ( n != 0 );
printf( "\n The %d and %d ’s greatest common
divisor is %d ! \n", m1, n1, m );
}
【例】任意输入一个整数,将其倒序后输出。如输入
5678,则输出8765
#include <>
void main( )
{
int m, r;
printf( "\n input an integer m : \n" );
scanf( "%d", &m );
do {
r = m % 10;
printf( "%d", r );
} while ( m /= 10 );
printf( "\n" );
}
for语句
for语句是结构比较完备,使用比较灵活的,用于构造循
环结构的语句之一。甚至可以取代do-while和 while 语句.
语法格式为:
for ( <初值表达式>;<条件表达式>;<增量表达式> )
复合语句
与do-while和while语句相比较,除了关键字不同之外,
还多了两个表达式;而且表达式之间必须使用分号相分隔;
即使某个表达式缺省,分号也不能少。表达式前面添加初
值,条件和增量字样仅是为了表达各自的作用和功能。
for语句的执行步骤描述如下:
1.计算初值表达式;
2.计算条件表达式,若值为真(非0),则按顺序执行复合
语句,之后执行步骤4;
3.若值为假(0值),则转到步骤5;
4.计算增量表达式;跳转到步骤2;
5.循环语句结束,执行其后的第一个语句。
【例】求0到200累加和。
#include <>
void main( )
{
int sum = 0, i;
for ( i = 0; i <= 200; i++ )
{ sum += i;
}
printf( "\n The sum is %d ! \n", sum );
}
总结循环结构设计的一般步骤如下:
1. 选择恰当的循环变量;
2. 明确循环的结束条件;
3. 循环变量的增量(步长)如何变化;
4. 循环体包含哪些语句。
【例】分别打印输出Fabonacci数列的前20项。
【例】任意输入一个整数m,判断其是否素数。
#include <>
int main( )
{
int i, m, isTrue = 1;
//m保存输入的整数,isTrue=1首先认定输入的数是素数
printf( "\n input a number \n " );
scanf( "%d", &m );
for ( i = 2; i < m – 1; i++ )
{ if ( m % i == 0 )
{ isTrue = 0; }
}
if ( isTrue )
{ printf("\n %d is prime number! \n", m ); }
else
{ printf("\n %d is not prime number!\n", m ); }
}
跳转语句
1. break语句
从前面的叙述可知,break语句有两种用法:一是用在
switch语句,结束分支语句,执行下一条语句;二是用在循
环结构中,同样是结束循环,执行下一条语句。其语法格式
如下:
break;
也就是关键字后面直接加一个分号构成。
【例】如下数列,求累加和sum大于5时,是第几项。
1/1,1/2,1/3,1/4,......,1/n
下面给出使用break语句完成的算法。
#include <>
void main( )
{
int i;
double sum = ;
for ( i = 1; ; i++ ) /*设置无限循环*/
{
sum += / i; //求1/n的累加和
if ( sum > 5 ) //条件成立时,跳出循环
{ break; }
}
printf( "\n The No. is %d, The sum is %lf.\n", i, sum );
}
2. continue语句
continue语句的作用是跳过循环中剩余的语句而强行执
行下一次循环。continue语句只用在for、
while、do-while等循环体中,常与if条件
语句一起使用,用来加速循环。其语法格式
如下:
continue;
3. return语句
语法格式如下:
return <表达式>;
return语句一旦被执行,其作用有三:一是计算表达式的
值;二是将表达式的值带回到函数调用语句所在位置;三是
结束当前函数的执行,即使return后面还有其它语句。如果
表达式为空,则return语句的作用是结束当前函数的执行,
并返回到函数调用语句处。注意表达式的值必须与函数返
回值类型一致。
举例说明如下:
int func1( )
{
c3: ...
c4: return 1 * 2;
c5: ...
c6:}
c7:int main( )
c8:{
c9: int x;
c10: x = func1( );
c11: ...
}
循环的嵌套
从判断语句可以包含判断语句的事实,推而广之,循环语
句的循环体既然是复合语句,当然有循环语句的嵌套形式。
也就是每一种循环语句的循环体内,均可以包含有三种循环
语句的任何一种。为了叙述方便,定义单层循环是循环体内
不包含循环语句的结构;双层循环是循环体内仅包含单层循
环的结构形式;三层循环就是双层循环内又包含单层循环的
结构形式。四层以上的循环很少有应用。
【例】请设计程序完成,统计100以内的全部素数的
个数;并输出最大的素数。
总结一下:双层循环的嵌套形式共可分9种,这里仅以for
作为外层循环为例,以while和do-while为外层循环的嵌套
与此相似。
-for嵌套
for ( ; ; ) // 各个表达式均被省略了,以下相同
{
//其它必要的语句
for ( ; ; )
{ //内层循环体
}
//必要语句
}
2. for-while嵌套
for ( ; ; )
{
//其它必要的语句
while ( <表达式> )
{ //内层循环体
}
//必要语句
}
-do-while嵌套
for ( ; ; )
{
//必要的语句
do
{
//内层循环体
} while ( <表达式> );
//必要语句
}
【例】请设计程序输出如下图形。
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
【例】设计程序完成,输出等腰三角形的图形如下:
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
【例】设计程序输出y = cos(x)的图像。
由于有字符窗口的限制,只能将cos(x)的图像顺时针旋
转90o输出,也就是屏幕向右的方向设为y轴正向,屏幕向
下的方向设为x轴的正向。
另外,屏幕宽度一般是80个字符,所以将x轴设定在屏幕
的40列位置处。x值从0o开始,间隔15o取值,到360o结束。
cos(x)的值在-1到+1之间变化,为了比较好地反映值的变
化情况,特别将y值放大35倍,并向屏幕的右侧移动40个
字符,使之介于5到75之间。
【例】 百马百担问题。共有100匹马,要驮100担货,其
中大马驮3担,中马驮2担,两匹小马驮1担,求大、中、小
马各有多少匹?
解决此问题,不妨设大、中、小马各有x,y,z匹。可以列
出方程组如下:
x + y + z = 100 (1)
3 * x + 2 * y + * z = 100 (2)
常见的错误
1.在条件表达式中,将==误写为=。例如:
if ( ( year % 4 = 0 && year % 100 != 0 ) || ( year % 400 = 0 ) )
此处产生的错误是:error C2106: '=' : left operand
must be l-value。是由于将==误写为=造成的。
2.将数学中的不等式写法,直接应用于程序设计之中。
例如:
( x > y > z )
// 正确的写法是 ( x > y && y > z )
子句之前的语句缺少分号。
子句缺少匹配的break语句。
子句的常值重复出现。
6.括号不匹配。建议书写程序时不管大括号,方括号,圆
括号均要成对输入。
7.误写空语句。例如:
while ( ch = getchar( ) != '\n' ) ;
{ putchar( ch ); }
while语句同一行的分号是误写的,但是编译系统却错误
理解为循环体是空语句。所以得不到希望的运行结果,因为
putchar( ch )仅被执行一次。而这样的错误有时是很隐蔽
的。