《C Primer Plus》学习笔记—第6章

目录

《C Primer Plus》学习笔记

第6章 C控制语句:循环

对于计算机科学(是研究计算机,不是用计算机做研究)而言,一门语言应该提供以下3种形式的程序流:.
1.执行语句序列;
2.如果满足某些条件就重复执行语句序列(循环);
3.通过测试选择执行哪一个语句序列(分支)。
第一种形式很熟悉,前面学过的程序中大部分都是由语句序列组成。while循环属于第二种形式。本章将详细讲解while循环和其他两种循环: for和do while。第三种形式用于在不同的执行方案之间进行选择,让程序更“智能”,且极大地提高了计算机的用途。下一章介绍。

1.再探while循环

1.演示程序summing.c
#include<stdio.h>
int main(void)
{
   
	long num;
	long sum=0L;
	int status;
	
 	printf("Please enter an integer to be summed");
	printf("(q to quit):");
	status=scanf("%ld",&num);
	while(status==1)
	{
   
		sum=sum+num;
		printf("Please enter next integer(q to quit):");
		status=scanf("%ld",&num); 
	}
	printf("Those integers sum to %ld.\n",sum);

	return 0;
} 

输入5,再输入5,再输入q,输出如下:

Please enter an integer to be summed(q to quit):5
Please enter next integer(q to quit):5
Please enter next integer(q to quit):q
Those integers sum to 10.

根据用户从键盘输入的整数进行求和。程序利用了scanf()的返回值来结束循环。

2.程序注释

先看while循环,该循环的测试条件是如下表达式:
status == 1
运算符是C的相等运算符(equality operator),该表达式判断status是否等于1。根据测试条件status1,只要status等于1,循环就会重复。每次循环,num的当前值都被加到sum上,这样sum的值始终是当前整数之和。当status的值不为1时,循环结束。然后程序打印sum的最终值。
要让程序正常运行,每次循环都要获取num的一个新值,并重置status。程序利用scanf()的两个不同的特性来完成。首先,使用scanf()读取num的一个新值;然后,检查scanf()的返回值判断是否成功获取值。scanf()返回成功读取项的数量。如果scanf()成功读取一个数,就把该数存入num并返回1,随后返回值将被赋给status(注意,用户输入的值储存在num中,不是status中)。这样做同时更新了num和status的值, while循环进入下一次迭代。如果用户输入的不是数字(如,q),scanf()会读取失败并返回0。此时,status的值就是0,循环结束。因为输入的字符q不是数字,所以它会被放回输入队列中(实际上,不仅仅是q,任何非数值的数据都会导致循环终止,但是提示用户输入q退出程序比提示用户输入一个非数字字符要简单)。

如果scanf()在转换值之前出了问题(例如,检测到文件结尾或遇到硬件问题),会返回一个特殊值EOF (其值通常被定义为-1)。这个值也会引起循环终止。如何告诉循环何时停止?该程序利用scanf()的双重特性避免了在循环中交互输入时的这个棘手的问题。例如,假设scanf() 没有返回值,那么每次循环只会改变num的值。虽然可以使用num的值来结束循环,比如把num>0(num大于0)或num!=0(num不等于0)作为测试条件,但是这样用户就不能输入某些值,如-3或0。也可以在循环中添加代码,例如每次循环时询问用户“是否继续循环?<y/n>”,然后判断用户是否输入y。这个方法有些笨拙,而且还减慢了输入的速度。使用scanf()的返回值,轻松地避免了这些问题。
现在,来看看该程序的结构。总结如下:

把sum初始化为0
提示用户输入数据
读取用户输入的数据
当输入的数据为整数时,
	输入添加给sum,
	提示用户进行输入,
	然后读取下一个输入
输入完成后,打印sum的值

这叫作伪代码(pseudocode), 是一种用简单的句子表示程序思路的方法,它与计算机语言的形式相对应。伪代码有助于设计程序的逻辑。确定程序的逻辑无误之后,再把伪代码翻译成实际的编程代码。使用伪代码的好处之一是,可以把注意力集中在程序的组织和逻辑上,不用在设计程序时还要分心如何用编程语言来表达自己的想法。例如,可以用缩进来代表块代码,不用考虑C的语法要用花括号把这部分代码括起来。

