【前言】:循环语句补充的知识点都挺重要的哦,铁汁们要用点心记哦。
一、while语句
while语句
1. while(表达式) 2. { 3. 循环语句; 4. }
【敲黑板】:条件表达式的执行次数比循环体的执行次数多 1次。
循环中的break语句和continue语句:
break语句在循环中的作用:break是永久终止循环;
continue语句在循环中的作用:continue是用于终止本次循环的,也就是说本次循环中continue后面的代码不会执行,而是直接跳转到while语句的判断部分
补充1:getchar()
函数原型:
int getchar(void)
功能:
在标准输入缓冲区中读取一个字符,返回读到的字符的ASCII码值,如果读取失败,则返回EOF(-1)
问:为什么getchar()返回的是字符的ASCII码值?函数返回值为什么要放到一个int类型的变量中呢?不是应该放在一个char类型的变量中吗?
如:
int ch = 0; ch = getchar();
答:
- getchar()返回了字符的ASCII码值,ASCII码值是整数,存放在整型变量中没有任何问题;
- 这一点也是最重要的一点,getchar()读取失败的时候返回EOF,EOF本质是-1(#define EOF -1),是一个整型值,在一个char类型中,是存储不下的;
- 再结合getchat()函数的原型,getchar()的返回类型被定义为int, 那么返回数据应该被存放在int变量中
getchar(): 获取/输入一个字符;
putchar(): 输出一个字符
注意哦,这两个函数一次只能操作一个字符,没有scanf(),printf()功能齐全
补充2:输入缓冲区
引入:
#include<stdio.h> int main() { char input[20] = { 0 }; printf("请输入密码:>"); scanf("%s", input);//输入abcdef最后加上一个\n printf("请确认密码:(Y / N):>"); int ch = getchar(); if (ch == 'Y') { printf("确认成功\n"); } else { printf("确认失败\n"); } return 0; }
此时输入缓冲区中放的是abcdef\n,scanf()函数将abcdef读走了,留下了一个\n,所以\n直接被getchar()读取,所以此时ch里面放的是\n,后面不会等到我们确认密码就直接输出确认失败了。
存在的问题:本题输入abcdef,然后按回车键,还没有等到我们输入字符(Y/ N)就直接输出“确认失败”了,那怎么改才能成为我们想要的呢?
在scanf()下面加上一条语句:getchar();目的只有一个,拿走回车键('\n'),
存在的问题:这样改只能保证输入“abcdef”时可以正常执行,但如果输入的是"abcdef hehe"时又会直接输出“确认失败”了,不信你看...
我丢,这什么鬼,怎么又错了!?
注意:
scanf("%s", ...); //scanf()读取字符串的时候遇到空格时就停下来不再读取了
分析上段代码:
#include<stdio.h> int main() { char input[20] = { 0 }; printf("请输入密码:>"); scanf("%s", input);//scanf()只读取了"abcdef" getchar();//getchar()读取了一个空格,所以这里就有问题了,这里的getchar()只能拿走一个字符,我们想要的是它能将缓冲区 //里剩下字符全部拿走,直至拿走'\n' printf("请确认密码:(Y / N):>"); int ch = getchar(); if (ch == 'Y') { printf("确认成功\n"); } else { printf("确认失败\n"); } return 0; }
是呀,这里的getchar()只能拿走一个字符,我们想要的是它能将缓冲区里剩下的全部拿走,直至拿走'\n',该怎么改?
#include<stdio.h> int main() { char input[20] = { 0 }; printf("请输入密码:>"); scanf("%s", input);//scanf()只读取了"abcdef" //清理缓冲区 int tmp = 0; while ((tmp = getchar()) != '\n') { ;//空语句,什么事都不干,拿字符就行了 } printf("请确认密码:(Y / N):>"); int ch = getchar(); if (ch == 'Y') { printf("确认成功\n"); } else { printf("确认失败\n"); } return 0; }
这样就可以了,注意实现上面清理缓冲区的功能哦。
二、for循环
注意:
for循环的风格更胜一筹,使用的频率也最高。
建议:
- 不可在for循环体内修改循环变量,防止for循环失去控制
- 建议for语句的循环控制变量的取值采用“前闭后开”的写法
如:
for(i = 10; i < 10; i++) //前闭后开 for(i = 10; i <= 10; i++) //前闭后闭(不建议这样写法,但是为了更好地解读也可以用)
一些for循环的变种:
- for的初始化、判断、调整三个部分都可以省略;
- 中间的判断部分如果省略就意味着判断恒为真,构成了死循环。
所以,如果条件允许,不建议省略哦。
笔试题:
//问:打印了多少次hehe? #include<stdio.h> int main() { int i = 0; int j = 0; for (; i < 3; i++) { for (; j < 3; j++) { printf("hehe\n"); } } return 0; }
嘿嘿,想必铁汁心里已经有了自己的答案,看看是不是和正确答案一样呢?
哈哈,说实话,我第一次做这道题目想的是9次,那为什么是3次呢?
看到了吧,所以尽量不要省略for循环里面的表达式。
再加一道笔试题:
int i = 0; int k = 0; for(i = 0, k = 0; k = 0; i++, k++) //k = 0为赋值,0为假,判断表达式恒为假,所以循环执行了0次 { k++; }
三、do...while()循环
do...while循环与前面两个循环不同的是,它的循环体至少执行一次!
do 循环语句; while(表达式);
注意:do...while语句使用的场景有限,所以不会经常使用。
四、练习题
练习1:计算n的阶乘
伪代码:
int ret = 1; int i = 0; for(i = 1; i <= n; i++) { ret = ret * i; }
练习2:计算1!+2!+3!+...+n!
在练习1中我们知道了计算n的阶乘的方法,所以计算1!+2!+3!+...+n!就简单多了。
来,先看一个错误示范:
//为了方便测试我在这里就简单计算到1!+2!+3! #include<stdio.h> int main() { int i = 0; int ret = 1; int sum = 0; int n = 0; for (n = 1; n <= 3; n++) { for (i = 1; i <= n; i++) { ret = ret * i; } sum = sum + ret; } printf("%d\n", sum); return 0; }
实际上1!+2!+3! = 9, 但是实际上结果是...
结果却是15,这是为什么呢?
因为第二层循环里面的ret是上次留下的值,而我们希望执行完一次计算某数的阶乘后,需要将ret重置为1,然后再计算下一个数的阶乘。
所以,改写代码如下:
//为了方便测试我在这里就简单计算到1!+2!+3! #include<stdio.h> int main() { int i = 0; int ret = 1; int sum = 0; int n = 0; for (n = 1; n <= 3; n++) { ret = 1;//注意哦 for (i = 1; i <= n; i++) { ret = ret * i; } sum = sum + ret; } printf("%d\n", sum); return 0; }
上面的代码虽然是对的但是依然不够高效,时间复杂度是O(N^2),那可不可以只利用一层循环就解决了呢?其实是可以的,因为上面进行了很多次重复计算。
如上面计算1!+2!+3!,就进行了很多次重复计算
1. //ret * 1 2. //ret * 1 * 2 3. //ret * 1 * 2 * 3
所以改写成高效的代码如下:
1. for(n = 1; n <= 3; n++) 2. { 3. //n*(n-1)! = n! 4. ret = ret * n; 5. sum = sum + ret; 6. }
【敲黑板】:当我们写出一个算法的时候,千万不要骄傲,好好想想怎么样才可以更加精炼,所以要多学多练!
加油加油!!