一、while 循环的语法结构
/* while 语法结构 */
while(表达式)
循环语句;
【描述】表达式结果为真(非0),就执行循环语句。否则循环语句不执行。
【注】循环语句可能不止一条,可能是一个代码块!!!
二、举例演示
1、示例
【例子】在屏幕上打印 1~10 的数字。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int i = 1; while (i <= 10) { printf("%d ", i); i++; } return 0; }
2、示例调试
(1)i = 1
i = 1,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,跳出循环时 i = 2。
(2)i = 2
i = 2,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 3。
(3)i = 3
i = 3,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 4。
(4)i = 4
i = 4,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 5。
(5)i = 5
i = 5,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 6。
(6)i = 6
i = 6,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 7。
(7)i = 7
i = 7,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 8。
(8)i = 8
i = 8,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 9。
(9)i = 9
i = 9,满足 i < 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 10。
(10)i = 10
i = 10,满足 i = 10 的条件,进入 while 的循环主体,执行 printf 函数和 i++ 语句,此时 i = 11。
(11)i = 11
i = 11,不满足 i <= 10 的条件,跳出 while 的循环主体,结束程序!!!
三、while 语句执行流程
四、while 循环中的 break
在 while 循环中,break 的作用是用于永久的终止循环。
1、示例
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int i = 1; while (i <= 10) { if (i == 5) break; printf("%d ", i); i++; } return 0; }
2、示例调试
(1)i = 1
i = 1,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 break 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 2。
(2)i = 2
i = 2,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 break 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 3。
(3)i = 3
i = 3,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 break 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 4。
(4)i = 4
i = 4,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 break 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 5。
(5)i = 5
i = 5,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i == 5,执行 break 语句,然后直接跳到 return 0 最终结束程序。
五、while 循环中的 continue
在 while 循环中,continue 的作用是跳过本次循环中 continue 后面的代码,直接去 while 循环的判断部分,看是否要再进行下一次的循环。
1、示例
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int i = 1; while (i <= 10) { if (i == 5) continue; printf("%d ", i); i++; } return 0; }
【注】输出的光标一直闪烁,说明代码还在运行,只不过陷入了死循环!!!
2、示例调试
(1)i = 1
i = 1,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 continue 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 2。
(2)i = 2
i = 2,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 continue 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 3。
(3)i = 3
i = 3,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 continue 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 4。
(4)i = 4
i = 4,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i != 5,跳过 continue 语句,然后执行 printf 函数和 i++ 语句,跳出循环时 i = 5。
(5)i = 5
i = 5,满足 i < 10 的条件,进入 while 的循环主体,首先判断 i == 5,执行 continue 语句,然后没有执行 continue 后面的任何一条语句而是跳回了 while 的条件判断,而此时的 i 依旧还是 5 ,进入 i == 5 的判断,再一次的执行 continue 语句,然后又跳回了 while 的条件判断。
【注】代码一直在 continue 语句和 while 的条件判断处来回的跳,就这样陷入了死循环(输出的光标一直闪烁,说明代码还在运行,只不过陷入了死循环)!!!
六、示例
/*
* getchar:获取字符
* 函数返回类型:int
* 函数名:getchar
* 入口参数:void(无)
* 函数含义:从一个 stream 中读取一个字符,或者从 stdin(标准输入,即键盘)中获取一个字符
* 函数返回值:返回的是读取到的字符。在读取的时候遇到读取错误或者文件结束,函数返回的是 EOF
* EOF:end of file(文件结束标志)
* 在库中EOF定义:#define EOF (-1),即 EOF 的本质是 -1
*/
int getchar(void);
/*
* putchar:输出字符
* 函数返回类型:int
* 函数名:putchar
* 入口参数:int c(整型变量)
* 含义:输出一个字符
*/
int putchar(int c);
【功能】输入什么字符,就输出什么字符。
【注】当在键盘中按下 Ctrl + Z 就能跳出 getchar 的读取!!!
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int ch = 0; while ((ch = getchar()) != EOF) putchar(ch); return 0; }
【注】EOF 的取值是 -1(-1 不是 ASCII 码值,因此 getchar 的返回类型是整型)!!!
1、getchar 的读取原则
(1)等待
在 getchar 和 键盘中间有一个缓冲区。首先 getchar 看 缓冲区 是空的就开始等待。
【原因】缓冲区是空的。
(2)读取字符
在键盘中输入 S\n 之后,getchar 首先读取一个 S ,S 就放入 ch 中,然后再在屏幕中打印一个 S 。之后,又回到 while 的判断条件处再一次的执行 getchar ,这一次读取了 \n(换行),就放入 ch 中,然后再在屏幕中打印一个 \n(换行)。故再次输入一个字符就会在下一行中呈现出来。
【注】getchar 是读取字符的,是不会忽略 \n(回车)的。因为 getchar 认为缓冲区中的任何字符都是字符!!!
2、示例分析
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
① 定义整型变量 ch。
② getchar 读取一个字符,并放入 ch 中。
③ 判断 ch 不等于 EOF 就说明 getchar 读取的是正常的字符。
④ 进入 while 的循环主体。
⑤ 执行 putchar 给 ch 中的字符打印出来。
⑥ 如果 getchar 返回的字符放入的 ch 并且发现返回的这个字符是 EOF ,而 EOF == EOF ,此时 while 的判断条件为假,因此不再进入 while 的主体循环去执行 getchar 函数。
七、举例1
【实现功能】输入密码,并确认密码是否正确。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char password[20] = { 0 }; printf("请输入密码:>"); /* password 是数组,数组的数组名本来就是地址 */ scanf("%s", password); printf("请确认密码(Y/N):>"); int ch = getchar(); if (ch == 'Y') { printf("密码确认成功!\n"); } else { printf("密码确认失败!\n"); } return 0; }
【注】还没有进行确认输入就被判断失败了。故,上述举例失败!!!
(1)输入函数
getchar();
scanf();
对于 getchar 和 scanf 这些输入函数来说,不是直接从键盘上获取数据,而是从缓冲区中获取数据。当调用这些输入函数时,这些输入函数会去缓冲区里查看是否有数据,如果没有数据,就会去等待,一直等待到键盘输入数据到缓冲区里。这些输入函数若发现缓冲区里有数据了,就会将这些数据取走。
(2)例子解析(分析错误原因)
① 首先是 printf 函数请输入密码;
② 其次是第一次调用 scanf 函数,scanf 前往缓冲区里查看数据;
③ 然后从键盘中输入一系列的字符串(123456\n)放入缓冲区里(其中 \n ,即回车,会触发 scanf 去读取内容);
④ scanf 将缓冲区中的 123456 取走,并放入 password 这个数组里面;
⑤ 紧接着就会在执行下面的 printf 函数输出 “请确认密码(Y/N):>” ;
⑥ 按道理来说会在往后执行 getchar 函数等待获取是 Y 还是 N;
【注】实际并没有等待获取 Y 或是 N !!!
⑦ 此时缓冲区里还有一个 \n 并没有被上一次的 scanf 给取走,这时候 getchar 将这个缓冲区里剩余的 \n 取走,并将这个 \n 存放进了变量 ch 里面了,没有多做等待。
⑧ 这时候 ch 是 \n ,又不等于 Y ,就直接执行 else 中的 printf 函数,然后就输出了 “密码确认失败!”。
【故】没有执行获取 Y 或者 N 的语句。
(3)例子更正1
【更正点】将缓冲区中的 \n 清除(在调用 getchar 函数之前)。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char password[20] = { 0 }; printf("请输入密码:>"); /* password 是数组,数组的数组名本来就是地址 */ scanf("%s", password); printf("请确认密码(Y/N):>"); /* 清理缓冲区 */ getchar(); //处理 \n int ch = getchar(); if (ch == 'Y') { printf("密码确认成功!\n"); } else { printf("密码确认失败!\n"); } return 0; }
【注】当输入 "123456 abc" 时就会再一次失败!!!
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char password[20] = { 0 }; printf("请输入密码:>"); /* password 是数组,数组的数组名本来就是地址 */ scanf("%s", password); printf("请确认密码(Y/N):>"); /* 清理缓冲区 */ getchar(); //处理 \n int ch = getchar(); if (ch == 'Y') { printf("密码确认成功!\n"); } else { printf("密码确认失败!\n"); } return 0; }
(4)例子更正1失败分析
① 首先是 printf 函数请输入密码;
② 其次是第一次调用 scanf 函数,scanf 前往缓冲区里查看数据;
③ 然后从键盘中输入一系列的字符串(123456 abc\n)放入缓冲区里;
④ scanf 将缓冲区中的 123456 取走(取走空格前面数据),并放入 password 这个数组里面;
⑤ 此时缓冲区里的数据还有很多( abc\n,即,空格abc回车);
⑥ 然后在执行 getchar 函数,此函数仅仅取走了一个字符(空格),这时候缓冲区里仍然有很多数据(abc\n,即,abc回车)。
⑤ 紧接着就会在执行下面的 printf 函数输出 “请确认密码(Y/N):>” ;
⑥ 按道理来说会在往后执行 getchar 函数等待获取是 Y 还是 N;
【注】实际并没有等待获取 Y 或是 N !!!
⑦ 此时缓冲区里还有一个 abc\n 并没有被上一次的 getchar 给取走,这时候又一次执行 getchar 将这个缓冲区里剩余的 abc\n 中的一个字符(a)取走,并将这个 a 存放进了变量 ch 里面了,没有多做等待。
⑧ 这时候 ch 是 a ,又不等于 Y ,就直接执行 else 中的 printf 函数,然后就输出了 “密码确认失败!”。
【故】没有执行获取 Y 或者 N 的语句。
(5)例子更正2
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char password[20] = { 0 }; printf("请输入密码:>"); /* password 是数组,数组的数组名本来就是地址 */ scanf("%s", password); printf("请确认密码(Y/N):>"); /* ---------- 清理缓冲区 ---------- */ int tmp = 0; //定义一个整型变量 tmp /* getchar 读取的字符不是 \n,就执行下面的空语句 * * 使用 while 循环直到将缓冲区里的数据全部清除干净 * 作用:清除缓冲区中的多个字符 */ while ((tmp = getchar()) != '\n') { ; } /* ---------- 清理缓冲区 ---------- */ int ch = getchar(); if (ch == 'Y') { printf("密码确认成功!\n"); } else { printf("密码确认失败!\n"); } return 0; }
八、举例2
ASCII 码中 字符(0)的十进制是 48,字符(9)的十进制是 57,即字符(0 ~ 9)的十进制就是 48 ~ 57。详见:ASCII码。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int ch = 0; while ((ch = getchar()) != EOF) { /* 如果不是数字字符就执行 continue 语句 */ if (ch < '0' || ch > '9') continue; putchar(ch); //只打印数字字符 } return 0; }
【注】此示例子仅仅打印数字字符!!!
九、总结
对于【例子】在屏幕上打印 1~10 的数字。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 1; //初始化
while (i <= 10) //判断部分
{
printf("%d ", i);
i++; //调整部分
}
return 0;
}
循环语句中包含初始化、判断部分、调整部分。对于 while 循环来说,随着循环语句的增加,这三个部分的距离也就会越来越远。一旦要进行修正,就会给编程增加很大的负担。
【注】循环语句中初始化、判断部分、调整部分任意一个发生改变都会影响到循环的结果!!!