总之,因为while循环是入口条件循环,程序在进入循环体之前必须获取输入的数据并检查status的值,所以在while前面要有一个scanf()。要让循环继续执行,在循环内需要一个读取数据的语句,这样程序才能获取下一个status的值,所以在while循环末尾还要有一个scanf(),它为下一次迭代做好了准备。可以把下面的伪代码作为while循环的标准格式:

获得第1个用于测试的值
当测试为真时
	处理值
	获取下一个值
3.C风格读取循环

根据伪代码的设计思路,程序summing.c可以用Pascal、BASIC 或FORTRAN来编写。但是C更为简洁,下面的代码:

status = scanf ("%1d", &num) ;
while (status == 1)
{
   
    /*循环行为*/
	status = scanf("%ld"&num) ;
}

可以用这些代码替换:

while (scanf("%ld", &num) == 1)
{
   
    /*循环行为*/
}

第二种形式同时使用scanf()的两种不同的特性。首先,如果函数调用成功,scanf()会把一个值存入num。然后,利用scanf()的返回值(0或1,不是num的值)控制while循环。因为每次迭代都会判断循环的条件,所以每次迭代都要调用scanf()读取新的num值来做判断。换句话说,C的语法特性可以用下面的精简版本替换标准版本:

当获取值和判断值都成功
	处理该值

2.while语句

while循环的通用形式如下:

while ( expression )
	statement

statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来的复合语句。
到目前为止,程序示例中的expression部分都使用关系表达式。也就是说,expression是值之间的比较,可以使用任何表达式。如果expression为真(或者更一般地说,非零),执行statement 部分一次,然后再次判断expression。在expression为假(0)之前,循环的判断和执行一直重复进行。每次循环都被称为一次迭代(iteration),如图6.1 所示。

while循环的结构

1.终止while循环

while循环有一点非常重要:在构建while循环时,必须让测试表达式的值有变化,表达式最终要为假。否则,循环就不会终止(实际上,可以使用break和if语句来终止循环,但是尚未学到)。

2.何时终止循环:程序when.c

要明确一点:只有在对测试条件求值时,才决定是终止还是继续循环。

#include<stdio.h>
int main(void)
{
   
	long n=5;
	
	while(n<7)
	{
   
		printf("n=%d\n",n);
		n++;
	 	printf("now n=%d\n",n);
	}	
	printf("The loop has finished.\n");

	return 0;
} 

输出:

n=5
now n=6
n=6
now n=7
The loop has finished.

在第2次循环时,变量n在第10行首次获得值7。但是,此时程序并未退出,它结束本次循环(第11行),并在对第7行的测试条件求值时才退出循环(变量n在第1次判断时为5,第2次判断时为6)。

3.入口条件循环

while循环是使用入口条件的有条件循环。所谓“有条件”指的是语句部分的执行取决于测试表达式描述的条件,如(index < 5)。该表达式是一个入口条件(entry condition),因为必须满足条件才能进入循环体。在下面的情况中,就不会进入循环体,因为条件一开始就为假:

index = 10;
while (index++ < 5)
	printf ("Have a fair day or better.\n") ;

把第1行改为:index = 3;就可以运行这个循环了。

4.语法要点
1.演示程序while1.c

牢记一点:只有在测试条件后面的单独语句(简单语句或复合语句)才是循环部分。缩进是为了阅读方便,不是计算机的要求。

#include<stdio.h>
int main(void)
{
   
	long n=0;
	while(n<3)//复合语句加{} 
		printf("n is %d\n",n);
		n++;	
	printf("The loop has finished.\n");

	return 0;
} 

虽然程序中缩进了n++;这条语句,但是并未把它和上一条语句括在花括号内。因此,只有直接跟在测试条件后面的一条语句是循环的一部分。变量n的值不会改变,条件n<3一直为真。该循环会一直打印n is 0,除非强行关闭程序。这是一个无限循环(infinite loop)的例子没有外部干涉就不会退出。
记住,即使while语句本身使用复合语句,在语句构成上,它也是一条单独的语句。该语句从while开始执行,到第1个分号结束。在使用了复合语句的情况下,到右花括号结束。

2.演示程序while2.c

要注意放置分号的位置。

#include<stdio.h>
int main(void)
{
   
	long n=0;
	while(n++<3);//加了分号就结束了,这是空语句 
		printf("n is %d\n",n);
	printf("The loop has finished.\n");

	return 0;
} 

输出如下:

n is 4
The loop has finished.

