• while 循环
  • for 循环
  • do while 循环

while 循环

语法结构:

1
2
while(表达式)
循环语句;

例题:在屏幕上打印 1~10

1
2
3
4
5
6
7
8
9
10
int main()
{
int a = 1;
while (a<=10)
{
printf("%d\n", a);
a++;
}
return 0;
}

break的使用

以下代码会打印输出什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
int a = 1;
while (a<=10)
{
if (a ==5)
{
break;
}
printf("%d\n", a);
a++;
}
return 0;
}

答案:1 2 3 4

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

continue的使用

以下代码会打印输出什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
int a = 1;
while (a<=10)
{
if (a ==5)
{
continue;
}
printf("%d\n", a);
a++;
}
return 0;
}

答案:1 2 3 4 (之后不打印,死循环)

因为代码在a++前就被continue跳过了,a的值一直保持在5,从而变成了一个死循环

while循环中,continue的作用时跳过本次循环中 continue 后面的代码,直接回到了while循环的判断部分,判断是否进行下一次循环

例题

题目一

1
2
3
4
5
6
7
8
9
//代码什么意思?
//代码1
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}

EOF - end of file - 文件结束标志 (-1)

getchar - 输入一个字符 ,注意它的返回值是整型

putchar - 打印输出一个字符

while循环进行getchar()读入,则会依次读到所有的字符,直到循环条件结束。

那么我们来看看以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);
printf("请确认密码(Y/N):");
//getchar返回值是一个整型
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
}

有问题吗?

运行代码后,我们发现

在输入完密码后,回车确认,直接确认密码失败了

为什么呢?

因为getcharscanf这种的输入函数,他并不是直接获取键盘输入的值,而是拿到键盘输入后存到缓冲区里的值

如果没有值,会等待键盘输入值

如果有值,会直接拿走使用

那么以上代码,在scanf函数运行时,发现缓冲区里没有值,就等待键盘输入值

键盘输入值后,回车进行确认,触发scanf函数,这时缓冲区内有 密码/n(/n哪里来的?回车输入的)

scanf函数拿走密码后,运行到getchar函数,此时缓冲区内还有一个/n,就被getchar函数直接拿走赋值给了ch

所以if语句判断时,ch的值是/n不等于Y,确认失败

那么如何解决这个问题呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);
printf("请确认密码(Y/N):");
//清除缓冲区
getchar();
//getchar返回值是一个整型
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
}

那我们可以在ch前面加上一个getchar(),把缓冲区内的数值清除就可以了

然而,就有一个新的问题出现了

当我在运行后输入密码 123456 abc 后

也出现了确认失败

也就是scanf只拿走了空格之前的 123456 而后面的 abc 以及 /n留在了缓冲区

因为getchar()只能获得一个字符;清除缓冲区后还留下了bc\n

等到ch拿走缓冲区的第一个字符b,判断又错误了

所以我们需要清理缓冲区的多个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", &password);
printf("请确认密码(Y/N):");
//清除缓冲区
//getchar();
//清除缓冲区的多个字符
while (getchar() != '\n')
{
}
//getchar返回值是一个整型
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
}

这样就可以成功运行代码了

不过这种方法写的密码不能含有空格,否则空格后的数值不会被存储

可以使用gets()来存储密码,因为它不会向scanf一样读到空格就停止,而是会一直读到回车

1
2
3
char password[20] = { 0 };
printf("请输入密码:");
gets(password);

题目二

1
2
3
4
5
6
7
8
9
10
11
//代码2
int main()
{
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}

以上代码会打印输出什么呢?

答案:不管你打了字母,符号,还是数字,它只会打印输出数字。

for 循环

语法结构:

1
2
for(表达式1;表达式2;表达式3)
循环语句;

表达式1表示初始化部分,用于初始化循环变量

表达式2表示条件判断部分,用于循环时候终止

表达式3表示调整部分,用于循环条件的调整

1
2
3
4
5
6
7
8
9
int main()
{
int i = 0;
for (i = 1; i <= 10; i ++)
{
printf("%d ",i);
}
return 0;
}

break的使用

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
break;
printf("%d ", i);//1 2 3 4
}
return 0;
}

