[简明C语言]分支和循环P_3:循环 - while语句

发布于:2022-12-25 ⋅ 阅读:(485) ⋅ 点赞:(0)


前言

第二章:分支和循环

循环语句,是用于重复执行某条语句的语句。

while循环语句的循环方式为利用一个条件来控制是否要继续反复执行这个语句,它是计算机的一种基本循环模式。当满足条件时进入循环,不满足跳出。


1. 循环结构

C语言概述第三部分简单介绍过循环结构:

中了五百万
没有中
成为大佬
菜鸟
上学
买彩票
走上人生巅峰
老实学习

在此之前我们已经掌握了if语句:

if(条件)
	语句;

当条件满足的情况下,if语句后的语句执行,否则不执行。
但是这个语句只会执行一次。

但是生活中很多的实际的例子是:同一件事需要完成很多次。

那么应该怎么做呢?

所以需要使用while语句,来实现循环。


2. while语句

while语句执行的流程:
流程图

用一段伪代码来描述模块结构图:

//while 语法结构
while(表达式)
	循环体;

想要在屏幕上打印1-10的数字:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 10)
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
运行结果
使用while循环将1-10的数字打印出来。

break关键字

在循环体中如何跳出循环呢?

在上述例子中,现在想要在i = 5时跳出循环:

#include <stdio.h>

int main()
{
	int i = 1;
	//在while循环中,break用于永久地终止循环
	while (i <= 10)
	{
		if (i == 5)
			{
			break;
			}
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
运行结果
i = 5时,在printf语句之前执行了break语句,跳出循环,所以结果中并没有打印5。

在while循环中,break用于永久地终止循环。

那么continue语句的作用是什么呢?

continue关键字

在上述例子中加入continue语句:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 10)
	{
		if (i == 5)
			{
			continue;
			}
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行程序:
运行程序

此处程序并没有结束!光标在闪烁!
这是因为代码陷入了死循环

通过逐过程调试F10监视变量i:
逐过程调试
可以看到,变量i一直以5的值进入while语句,又从continue出来继续执行,陷入了死循环

在while循环中,continue用于跳过本次循环continue后面的循环,直接进入判断部分,判断是否执行下一次循环。


3. 例一

下面的代码是什么意思?

#include <stdio.h>

int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)getchar获取一个字符
		putchar(ch);//putchar打印一个字符
		//printf("%c\n", ch);
	return 0;
}
  • getc, getwc, getchar, getwchar
    Read a character from a stream(getc, getwc), or get a character from stdin(标准输入 - 键盘)(getchar, getwchar).
     

  • 返回值(Return Value)
    Each of these fiunctions returns the character read.(返回字符的ASCII码值) To indicate an read error or end-of-fle condition, getc and getchar returmn EOF(遇到错误或文件终止时返回“EOF”) , and getwe and getwchar return WEOF. For getc and getchar, use ferror or feof to check for an error or for end of file.
     

  • EOF - end of file - 文件结束标志 - 定义为(-1)

运行结果为:
运行结果
给出字符,就可以打印出这个字符!

想要结束程序,键入Ctrl+Z
请添加图片描述
当getchar读取到EOF时,程序就结束运行了。

回车 - 换行符(\n)也会被getchar读取。


4. 例二

尝试写一个输入密码的程序:

#include <stdio.h>