循环在执行完测试条件后面的第1条语句(简单语句或复合语句)后进入下一轮迭代,直到测试条件为假才会结束。该程序中第5行的测试条件后面直接跟着一个分号,循环在此进入下一轮迭代,因为单独一个分号被视为一条语句。虽然n的值在每次循环时都递增1,但是第6行的语句不是循环的一部分,因此只会打印一次循环结束后的n值。
在该例中,测试条件后面的单独分号是空语句(null statement),它什么也不做。在C语言中,单独的分号表示空语句。有时,会故意使用带空语句的while语句,因为所有的任务都在测试条件中完成了,不需要在循环体中做什么。例如,假设想跳过输入到第1个非空白字符或数字,可以这样写:

while (scanf("%d"&num) == 1)
	; /*跳过整数输入*/

只要scanf()读取一个整数,就会返回1,循环继续执行。注意,为了提高代码的可读性,应该让这个分号独占一行,不要直接把它放在测试表达式同行。这样做一方面更容易看到空语句,一方面也说明空语句是有意而为之。处理这种情况更好的方法是使用下一章介绍的continue语句。

3.用关系运算符和表达式比较大小

while循环经常依赖测试表达式作比较,这样的表达式被称为关系表达式(relational expression), 出现在关系表达式中间的运算符叫做关系运算符(relational operator)。表6.1列出了C语言的所有关系运算符。该表也涵盖了所有的数值关系。

关系运算符

关系运算符常用于构造while语句和其他C语句(稍后讨论)中用到的关系表达式。这些语句都会检查关系表达式为真还是为假。关系表达式还可用于比较字符。比较时使用的是机器字符码(假定为ASCII)。例如ch!=‘$’;不能用关系运算符比较字符串。第11章将介绍如何比较字符串。
虽然关系运算符也可用来比较浮点数,但是要注意:比较浮点数时,尽量只使用<和>。因为浮点数的舍入误差会导致在逻辑上应该相等的两数却不相等。例如,3乘以1/3的积是1.0。如果用把1/3表示成小数点后面6位数字,乘积则是99999不等于1。使用fabs()函数(声明在math.h头文件中)可以方便地比较浮点数,该函数返回一个浮点值的绝对值(即,没有代数符号的值)。例如,可以用程序cmpflt.c的方法来判断一个数是否接近预期结果。

1.程序演示cmpflt.c
#include<stdio.h>
#include<math.h>
int main(void)
{
   
	const double ANSWER=3.14159;
	double response;
	
	printf("What is the value of pi?\n");
	scanf("%lf",&response);
	while(fabs(response-ANSWER)>0.0001)
	/*fabs函数比较浮点数,该函数返回一个浮点值的绝对值,
	判断一个数是否接近预期结果 */
	{
   
		printf("Try again!\n");
		scanf("%lf",&response);
	}
	printf("Close enough!\n");

	return 0;
} 

输入3.14,再输入3.1416,输出如下:

What is the value of pi?
3.14
Try again!
3.1416
Close enough!

循环会一直提示用户继续输入,除非用户输入的值与正确值之间相差0.0001。

2.什么是真:程序t_and_f.c

在C中,表达式一定有一个值,关系表达式也不例外。程序t_and_f.c中的程序用于打印两个关系表达式的值, 一个为真,一个为假。

#include<stdio.h>
int main(void)//true1,false0
{
   
	int true_val,false_val;
	
	true_val=(10>2);
	false_val=(10==2);
	printf("true=%d;false=%d\n",true_val,false_val);

	return 0;
} 

输出如下:

true=1;false=0

对C而言,表达式为真的值是1,表达式为假的值是0,1真0假。一些C程序使用下面的循环结构,由于1为真,所以循环会一直进行。

while (1){
   
    ...
}
3.其他真值:程序truth.c
#include<stdio.h>
int main(void)//true1,false0
{
   
	int n=3;
	
	while(n)
	{
   
		printf("%2d is true\n",n);
		n--;
	}
	printf("%2d is false\n",n);
	
	n=-3;
	while(n)
	{
   
		printf("%2d is true\n",n);
		n++;
	}

	return 0;
} 

输出如下:

 3 is true
 2 is true
 1 is true
 0 is false
-3 is true
-2 is true
-1 is true

