第五章 C控制语句:循环
一个好的语言应该能够提供以下三种形式的程序流:
●顺序执行语句序列(顺序)
●在满足某个条件之前反复执行一个语句序列(循环)
●通过进行一个判断在两个可选的语句序列之间选择执行(分支)
5.1while循环
程序
//summing.c -- 对用户输入的整数求和
#include<stdio.h>
int main(void)
{
long num;
long sum = 0L;//把sum初始化为0
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;
}
结果
Please enter an integer to be summed. (q to quit):44
Please enter next integer (q to quit):33
Please enter next integer (q to quit):88
Please enter next integer (q to quit):121
Please enter next integer (q to quit):q
Those integers sum to 286.
C:\Users\51670\source\repos\summing\Debug\summing.exe (进程 6224)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
1.程序注解
scanf返回成功读入的项目的个数。如果scanf()成功读入了一个整数,就把这个整数放在num中并返回值1,随后值1被赋值给status(请注意输入值赋给num,而不是status)。如果您输入的不是数字,例如输入q,那么scanf()就不能读入一个整数,所以它的返回值和status都为0。这将使循环终止。
while是一个入口条件循环,所以程序员必须在进入循环体之前获取输入并检查status的值。
5.2比较大小:使用关系运算符和表达式
1.真值的问题
关系运算符也可以用于字符的比较。进行比较时使用的是机器的字符代码(ASCLL)。然而,不能使用关系运算符来比较字符串。
关系运算符也可以用与浮点数。但要小心,在浮点数比较中只能使用<和>。原因在于舍入误差可能造成两个逻辑上应该相等的数不相等。
如果进行比较的数有一个是常量,则可以把它放在比较表达式的左边,这样有助于发现错误:
5 = canoes;//语法错误
5 == canoes;//检查canoes的值是否为5
2.新的__Bool类型
//boolean.c --使用_Bool变量
#include<stdio.h>
int main(void)
{
long num;
long sum = 0L;
_Bool input_is_good;
printf("Please enter an integer to be summed. ");
printf("(q to quit): ");
input_is_good = (scanf("%ld",&num) == 1);
while (input_is_good)
{
sum = sum+num;
printf("Please enter next integer (q to quit): ");
input_is_good = (scanf("%ld", &num) == 1);
}
printf("Those integers sum to %ld.\n",sum);
return 0;
}
C99还提供了一个stdio.h头文件。包含这个头文件可以使用bool来代替_B00l,并把true和false定义成值为1和0的符号常量。在程序中包含这个头文件可以写出与C++兼容的代码,因为C++把bool、图恩和false定义为关键字。
关系运算符的优先级要低于包括+和-在内的算数运算符,但是要高于赋值运算符。
5.3不确定循环与计数循环
#define _CRT_SECURE_NO_WARNINGS 1
//sweetie1.s -- 一个计数循环
#include<stdio.h>
int main(void)
{
const int NUMBER = 22;
int count = 1;
//初始化
while (count <= NUMBER)//判断
{
printf("Be my Valentine!\n");//动作
count++; //更新计数
}
return 0;
}
在建立一个重复执行固定次数的循环时涉及到三个动作:
1.必须初始化一个计数器
2.计数器与某个有限的值进行比较
3.每次执行循环,计数器的值都要递增
while循环条件执行比较的动作,增量运算符执行递增的动作。上述程序中,递增在循环的结尾处执行。这种选择使得有可能不小心漏掉递增的动作。所以更好的方法是使用count++<=NUMBER来吧判断与更新动作结合在一个表达式中,但使用这种方法时计数器的初始化仍然是在循环之外进行的,这样就有可能忘记初始化。
5.4逗号运算符
#define _CRT_SECURE_NO_WARNINGS 1
//2022年4月8日08:57:37
#include<stdio.h>
int main(void)
{
const int FIRST_OZ = 37;
const int NEXT_OZ = 23;
int ounces, cost;
printf(" ounces cost\n");
for (ounces = 1, cost = FIRST_OZ; ounces <= 16; ounces++, cost += NEXT_OZ)
printf("%5d $%4.2f\n",ounces,cost/100.0);
return 0;
}
逗号运算符的两个属性。首先,它保证被它分开的表达式按从左到右的次序进行计算(换句话说,逗号是个顺序点,逗号左边产生的所有副作用都在程序运行到逗号右边之前生效)。其次,逗号运算符的值是右边成员的值。逗号也被用作分隔符。
5.5数组
一个数组就是线性存储的一系列相同类型的值,整个数组有一个单一的名字,单独的项或元素可以使用一个整数索引来进行访问。
float debts[20];
实际上,可以像使用相同类型的变量那样使用一个数组元素。例如,您可以把一个值读入一个特定的元素:
scanf("%f",&debts[4]);
一个潜在的易犯错误是:出于执行速度的考虑,C并不检查您是否使用了正确的下标。例如,以下是错误的代码:
debts[20] = 88.32;//没有这个数组元素
但是编译器并不会发现这样的错误。当程序运行时,这些语句把数据放在可能由其他数据使用的位置上,因而可能破坏程序的结果甚至使程序崩溃。
如果字符数组包含了空字符\0,那么字符数组的内容就构成一个字符串,其中空字符标志着字符串的结尾。数组中元素是顺序存储的。
在for循环中使用数组
//2022年4月8日09:34:09
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define SIZE 10
#define PAR 72
int main(void)
{
int index, score[SIZE];
int sum = 0;
float average;
printf("Enter %d golf scores:\n", SIZE);
for (index = 0; index < SIZE; index++)
scanf("%d", &score[index]);//读入10个分数
printf("The scores read in are as follows:\n");
for (index = 0; index < SIZE; index++)
printf("%5d", score[index]);//验证输入
printf("\n");
for (index = 0; index < SIZE; index++)
sum += score[index];//求它们的和
average = (float)sum / SIZE;//节省时间的方法
printf("Sum of scores = %d,average = %.2f\n", sum, average);
printf("That's a handicap of %.0f.\n",average-PAR);
return 0;
}
结果
Enter 10 golf scores:
102 98 112 108 105 103
99 101 96 102 100
The scores read in are as follows:
102 98 112 108 105 103 99 101 96 102
Sum of scores = 1026,average = 102.60
That's a handicap of 31.
C:\Users\51670\Desktop\C Program\scores_in\Debug\scores_in.exe (进程 21444)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
首先,注意到尽管这个例子显示您输入了11个数,但只有10个被读入了,因为读取循环只读入10个值。因为scanf()跳过空白字符,所以您可以在一行之内输入所有的10个数,也可以在每一行只输入一个数,或者您也可以像这个例子一样混合使用新行与空格来分隔输入(因为要对输入进行缓冲,所以只有当您键入回车键的时候这些数字才被发送给程序)。
这些例子说明了一些风格问题。首先,使用#define指令创建一个指定数组大小的明显常量(SIZE)是一个好主意,您可以在定义数组和设置循环限制条件时使用这个常量。
其次,下面的代码可以很方便地处理一个大小为SIZE的数组:
for(index=0;index<SIZE;index++)
第三,一个好的编程习惯是使程序重复输出或“回显”刚刚读入的值。这有助于确保程序出来了您所期望的数据。
5.6关键概念
循环是一个强大的编程工具。在建立循环时应该特别注意三个方面:
●明确定义结束循环的条件
●确保在循环判断中使用的值在第一次使用之前已经初始化
●确保循环在每个周期中更新了判断值
C通过数值计算来处理判断条件。结果为0表示假,任何其他值都表示为真。使用了关系运算符的表达式通常被用来进行判断,他们有些特殊。如果为真,关系表达式的值为1,假为0,这与新的_Bool类型所允许的值保持一致。
数组由相同类型的邻近的内存位置组成。您需要谨记数组元素是从0开始编号的,这样最后一个元素的下标就比元素个数少1.C并不检查您是否使用了合法的下标值,所以这需要由您自己来负责。
使用一个函数需要完成三个单独的步骤:
1.使用函数原型声明该函数
2.在程序中通过函数调用来使用该函数。
3.定义函数
原型使编译器可以检查您是否正确地使用了函数,而定义则规定了函数如何工作。现代的编程习惯是把程序的元素分为接口和实现部分,原型和定义就是这样的例子。接口部分描述了如何使用一个特性,这正是原型所做的;而实现部分说明了采取的具体动作,这正是定义所做的。