int main()
{
	//输入密码
	char password[20] = { 0 };//存放20个字符,默认0
	printf("请输入密码:>");
	scanf("%s", password);//数组名代表数组首元素的地址,不需要另外取地址
	printf("请确认密码(Y/N):>");
	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

如果使用scanf_s,此处会报错。
scanf_s要求在输入char或字符串数组时,添加最大长度作为参数,所以应该是scanf_s(“%s”, password, 20);

运行一下看看:
运行结果
这还没输入(Y/N)呢,怎么就弹出确认失败了?

这是因为此处读取了回车Enter

getchar或scanf都是输入函数,都是从缓冲区获取数据。
缓冲区没有数据时,语句就会等待键盘输入。
 
输入密码时,键盘输入123456。
按下回车Enter,将换行符\n和123456一起放到缓冲区中:
\n
此时scanf开始读取,它只读取密码123456并放到password中。
scanf读取之后,缓冲区只剩下\n
缓冲区
scanf执行完之后,执行getchar时,getchar发现缓冲区中有数据
所以getchar直接读取\n,并放到ch中去。
 
由于\n不等于Y,所以进入else,打印出确认失败。
结果
整个过程中没有等待,直接跳到了确认失败。
 
所以需要将缓冲区的\n清除后,再进行下面输入(Y\N)判断的操作。

在代码中加入一个getchar语句把多余的\n读取掉:

#include <stdio.h>

int main()
{
	//输入密码
	char password[20] = { 0 };//存放20个字符,默认0
	printf("请输入密码:>");
	scanf("%s", password);//数组名代表数组首元素的地址,不需要另外取地址
	printf("请确认密码(Y/N):>");
	//清理缓冲区

	getchar();//把'\n'读取走

	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

运行结果:
运行
可见符合预期结果。

拓展

如果想输入两段密码呢?

运行

运行失败!

输入密码时,键盘输入123 abc。
按下回车Enter,将换行符\n和123 abc一起放到缓冲区中:
缓冲区
此时scanf开始读取,它只读取空格之前的123并放到password中。
 
scanf读取之后,缓冲区还剩下abc\n
剩下
但是,getchar()时stdio.h中的库函数,它的作用是从stdin流中读入一个字符。
 
此时缓冲区中有多个字符,读走一个字符并没有解决问题。
所以getchar直接读取了剩余的字符,并放到ch中去。
 
由于剩余的字符不等于Y,所以进入else,打印出确认失败。
结果
整个过程中没有等待,直接跳到了确认失败。
 
所以需要将缓冲区的所有多余字符清除后,再进行下面输入(Y\N)判断的操作。

加入while函数

可以使用循环语句,使用getchar一直读取字符,直到读取到\n结束。

#include <stdio.h>

int main()
{
	//输入密码
	char password[20] = { 0 };//存放20个字符,默认0
	printf("请输入密码:>");
	scanf("%s", password);//数组名代表数组首元素的地址,不需要另外取地址
	printf("请确认密码(Y/N):>");
	//清理缓冲区
	//getchar();//把'\n'读取走

	//清理缓冲区中的多个字符
	int tmp = 0;
	while ((tmp = getchar()) != '\n')
	{
		;//清理字符
	}

	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

运行结果:
运行结果
这就是while语句的应用场景之一。

一些问题:

  • 为什么getchar读取的是一个字符,但是用整型变量来存储呢?
     
    int getchar( void );
    getchar的返回类型就是int
    getchar读取字符的ASCII码值
    getchar还有可能返回EOF,即(-1)
     
     
  • 所以输入密码里不能出现空格吗?
     
    函数都有局限性;
    本例中只是体现出while语句的作用;
    想要实现某些功能,还有更多更适合的函数可以使用。

5. 例三

下面的代码是什么意思?

#include <stdio.h>

int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
	{
		if (ch < '0' || ch > '9')//数字字符的两端(ASCII码值48-57)
			continue;
		putchar(ch);
	}
	return 0;
}

运行一下:
运行
可以看到这个代码的作用是:只打印数字字符,跳过其他字符。

6. while语句注意点

Ⅰ初始化

初始化发生变化会影响循环。

当初始化i为1时:

#include <stdio.h>

int main()
{
	int i = 1;//初始化
	while (i <= 10)
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
运行结果
可以发现打印出了1-10十个数字。

当初始化i为10时:

#include <stdio.h>

int main()
{
	int i = 10;//初始化
	while (i <= 10)
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
在这里插入图片描述
打印结果为10。

Ⅱ判断部分

判断部分发生变化会影响循环。

当判断部分为i <= 10时:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 10)//判断部分
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
运行结果
可以发现打印出了1-10十个数字。

当判断部分为i <= 5时:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 5)//判断部分
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

运行结果:
运行结果
打印结果为1-5五个数字。

Ⅲ调整部分

调整部分发生变化会影响循环。

当调整部分为i++时:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 10)
	{
		printf("%d ", i);
		i++;//调整部分
	}
	return 0;
}

运行结果:
运行结果
可以发现打印出了1-10十个数字。

当调整部分为i += 2时:

#include <stdio.h>

int main()
{
	int i = 1;
	while (i <= 5)
	{
		printf("%d ", i);
		i += 2;//调整部分
	}
	return 0;
}

运行结果:
运行结果

打印结果为1,3,5,7,9五个数字。

问题

由于初始化判断部分调整部分,发生改变,循环语句也会受到影响。

随着代码行数增加,初始化、判断部分、调整部分三个部分的距离也会越来越远,在后续想要修改的时候会比较麻烦。

这个时候就需要引入for循环
for循环将初始化、判断部分和调整部分放在了一起。

for循环将在下一部分讲解。

总结

本文详细介绍了循环语句中的while语句,对循环语句有一个深入的了解。