C语言学习笔记(六)

传送链接:分支和循环(一)

 

一、循环语句

循环语句:

  • while 循环语句
  • for 循环语句
  • do while 循环语句

 

while循环语句

分支和循环(二)_C语言学习笔记

while语句的语法:

while(表达式)
{
	语句;
}
#include <stdio.h>
int main()
{
	while(1)
		printf("hehe\n");
	
	return 0;
}

上面代码的结果是死循环,一直打印 hehe ,原因,表达式结果为 1 ,表示真,条件恒成立,所以无限执行循环体

 

【练习】:用while循环打印1~10

#include <stdio.h>

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

 

【拓展】

分支和循环(二)_C语言_02

由图可知,while循环语句里也可以使用 break;和 continue;语句,下面是代码实例:

【break】

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

运行结果:1 2 3 4

分析:定义循环变量 i ,并赋值为 1 ,第一次 while 循环,i == 1;1 <= 10 ,结果为真,执行循环体中的 if 判断语句,i != 5,结果为假,打印 i 的值,i ++,i == 2;2 <= 10,再次进入循环,直到 i == 5 的时候,if 语句条件成立,执行 break 语句,跳出 while 循环,此时 printf 语句不会被执行,所以 i = 5 没被输出,结果就只输出了 1~4

【continue】

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

运行结果:1 2 3 4 死循环

分析:定义循环变量 i ,并赋值为 1 ,第一次 while 循环,i == 1;1 <= 10 ,结果为真,执行循环体中的 if 判断语句,i != 5,结果为假,打印 i 的值,i ++,i == 2;2 <= 10,再次进入循环,直到 i == 5 的时候,if 语句条件成立,执行 continue 语句(当执行到continue时,直接认为本次循环结束,continue后面的语句在本次循环会被忽略),忽略后面的语句回到 while 语句的判断处,此时 i == 5;i <= 10,条件成立,进入循环体,同时 i 还满足if语句的条件( i 的值未被改变仍是 5 )再次执行 continue ,再次被返回,一直这样下去,造成程序死循环。

解决上述死循环的方法

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

运行结果:1 2 3 4 6 7 8 9 10

结果少了一个 5 ,原因就是上面代码分析的加黑字

小总结:continue 是用于终止本次循环的,也就是本次循环 continue 后面的代码不会再执行,而是直接跳转到 while 语句的判断部分,进行下一次循环的入口判断

 

【while 语句与 EOF 的结合使用】

int ch = 0;
while((ch = getchar()) != EOF)
	putchar(ch);

分析:定义一个整型变量 ch ,使用函数 getchar()从键盘上获取一个字符,获取到的字符保存到 ch 中,然后与 EOF 进行比较,相等则结果为假,跳出循环,不相等则结果为真,打印 ch 存的数据。(EOF --- end of file(文件结束符,类似字符串结束符‘\0’) 值为 -1),那要如何结束这样一个程序呢? 答案是:在键盘上按 ctrl + z,控制台上会出现:^z 按回车即可结束程序,因为 ctrl + z = EOF

 

这样的代码有实际意义吗?

分支和循环(二)_分支和循环_03

下面就举一些例子:

【例一、】

#include <stdio.h>
int main()
{
	int ret = 0;
	char password[20] = {0};
	
	printf("请输入密码:");
	scanf("%s",password);
	printf("请确认(Y/N):");
	
	ret = getchar();
	
	if(ret == 'Y')
		printf("确认密码\n");
	else
		printf("放弃确认\n"); 
	
	return 0;
}

分支和循环(二)_分支和循环_04

但是代码的运行结果与我们预想的不符合,我们要求输入密码后,等待我们输入 Y 或 N 来决定是确认输入还是放弃确认,但是运行结果跳过了ret = getchar(); 这一步,直接输出放弃确认。

分析错误原因:这里我们要先了解输入函数的工作原理,scanf 要读取一个字符串,首先会去检测一个地方,这个地方叫输入缓冲区,这个区里可能会存放一些数据,第一次去读的时候,缓冲区里什么都没有,因为程序才刚刚运行,此时屏幕的光标会一直闪动,等待用户输入数据,一旦用户输入了数据就被保存到缓冲区里,scanf 就进缓冲区读走它所需要的那一部分,到了 getchar 时,也同样去缓冲区里看看有没有数据,没有就等待用户输入,有就直接读走;上面代码运行后,我们输入了 123456 然后回车使程序继续执行,scanf 就读走了字符串 123456 缓冲区此时留下了回车(即:\n),到了 getchar 的时候,它检测缓冲区,检测到有数据:\n,就把 \n 读走了,所以用户无法输入,因为 \n 对应的 ASCII 码值为十进制的 10,10 != ‘Y’,所以屏幕输出了放弃确认。

【小拓展】:为什么上面代码 scanf(“%s”,password)不需要加 &(取地址符),原因是在C语言中数组名就代表了地址,所以不需要再加 & 去取地址了

 

解决思路:

既然缓冲区里还留下了一个 \n 那我们再写一个 getchar 把它读走让下一个 getchar 被执行时,缓冲区里没有数据不就可以了,答案:正确

int ret = 0;
char password[20] = {0};
	
printf("请输入密码:");
scanf("%s",password);
printf("请确认(Y/N):");
	
getchar();
ret = getchar();
	
if(ret == 'Y')
	printf("确认密码\n");
