龙空技术网

C程序设计.进阶

西虹杰出青年 64

前言:

现时朋友们对“scanf循环读入”都比较关怀,朋友们都需要了解一些“scanf循环读入”的相关内容。那么小编在网上汇集了一些对于“scanf循环读入””的相关内容,希望同学们能喜欢,咱们快快来学习一下吧!

前言:

数组(array) 是按顺序储存的一系列类型相同的值, 如10个char类型的字符或15个int类型的值。 整个数组有一个数组名, 通过整数下标访问数组中单独的项或元素(element) 。 例如, 以下声明:float debts[20];声明debts是一个内含20个元素的数组, 每个元素都可以储存float类型的值。 数组的第1个元素是debts[0], 第2个元素是debts[1], 以此类推, 直到debts[19]。 注意, 数组元素的编号从0开始, 不是从1开始。 可以给每个元素赋float类型的值。 例如, 可以这样写:debts[5] = 32.54;debts[6] = 1.2e+21;实际上, 使用数组元素和使用同类型的变量一样。 例如, 可以这样把值读入指定的元素中:scanf("%f", &debts[4]); // 把一个值读入数组的第5个元素这里要注意一个潜在的陷阱: 考虑到影响执行的速度, C 编译器不会检查数组的下标是否正确。 下面的代码, 都不正确:

debts[20] = 88.32; / 该数组元素不存在/

debts[33] = 828.12;/ 该数组元素不存在/

编译器不会查找这样的错误。 当运行程序时, 这会导致数据被放置在已被其他数据占用的地方, 可能会破坏程序的结果甚至导致程序异常中断。数组的类型可以是任意数据类型。

int nannies[22]; /* 可储存22个int类型整数的数组 */

char actors[26]; /* 可储存26个字符的数组 */

long big[500]; /* 可储存500个long类型整数的数组 */

用于识别数组元素的数字被称为下标(subscript) 、 索引(indice) 或偏移量(offset) 。 下标必须是整数, 而且要从0开始计数。 数组的元素被依次储存在内存中相邻的位置。

一、在for循环中使用数组

// scores_in.c -- 使用循环处理数组

#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:

99 95 109 105 100

96 98 93 99 97 98

The scores read in are as follows:

99 95 109 105 100 96 98 93 99 97

Sum of scores = 991, average = 99.10

That's a handicap of 27.

程序运行没问题, 我们来仔细分析一下。 首先, 注意程序示例虽然打印了11个数字, 但是只读入了10个数字, 因为循环只读了10个值。 由于scanf()会跳过空白字符, 所以可以在一行输入10个数字, 也可以每行只输入一个数字, 或者像本例这样混合使用空格和换行符隔开每个数字(因为输入是缓冲的, 只有当用户键入Enter键后数字才会被发送给程序) 。该程序示例演示了一些较好的编程风格。 第一, 用#define 指令创建的明示常量(SIZE) 来指定数组的大小。 这样就可以在定义数组和设置循环边界时使用该明示常量。 如果以后要扩展程序处理20个分数, 只需简单地把SIZE重新定义为20即可, 不用逐一修改程序中使用了数组大小的每一处。

第二, 下面的代码可以很方便地处理一个大小为SIZE的数组:for (index = 0; index < SIZE; index++)

设置正确的数组边界很重要。 第1个元素的下标是0, 因此循环开始时把index设置为0。 因为从0开始编号, 所以数组中最后一个元素的下标是SIZE -1。 也就是说, 第10个元素是score[9]。 通过测试条件index < SIZE来控制循环中使用的最后一个index的值是SIZE - 1。第三, 程序能重复显示刚读入的数据。 这是很好的编程习惯, 有助于确保程序处理的数据与期望相符。最后, 注意该程序使用了3个独立的for循环。

这是否必要? 是否可以将其合并成一个循环? 当然可以, 读者可以动手试试, 合并后的程序显得更加紧凑。 但是, 调整时要注意遵循模块化(modularity) 的原则。 模块化隐含的思想是: 应该把程序划分为一些独立的单元, 每个单元执行一个任务。 这样做提高了程序的可读性。 也许更重要的是, 模块化使程序的不同部分彼此独立, 方便后续更新或修改程序。 在掌握如何使用函数后, 可以把每个执行任务的单元放进函数中, 提高程序的模块化。

二、分支和跳转

(1)if语句

我们从一个有if语句的简单示例开始学习, 请看程序清单7.1。 该程序读取一列数据, 每个数据都表示每日的最低温度(℃) , 然后打印统计的总天数和最低温度在0℃以下的天数占总天数的百分比。 程序中的循环通过scanf()读入温度值。 while循环每迭代一次, 就递增计数器增加天数, 其中的if语句负责判断0℃以下的温度并单独统计相应的天数。

程序 colddays.c程序

// colddays.c -- 找出0℃以下的天数占总天数的百分比

#include <stdio.h>

int main(void)