for循环中,break用于永久终止循环

continue的使用

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i);//1 2 3 4 6 7 8 9 10
}
return 0;
}

for循环中,continue的作用时跳过本次循环中 continue 后面的代码,直接回到了for循环的判断部分,判断是否进行下一次循环

注意:for循环在调整部分可以调整变量的值,这点和while不同

for 语句的循环控制变量

尽量不要再for循环中修改循环变量,防止for循环失去控制

建议在for语句的循环控制变量的取值采用“前闭后开区间”写法

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
int i = 0;
//前闭后开
for (i = 0; i < 10; i++)
{
}
//两边都是闭区间
for (i = 0; i <= 9; i++)
{
}
return 0;
}

for循环的变种

以下代码打印输出什么?

1
2
3
4
5
6
7
8
int main()
{
for (;;)
{
printf("hello\n");
}
return 0;
}

答案:死循环输出hello

在使用for语句的时候,初始化,判断,调整是可以省略的(但没必要)

但是,当你省略for循环语句的判断部分的时候,会让判断恒为真,即一直进行循环

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
for (int i = 0; i <3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("hello\n");
}
}

return 0;
}

以上代码打印输出几次hello

答案:9次

因为每一次的的 i 的循环里都会有 3 次 j的循环

那么以下代码会打印输出几次hello呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
int i = 0;
int j = 0;
for (; i <3; i++)
{
for (; j < 3; j++)
{
printf("hello\n");
}
}

return 0;
}

答案:3次

因为循环没有初始化

在第一次i循环后,j循环运行了3次,结束循环,开始第二次

但是因为没有初始化 j的值,j 一直等于3,判断不通过

只输出了3次

1
2
3
4
5
6
7
8
9
int main()
{
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hello\n");
}
return 0;
}

以上代码打印输出几次hello

答案:两次

例题

请问循环要循环多少次?

1
2
3
4
5
6
7
8
int main()
{
int i = 0;
int k = 0;
for(i =0,k=0; k=0; i++,k++)
k++;
return 0;
}

答案:0次

解析:请注意判断部分,k = 0是赋值操作,0为假,判断不通过,没有进行循环

do while 循环

语法结构:

1
2
3
do
循环语句;
while(表达式)

注意:do while 循环最少也会循环一次

1
2
3
4
5
6
7
8
9
int main()
{
int i = 1;
do
{
printf("%d", i);
i++;
} while ( i <= 10);
}

break的使用

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int i = 1;
do
{
if (i == 5)
break;
printf("%d ", i);//1 2 3 4
i++;
} while ( i <= 10);
}

do while 循环中,break用于永久终止循环

continue的使用

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int i = 1;
do
{
if (i == 5)
continue;
printf("%d ", i);// 1 2 3 4 死循环
i++;
} while ( i <= 10);
}

do while循环中,continue的作用时跳过本次循环中 continue 后面的代码,直接到了do while循环的判断部分,判断是否进行下一次循环

goto语句

C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。

从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。

最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。

例如:一次跳出两层或多层循环。

多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。

1
2
3
4
5
6
7
8
int main()
{
flag:
printf("hello\n");
printf("hello\n");
goto flag;
return 0;
}

以上的代码会死循环打印hello

因为goto 语句可以直接跳到你需要的位置

你执行完前面两个打印输出,碰到goto语句,又回到了最开始flag的位置,进行执行,形成一个死循环

关机程序编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//关机程序
//只要运行起来,代码就会在一分钟后进行关机,输入“我是猪”,取消关机
//shutdown -s -t 60 一分钟关机命令 -s表示关机 -t表示时间 60表示多少秒后关机
//shutdown -a 取消关机
#include <stdlib.h>
#include <string.h>
int main()
{
char input[20] = { 0 }; //存放输入的信息
//system() 执行系统命令,需要引用 stdlib.h
system("shutdown -s -t 60");
printf("请注意,你的电脑在一分钟内关机,如果你输入:“我是猪”,我就给你取消关机\n");
again:
scanf("%s",input);
//strcmp 需要引用 string.h
if (strcmp(input,"我是猪") == 0)
{
system("shutdown -a");
printf("好的猪,这次就放过你了\n");
}
else
{
printf("不对哦,快点输入:“我是猪”,电脑要关机咯\n");
goto again;
}

return 0;
}

