C语言学习笔记(六)
传送链接:分支和循环(一)
一、循环语句
循环语句:
- while 循环语句
- for 循环语句
- do while 循环语句
【while循环语句】
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;
}
【拓展】
由图可知,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
这样的代码有实际意义吗?
下面就举一些例子:
【例一、】
#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;
}
但是代码的运行结果与我们预想的不符合,我们要求输入密码后,等待我们输入 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");
这样程序就不存在问题了吗?
代码当然还是存在问题的,如果我们输入 123456 abcd 这样的字符串呢?(中间加了一个空格)
明显执行结果和我们预期的又不一样了,原因是 scanf 只读取空格前面的数据 所以把123456读走了,剩下 空格abcd\n 执行倒第一个getchar时,读走了空格,剩下 abcd\n,第二个 getchar 把 a 读走了,a != ‘Y’ 所以输出放弃确认
解决思路:
循环读取缓冲区里的数据,直到读取到的字符是 ‘\n‘ 时,停止循环,此时缓冲区就空了,保证第二个 getchar 读取时缓冲区没数据
【例二、】
#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循环语句】
语法
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 语句的循环控制变量的取值采用“前闭后开区间”写法
【建议①】
由于在循环体内改了循环变量 i 的值(i = 5)导致程序死循环
【建议②】
闭区间:可以取等;开区间:不可以取等,上图红方块中 i = 0(闭区间);i < 10(开区间);如果改成 i <= 9 ,那就是闭区间了
【for循环的变种】
【变种一】
for(;;)
{
printf("hehe\n");
}
分析:for 循环中 表达式2(判断部分)被省略,那条件就恒为真,所以程序死循环了
【变种二】
int x,y;
for(x = 0,y = 0;x < 2 && y < 5;++ x,y ++)
{
printf("hehe\n");
}
分析: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 补上)
今天的比较内容比较多,有错的在评论区留言