else
	printf("放弃确认\n"); 

分支和循环(二)_分支和循环_05

这样程序就不存在问题了吗?

分支和循环(二)_C语言学习笔记_06

代码当然还是存在问题的,如果我们输入 123456 abcd 这样的字符串呢?(中间加了一个空格)

分支和循环(二)_分支和循环_07

明显执行结果和我们预期的又不一样了,原因是 scanf 只读取空格前面的数据 所以把123456读走了,剩下 空格abcd\n 执行倒第一个getchar时,读走了空格,剩下 abcd\n,第二个 getchar 把 a 读走了,a != ‘Y’ 所以输出放弃确认

 

解决思路:

循环读取缓冲区里的数据,直到读取到的字符是 ‘\n‘ 时,停止循环,此时缓冲区就空了,保证第二个 getchar 读取时缓冲区没数据

分支和循环(二)_C语言学习笔记_08

 

【例二、】

#include <stdio.h>
int main()
{
	char ch = 0;
	while((ch = getchar()) != EOF)
	{
		if(ch < '0' || ch > '9')
			continue;
		putchar(ch);
	}
	
	return 0;
}

代码分析:从键盘上获取一个字符,不等于 EOF 就往下执行,判断 ch 获取的字符是否为0~9之间的数字字符,如果不是就重新执行while(只会输出数字字符,非数字字符则不能输出)

 

【for循环语句】

分支和循环(二)_C语言学习笔记_09

语法

for(表达式1;表达式2;表达式3)
	循环语句;
  • 表达式1:为初始化部分,用于初始化循环变量
  • 表达式2:条件判断部分,用于判断循环的终止
  • 表达式3:调整部分,用于循环条件的调整

 

提示:for循环 表达式1 只被编译器执行一遍(第一次进入循环的时候,此时表达式3不会被执行),也就是第二次循环的时候,直接从 表达式2 开始,跳过 表达式1 

 

同样的 break 和 continue 语句也可以加在 for 循环中(执行过程和 while 中使用的一样)

#include <stdio.h>
int main()
{
	int i;
	for(i = 1;i <= 10;i ++)
	{
		if(i == 5)
			continue;
		printf("%d ",i);
	}
	
	return 0;
}

运行结果:1 2 3 4 6 7 8 9 10
#include <stdio.h>
int main()
{
	int i;
	for(i = 1;i <= 10;i ++)
	{
		if(i == 5)
			break;
		printf("%d",i);
	}
	
	return 0;
}

运行结果:1 2 3 4

 

使用for循环语句的一些建议

  • 不可在 for 循环体内修改循环变量,防止 for 循环失去控制
  • 建议 for 语句的循环控制变量的取值采用“前闭后开区间”写法

 

【建议①】

分支和循环(二)_分支和循环_10

由于在循环体内改了循环变量 i 的值(i = 5)导致程序死循环

【建议②】

分支和循环(二)_C语言_11

闭区间:可以取等;开区间:不可以取等,上图红方块中 i = 0(闭区间);i < 10(开区间);如果改成 i <= 9 ,那就是闭区间了 

 

【for循环的变种】

【变种一】

for(;;)
{
	printf("hehe\n");
}

分支和循环(二)_C语言_12

分析:for 循环中 表达式2(判断部分)被省略,那条件就恒为真,所以程序死循环了

 

【变种二】

int x,y;
for(x = 0,y = 0;x < 2 && y < 5;++ x,y ++)
{
	printf("hehe\n");
}

分支和循环(二)_C语言学习笔记_13

分析:x,y都被初始化为 0 ,所以满足循环条件( x < 2 和 y < 5 同时满足),条件成立,打印一个 hehe 后 x 和 y 都自增加 1 ,x == 1,y == 1,满足条件,再次打印 hehe ,x 和 y 再自增加 1 ,x == 2,满足条件,循环结束,所以打印了两次 hehe 

 

【使用for常见的错误】

正常代码:

int i,j;
for(i = 0;i < 10;i ++)
{
	for(j = 0;j < 10;j ++)
	{
		printf("hehe\n");
	}
}

运行结果:打印100个hehe

分析代码:i = 0,i < 10,条件为真,进入循环,j = 0,j < 10,条件为真,打印一个 hehe j ++,仍小于 10 再打印,直到 j == 10 停止循环,共打印十个 hehe ,i ++,仍小于10,进行第二次循环,直到 i == 10,整个循环结束,共打印 100 个 hehe

 

省略表达式:

int i = 0,j = 0;
for(;i < 10;i ++)
{
	for(;j < 10;j ++)
	{
		printf("hehe\n");
	}
}

运行结果:打印10个hehe

分析代码:i,j 都初始化为 0,进入第一次循环 i == 0;i < 10,条件为真,j 也同时满足条件进入循环,打印一个 hehe ,j++,j == 1;仍小于 10,继续循环,直到 j == 10,跳出循环,i++,i 仍小于 10,执行第二个 for 的判断语句,此时 j 仍然等于 10,10 < 10 结果为假,不成立,所以不执行第二个 for 的循环体,i++,直到 i == 10,整个循环结束,一共打印了 10 个 hehe

解决方法:把第二个循环的初始化变量补上就好了,第一个的可以不补(j = 0 补上)

 

分支和循环(二)_C语言_14

今天的比较内容比较多,有错的在评论区留言