不使用goto语句的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdlib.h>
#include <string.h>
int main()
{
char input[20] = { 0 }; //存放输入的信息
//system() 执行系统命令,需要引用 stdlib.h
system("shutdown -s -t 60");
printf("请注意,你的电脑在一分钟内关机,如果你输入:“我是猪”,我就给你取消关机\n");
while (1)
{
scanf("%s", input);
//strcmp 需要引用 string.h
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
printf("好的猪,这次就放过你了\n");
break;
}
else
{
printf("不对哦,快点输入:“我是猪”,电脑要关机咯\n");

}
}
return 0;
}

goto语句的适用场景

用于跳出多个循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
...
error:
if(disaster)
// 处理错误情况
...

注意:goto语句只能在一个函数范围内跳转,不能跨函数跳转

练习题

  1. 计算 n的阶乘。

  2. 计算 1!+2!+3!+……+10!

  3. 在一个有序数组中查找具体的某个数字n。

  4. 编写代码,演示多个字符从两端开始显示,向中间汇聚。hello world!!!

    最开始:##############

    ​ h############!

    ​ he##########!!

    ​ hel########!!!

    ​ ……

    到最后:hello world!!!

  5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。

第一题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1.计算n的阶乘
int main()
{
int n = 0;
int i = 0;
//注意 ret不能为0 因为 0 乘任何数都是0
int ret = 1;
printf("请输入n的值:");
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret *= i;
}
printf("%d", ret);

return 0;
}

第二题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//2.计算 1!+ 2!+ 3!+ …… + 10!
int main()
{
int i = 0;
int ret = 1;
int sum = 0;
for (i = 1; i <= 10 ; i++)
{
ret *= i;
sum += ret;
}
printf("%d", sum);

return 0;
}

第三题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//在一个有序数组中查找具体的某个数字n。
//二分法
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//有序数组
int num = 7;//要查找的数字
//通过整个数组的大小(字节)除 单个数组元素的大小,得到整个数组的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz-1;//下标从0开始,最后一个下标为长度length-1
while (left <= right)
{
int mid = (left + right) / 2; //找到数组中间的那个数的下标
if (arr[mid] < num) // 如果中间的数小于我要查找的数字,说明我要的数字在数组的右边
{
left = mid + 1; // mid+1 拿到后面一个数的下标
}
else if (arr[mid] > num)// 如果中间的数大于要查找的数字,说明我要的数字在数组的左边
{
right = mid - 1; // mid-1 拿到前面一个数的下标
}
else
{
printf("找到了:%d,下标为%d\n", arr[mid], mid);
break;
}
}
if (left > right)
{
printf("找不到这个数字");
}

return 0;
}