执行第1个循环时,n分别是3、2、1,当n等于0时,第1个循环结束。与此类似,执行第2个循环时,n分别是-3、-2和-1,当n等于0时,第2个循环结束。一般而言,所有的非零值都视为真,只有0被视为假,非零为真零为假。只要测试条件的值为非零,就会执行while循环。这是从数值方面而不是从真/假方面来.看测试条件。要牢记:关系表达式为真,求值得1;关系表达式为假,求值得0。因此,这些表达式实际上相当于数值。
利用测试条件的这一特性。例如,用while(goats)替换while(goats!=0),因为表达式goats!=0和goats都只有在goats的值为0时才为0或假。第1种形式(while(goats!=0))对初学者而言可能比较清楚,但是第2种形式(while(goats))才是最常用的。

4.真值的问题:程序trouble.c
#include<stdio.h>
int main(void)
{
   
	long num;
	long sum=0L;
	int status;
	
 	printf("Please enter an integer to be summed");
	printf("(q to quit):");
	status=scanf("%ld",&num);
	while(status=1)//一定要注意==与=的区别,可以把常量放在左侧 
	{
   
		sum=sum+num;
		printf("Please enter next integer(q to quit):");
		status=scanf("%ld",&num); 
	}
	printf("Those integers sum to %ld.\n",sum);

	return 0;
} 

运行该程序,其输出如下:
P1ease enter an integer to be summed (q to quit) : 20
Please enter next integer (q to quit): 5
Please enter next integer (q to quit) : 30
Please enter next integer (q to quit) : q
Please enter next integer (q to quit) :
Please enter next integer (q to quit) :

(…屏幕上会一直显示最后的提示内容,除非强行关闭程序。)
这个程序示例改动了while循环的测试条件,把status==1替换成status=1。后者是一个赋值表达式语句,所以status的值为1。而且,整个赋值表达式的值就是赋值运算符左侧的值,所以status=1的值也是1.这里,while(status=1)实际上相当于while(1),也就是说,循环不会退出。虽然用户输入q,status被设置为0,但是循环的测试条件把status又重置为1,进入了下一次迭代。
程序的循环一直运行着,用户在输入q后完全没机会继续输入。如果scanf()读取指定形式的输入失败,就把无法读取的输入留在输入队列中,供下次读取。当scanf()把q作为整数读取时失败了,它把q留下。在下次循环时,scanf()从上次读取失败的地方(q)开始读取,scanf()把q作为整数读取,又失败了。因此,这样修改后不仅创建了一个无限循环,还创建一个无限失败的循环。

不要在本应使用==的地方使用=。一些计算机语言(如,BASIC)用相同的符号表示赋值运算符和关系相等运算符,但是这两个运算符完全不同(见图6.2)。 赋值运算符把一个值赋给它左侧的变量;而关系相等运算符检查它左侧和右侧的值是否相等,不会改变左侧变量的值(如果左侧是一个变量)。

=和==

如果待比较的一个值是常量,可以把该常量放在左侧有助于编译器捕获错误:
5 = canoes <–语法错误
5 == canoes <–检查canoes的值是否为5
可以这样做是因为C语言不允许给常量赋值,编译器会把赋值运算符的这种用法作为语法错误标记出来。构建比较是否相等的表达式时,都习惯把常量放在左侧
关系运算符用于构成关系表达式。关系表达式为真时值为1,为假时值为0。通常用关系表达式作为测试条件的语句(如while和if)可以使用任何表达式作为测试条件,非零为真,零为假。

5.新的_Bool类型:程序boolean.c

在C语言中,一直用int类型的变量表示真/假值。C99专门针对这种类型的变量新增了_Bool 类型。在编程中,表示真或假的变量被称为布尔变量(Boolean variable),所以Bool是C语言中布尔变量的类型名。该类型的变量只能储存1(真)或0(假)。如果把其他非零数值赋给该类型的变量,该变量会被设置为1。这反映了C把所有的非零值都视为真。

程序boolean.c修改了程序trouble.c中的测试条件,把int类型的变量status替换为_Bool 类型的变量input_is_good。给布尔变量取一个能表示真或假值的变量名是一种常见的做法。

#include<stdio.h>
int main(void)
{
   
	long num;
	long sum=0L;
	int input_is_good;
	//_Bool input_is_good;//C99支持bool类型,此编译器不支持 
	
 	printf("Please enter an integer to be summed");
	printf("(q to quit):");
	input_is_good=(scanf("%ld",&num)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值