{

const int FREEZING = 0;

float temperature;

int cold_days = 0;

int all_days = 0;

printf("Enter the list of daily low temperatures.\n");

printf("Use Celsius, and enter q to quit.\n");

while (scanf("%f", &temperature) == 1)

{

all_days++;

if (temperature < FREEZING)

cold_days++;

}

if(all_days != 0)

printf("%d days total: %.1f%% were below freezing.\n",

all_days, 100.0 * (float) cold_days / all_days);

if (all_days == 0)

printf("No data entered!\n");

return 0;

}

下面是该程序的输出示例:

Enter the list of daily low temperatures.

Use Celsius, and enter q to quit.

12 5 -2.5 0 6 8 -3 -10 5 10 q

10 days total: 30.0% were below freezing.

while循环的测试条件利用scanf()的返回值来结束循环, 因为scanf()在读到非数字字符时会返回0。 temperature的类型是float而不是int, 这样程序既可以接受-2.5这样的值, 也可以接受8这样的值。while循环中的新语句如下:

if (temperature < FREEZING)

cold_days++;

if 语句指示计算机, 如果刚读取的值(remperature) 小于 0, 就把cold_days 递增 1; 如果temperature不小于0, 就跳过cold_days++;语句, while循环继续读取下一个温度值。接着, 该程序又使用了两次if语句控制程序的输出。 如果有数据, 就打印结果; 如果没有数据, 就打印一条消息(稍后将介绍一种更好的方法来处理这种情况) 。

为避免整数除法, 该程序示例把计算后的百分比强制转换为 float类型。 其实, 也不必使用强制类型转换, 因为在表达式100.0 * cold_days /all_days中, 将首先对表达式100.0 * cold_days求值, 由于C的自动转换类型规则, 乘积会被强制转换成浮点数。 但是, 使用强制类型转换可以明确表达转换类型的意图, 保护程序免受不同版本编译器的影响。 if语句被称为分支语句(branching statement) 或选择语句(selection statement) , 因为它相当于一个交叉点, 程序要在两条分支中选择一条执行。 if语句的通用形式如下:

if ( expression )

statement

如果对expression求值为真(非0) , 则执行statement; 否则, 跳过statement。 与while循环一样, statement可以是一条简单语句或复合语句。 if语句的结构和while语句很相似, 它们的主要区别是: 如果满足条件可执行的话, if语句只能测试和执行一次, 而while语句可以测试和执行多次通常, expression 是关系表达式, 即比较两个量的大小(如, 表达式 x >y 或 c == 6) 。 如果expression为真(即x大于y, 或c == 6) , 则执行statement。 否则, 忽略statement。 概括地说, 可以使用任意表达式, 表达式的值为0则为假。

statement部分可以是一条简单语句, 如本例所示, 或者是一条用花括号括起来的复合语句(或块) :

if (score > big)

printf("Jackpot!\n"); // 简单语句

if (joe > ron)

{

joecash++;

printf("You lose, Ron.\n");

}

注意, 即使if语句由复合语句构成, 整个if语句仍被视为一条语句。

(2)if else语句

简单形式的if语句可以让程序选择执行一条语句, 或者跳过这条语句。C还提供了if else形式, 可以在两条语句之间作选择。 我们用if else形式修正程序清单7.1中的程序段。

if (all_days != 0)

printf("%d days total: %.1f%% were below freezing.\n",

all_days, 100.0 * (float) cold_days / all_days);

if (all_days == 0)

printf("No data entered!\n");

如果程序发现all_days不等于0, 那么它应该知道另一种情况一定是all_days等于0。 用if else形式只需测试一次。 重写上面的程序段如下:

if (all_days!= 0)

printf("%d days total: %.1f%% were below freezing.\n",

all_days, 100.0 * (float) cold_days / all_days);

else

printf("No data entered!\n");

如果if语句的测试表达式为真, 就打印温度数据; 如果为假, 就打印警告消息。

注意, if else语句的通用形式是:

if ( expression )

statement1

else

statement2

如果expression为真(非0) , 则执行statement1; 如果expression为假或0, 则执行else后面的statement2。 statement1和statement2可以是一条简单语句或复合语句。 C并不要求一定要缩进, 但这是标准风格。 缩进让根据测试条件的求值结果来判断执行哪部分语句一目了然。如果要在if和else之间执行多条语句, 必须用花括号把这些语句括起来成为一个块。 下面的代码结构违反了C语法, 因为在if和else之间只允许有一条语句(简单语句或复合语句) :

if (x > 0)

printf("Incrementing x:\n");

x++;

else // 将产生一个错误

printf("x <= 0 \n");

编译器把printf()语句视为if语句的一部分, 而把x++;看作一条单独的语句, 它不是if语句的一部分。 然后, 编译器发现else并没有所属的if, 这是错误的。 上面的代码应该这样写:

if (x > 0)

{

printf("Incrementing x:\n");

x++;

}