第四题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//编写代码,演示多个字符从两端开始显示,向中间汇聚。
#include <windows.h>
int main()
{
char arr1[] = "hello world!!!";
char arr2[] = "##############";
int left = 0;
int right = strlen(arr1) - 1;
while (left<=right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//睡眠1秒钟 需要引用头文件 window.h
system("cls");//清空屏幕
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}

第五题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//编写代码实现,模拟用户登录情景,并且只能登录三次。
//(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。
int main()
{
int i = 0;
char password[20] = { 0 };
for (i = 0; i < 3; i++)
{
printf("请输入密码:");
scanf("%s", password);
//两个字符串进行比较,不能使用 == 要使用strcmp 使用时要引入头文件 string.h
//strcmp(s1,s2) s1<s2时,返回为负数;s1 = s2时,返回值 = 0;s1 > s2时,返回正数。
if (strcmp(password, "123456") == 0)
{
printf("登陆成功\n");
break;
}
else
{
if (i < 2)
{
printf("密码错误,请重新输入\n");
}
}
}
if (i == 3)
{
printf("三次密码均输入错误,请稍后再试");
}

return 0;
}

扩展:

strcmp(s1,s2) s1<s2时,返回为负数;s1 = s2时,返回值 = 0;s1 > s2时,返回正数。

它比较的是ASCII码值,比如abcdabca进行比较,

前面三个字符一样,第四位d的ASCII码值为68,而a的ASCII码值为65

所以abcd > abca

猜数字游戏代码实现

写一个猜数字游戏
1.自动生成一个1~100的数字
2.猜数字
a.才对了,就恭喜你,游戏结束
b.你猜错了,就会告诉猜大了,还是猜小了,直到猜对
3.游戏可以一直玩,除非退出游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//写一个猜数字游戏
//1.自动生成一个1~100的数字
//2.猜数字
// a.才对了,就恭喜你,游戏结束
// b.你猜错了,就会告诉猜大了,还是猜小了,直到猜对
//3.游戏可以一直玩,除非退出游戏
#include <stdlib.h>
#include <time.h>
#include <windows.h>
//菜单
void menu()
{
printf("********************************************\n");
printf("************ 1.play **************\n");
printf("************ 0.exit *************\n");
printf("********************************************\n");
}
//猜数字
void game()
{
// rand()生成随机数,需要引入头文件 stdlib.h
// 请注意计算机无法生成真随机数,只能做到使产生的数字重复率很低,这样看起来好象是真正的随机数
// 这叫做伪随机数发生器函数
// rand()会返回一个0~32747的值,但是需要前面先使用srand()函数,初始化它的随机值,否则它每次会默认调用srand(1)来初始化随机值
// srand()也需要引入头文件 stdlib.h,当srand(seed)的seed为固定值的时候,rand()也为固定的值
// srand(seed) 中 seed的类型是 unsigned int 类型
// 所以 srand(seed) 的seed也要是随机数? 我们可以使用时间戳,因为时间戳是一直在变动的
// time() 可以拿到当前时间的时间戳 需要引入头文件 time.h
// 注意 srand() 该是每组随机数调用一次,不是每次rand之前都调用,所以将他放到主函数内,具体为什么需要去了解源码
/*srand((unsigned int)time(NULL));*/
int ret = rand()%100+1;//%100的余数是0~99 +1后就是1~100的数字
int guess = 0;
while (1)
{
printf("请猜数字:");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
Sleep(1000);
system("cls");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();//打印菜单
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1 :
game();
break;
case 0 :
printf("退出游戏:\n");
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
} while (input);
return 0;
}

最大公约数和最小公倍数的代码实现

求两个数的最大公约数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//求两个数的最大公约数:
int main()
{
int m = 0;
int n = 0;
printf("请输入两个数字:");
scanf("%d %d",&m,&n);
int t = 0;
while (t=m%n)
{
m = n;
n = t;
}
printf("最大公约数为:%d",n);
return 0;
}

求两个数的最小公倍数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//求两个数的最小公倍数
int main()
{
int m = 0;
int n = 0;
printf("请输入两个数字:");
scanf("%d %d",&m,&n);
int t = m > n ? m : n;
while (t%m !=0 || t%n != 0)
{
t++;
}
printf("最小公倍数为:%d",t);
return 0;
}

素数代码实现

求100~200中的素数及其数量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//求100~200中的素数及其数量:
//素数 - 质数
//只能被1和本身整除的数
#include <math.h>
int main()
{
int i = 0;
int count = 0;
//代码优化
//除2以外,偶数不可能是素数,也就是除2以外,所有的素数都是奇数
//优化前
//for (i = 100; i <= 200; i++)
for ( i = 101; i <=200; i+=2)
{
int j = 0;
int flag = 1;//默认它为素数
// 代码优化
// 如果这个数不是素数,这个数是可以被2~开平方后的数整除的
// 比如 根号16 = 4;
// 16 = 2*8 = 4*4 这里的2和4都可以整除16
//sqrt(i),返回值是i的平方根。需要引入头文件 math.h
//这样可以省略大量的循环
//优化前:
//for ( j = 2; j < i-1; j++)
for (j = 2; j <= sqrt(i); j++)
{
if (i%j == 0)
{
//如果 i%j == 0代表这个数可以被整除,不是素数
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", i);
count++;
}
}
printf("素数一共%d ", count);
}