else

printf("x <= 0 \n");

if语句用于选择是否执行一个行为, 而else if语句用于在两个行为之间选择。

(3)介绍getchar()和putchar()

到目前为止, 学过的大多数程序示例都要求输入数值。 接下来, 我们看看输入字符的示例。 相信读者已经熟悉了如何用 scanf()和 printf()根据%c 转换说明读写字符, 我们马上要讲解的示例中要用到一对字符输入/输出函数: getchar()和putchar()。getchar()函数不带任何参数, 它从输入队列中返回下一个字符。 例如,下面的语句读取下一个字符输入, 并把该字符的值赋给变量ch:ch = getchar();

该语句与下面的语句效果相同:

scanf("%c", &ch);

putchar()函数打印它的参数。 例如, 下面的语句把之前赋给ch的值作为

字符打印出来:

putchar(ch);

该语句与下面的语句效果相同:

printf("%c", ch);

由于这些函数只处理字符, 所以它们比更通用的scanf()和printf()函数更快、 更简洁。 而且, 注意 getchar()和 putchar()不需要转换说明, 因为它们只处理字符。 这两个函数通常定义在 stdio.h头文件中(而且, 它们通常是预处理宏, 而不是真正的函数, 第16章会讨论类似函数的宏) 。接下来, 我们编写一个程序来说明这两个函数是如何工作的。 该程序把一行输入重新打印出来, 但是每个非空格都被替换成原字符在ASCII序列中的下一个字符, 空格不变。 这一过程可描述为“如果字符是空白, 原样打印; 否则, 打印原字符在ASCII序列中的下一个字符”。C代码看上去和上面的描述很相似, 请看下面程序。

// cypher1.c -- 更改输入, 空格不变

#include <stdio.h>

#define SPACE ' ' // SPACE表示单引号-空格-单引号

int main(void)

{

char ch;

ch = getchar(); // 读取一个字符

while (ch != '\n') // 当一行未结束时

{

if(ch == SPACE) // 留下空格

putchar(ch); // 该字符不变

else

putchar(ch + 1); // 改变其他字符

ch = getchar(); // 获取下一个字符

}

putchar(ch); // 打印换行符

return 0;

}

( 如果编译器警告因转换可能导致数据丢失, 不用担心。讲到EOF时再解释。 )

下面是该程序的输入示例:

CALL ME HAL.

DBMM NF IBM/

把程序中的循环和该例中的循环作比较。 前者使用scanf()返回的状态值判断是否结束循环, 而后者使用输入项的值来判断是否结束循环。 这使得两程序所用的循环结构略有不同: 程序中在循环前面有一条“读取语句”, 程序中在每次迭代的末尾有一条“读取语句”。 不过, C的语法比较灵活, 读者也可以模仿程序, 把读取和测试合并成一个表达式。 也就是说, 可以把这种形式的循环:

ch = getchar(); /* 读取一个字符 */

while (ch != '\n') /* 当一行未结束时 */

{ ..

. /* 处理字符 */

ch = getchar(); /* 获取下一个字符 */

}

替换成下面形式的循环:

while ((ch = getchar()) != '\n')

{ ..

. /* 处理字符 */

}

关键的一行代码是:

while ((ch = getchar()) != '\n')

这体现了C特有的编程风格——把两个行为合并成一个表达式。 C对代码的格式要求宽松, 这样写让其中的每个行为更加清晰:

while (

(ch = getchar()) // 给ch赋一个值

!= '\n') // 把ch和\n作比较

以上执行的行为是赋值给ch和把ch的值与换行符作比较。 表达式ch =getchar()两侧的圆括号使之成为!=运算符的左侧运算对象。 要对该表达式求值, 必须先调用getchar()函数, 然后把该函数的返回值赋给 ch。 因为赋值表达式的值是赋值运算符左侧运算对象的值, 所以 ch = getchar()的值就是 ch的新值, 因此, 读取ch的值后, 测试条件相当于是ch != '\n'(即, ch不是换行符) 。这种独特的写法在C编程中很常见, 应该多熟悉它。 还要记住合理使用圆括号组合子表达式。 上面例子中的圆括号都必不可少。 假设省略ch =getchar()两侧的圆括号:

while (ch = getchar() != '\n')

!=运算符的优先级比=高, 所以先对表达式getchar() != '\n'求值。 由于这是关系表达式, 所以其值不是1就是0(真或假) 。 然后, 把该值赋给ch。 省略圆括号意味着赋给ch的值是0或1, 而不是 getchar()的返回值。 这不是我们的初衷。下面的语句:

putchar(ch + 1); /* 改变其他字符 */

再次演示了字符实际上是作为整数储存的。 为方便计算, 表达式ch + 1中的ch被转换成int类型, 然后int类型的计算结果被传递给接受一个int类型参数的putchar(), 该函数只根据最后一个字节确定显示哪个字符。

标签: #scanf循环读入