“花会沿途盛开,以后的路也是。” 今天我们一起来继续学完分支语句和循环语句。
分支和循环
- 3.循环语句
- 3.4 do...while()循环
- 3.4.1 do语句的用法
- 3.5关于循环的一些练习
- 3.6 goto语句
3.循环语句
3.4 do…while()循环
3.4.1 do语句的用法
do
循环语句;//当循环语句不止一句的时候,要用大括号括起来
while(表达式);
在前面,我们学过了用while循环
和for循环
在屏幕上打印1~10的数字,那么在do...while()循环
中,如何实现这步操作呢?
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
接下来,让我们用一张图了解一下do...while()循环
的执行流程:
do语句
的特点:循环至少执行一次,使用的场景有限,所以不是经常使用。
同样的,在do…while()循环中,也存在break和continue语句。接下来,让我们用示例看看用法是否一致?
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (i == 5)
break;//遇到break循环终止,因此屏幕上只打印出1~4
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (i == 5)
continue;//遇到continue,跳过本次循环后面的代码
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
通过运行,我们可以发现程序进入了死循环,光标在4的后面一直闪烁。
总结:
do...while循环
中的break
和continue
和在while循环
中一模一样。break
都是用来终止循环的,continue
是跳过本次循环后面的代码,直接去判断部分。
3.5关于循环的一些练习
下面让我们一起下一些关于循环的练习:
1.计算n的阶乘
//计算n的阶乘
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int n = 0;
int i = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)//循环产生1~n的数字
{
ret = ret * i;
}
printf("n! = %d", ret);
}
2.计算1!+2!+3!+4!+5!+6!+7!+8!+9!+10!
//计算1!+2!+3!+4!+5!+6!+7!+8!+9!+10!
#include <stdio.h>
int main()
{
int n = 0;
int i = 0;
int ret = 1;
int sum = 0;
for (n = 1; n <= 10; n++)
{
ret = 1;//要让ret每次都从1开始
for (i = 1; i <= n; i++)//循环产生1~n的数字
{
ret = ret * i;
}
sum = ret + sum;
}
printf("%d\n", sum);
return 0;
}
我们可以发现,这段代码相对来说还是比较复杂的,每次都得先把n!算出来,然后在相加,每次都要从1开始乘到n,但实际上我们可以发现后一个数的阶乘等于前一个数的阶乘乘以这个数本身就行了,这样一来,简化了计算过程。那么,我们一起把代码优化一下吧!
#include <stdio.h>
int main()
{
int i = 0;
int ret = 1;
int sum = 0;
for (i = 1; i <= 10; i++)
{
ret = ret * i;
sum = ret + sum;
}
printf("%d\n", sum);
return 0;
}
我们可以发现,这两段代码的结果相同,后者更优!因此,我们在写代码的时候,不光要看怎么能写出代码,还需要思考怎么样写才是最简便的。
3.在一个有序数组中查找具体的某个数字n,如果有,打印出它的下标,没有的话,给出提示。
当我们看到这个题目的第一眼,可能大多数人的想法就是从左向右一个一个寻找,找到的话就打印出下标,找不到就给出提示,这种方法我们也叫做遍历查找。假设我们要从1~10的十个数中找出6,那么该如何写这段代码呢?
//假设我们要从1~10的十个数中找出6,那么该如何写这段代码呢?有的话给出下标,没有的话给出提示
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int n = 0;
int flag = 0;
scanf("%d", &n);
for (int i = 0; i <= 9; i++)
{
if (arr[i] == n)
{
printf("找到了,下标是: % d", i);
flag = 1;
break;
}
}
if (flag == 0)
{
printf("没找到!\n");
}
return 0;
}
虽然这种方法实现了我们想要的结果,但是我们发现这种算法比较复杂,如果我们现在要寻找的是10.那么这个循环将要走10次,那么如果是一个更大的数呢?比方说:妈妈带着小明去买了一双鞋,小明说这双鞋在600元以内,那么这个时候让我们来猜这双鞋是多少钱,难道我们需要从1开始一直查找到600吗?显然,计算量是相当大的,对于这种有序数组中某个元素的查找,我们这个给大家介绍一个新的方法,叫做折半查找,又称二分法。
下面,我们给出用二分查找解决上述问题的示例先帮助大家了解一下二分查找的过程。
我们可以发现
二分查找
的效率很高,但是条件苛刻,只针对有序的数组中。
下面,我们给出详细代码:
//二分查找
#include <stdio.h>
int main()
{
int left = 0;
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int right = sizeof(arr)/sizeof(arr[0])-1; //sizeof(arr)算的是数组的总大小,单位是字节
int mid = 0;
int flag = 0;
int n = 0;
scanf("%d", &n);
while (left <= right)
{
mid = (right + left)/2;
if (arr[mid] == n)
{
printf("下标为:%d", mid);
flag = 1;
break;
}
else if (arr[mid] > n)
{
right = mid - 1;
}
else if (arr[mid] < n)
{
left = mid + 1;
}
}
if (flag == 0)
{
printf("没找到!\n");
}
return 0;
}
4.编写代码,演示多个字符从两端移动,向中间汇聚。
假设我们这里需要实现Hello C World !!!的字符从两端移动,往中间汇聚,该如何实现呢?
#define _CRT_SECURE_NO_WARNINGS 1
//编写代码,演示多个字符从两端移动,向中间汇聚。Hello C World !!!!!!
#include <stdio.h>
#include <string.h>//使用strlen需要添加的头文件
#include <windows.h>//使用system("cls");需要添加的头文件
int main()
{
char arr1[] = "Hello C World !!!!!!";
char arr2[] = "********************";
int left = 0;
int right = strlen(arr2) - 1;//此时是数组元素是字符串,而不是一个个字符,通过前面的学习我们知道了字符串结束标志为'\0',但strlen计算的是除了'\0'以外的字符长度,因此-1即得到最右边数组的下标
//int right = sizeof(arr2) / sizeof(arr2[0]) - 2;当然使用sizeof也是可以的,不过此时我们要进行减2的操作
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//等待1秒
system("cls");//清空屏幕
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
5.编写代码实现,模拟用户登录情景,并且只能登录三次(如果正确,显示登录成功,如果错误,退出程序。)
//编写代码实现,模拟用户登录情景,并且只能登录三次(如果正确,显示登录成功,如果错误,退出程序。)
#include <stdio.h>
#include <string.h>//strcmp需要包含该头文件
int main()
{
char arr[20] = { 0 };
char password[] = "123456";
int i;
for (i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", arr);
//strcmp函数是比较字符串的大小,如果>,则返回>0的值,如果<,则返回<0的值,如果相等,则返回0
if (strcmp(arr, password) == 0)
{
printf("登录成功!\n");
break;
}
else
printf("密码错误!\n");
}
if (i == 3)
{
printf("三次密码均输入错误,退出程序!\n");
}
return 0;
}
6.猜数字游戏-电脑随机产生1~100之间的一个数,接下来,我们猜数字,如果小了,我们给出提示猜小了,如果猜大了我们给出提示猜大了,如果猜对了,那么恭喜你猜对了,游戏结束。
这里,我们需要先给大家介绍一下产生随机数的函数rand()
上述资料中,我们知道想要使用rand()
函数,我们需要添加头文件#include <stdlib.h>
我们可以先通过简单的代码看看rand()
的功能
#include <stdio.h>
#include <stdlib.h>
int main()
{
int ret = rand();
printf("%d", ret);
return 0;
}
经过调试运行,我们发现rand()
确实能够产生一个随机数,但是多次调试我们又发现,每次产生的随机数都是41,那这就不能达到我们期望的结果。
查阅资料,我们可以发现
rand()函数
是通过某种算法生成的随机数,因此它还不够随机,不能达到我们预期的结果。rand()函数
生成的是伪随机数,且其生成的随机数的范围是:0~RAND_MAX. 通过查找我们可以发现RAND_MAX的值为32767。rand()函数
在生成随机数之前,需要通过srand()函数
设置随机数的生成器。
大家肯定又在想srand()函数又是干什么的呢?这里,我们带大家一起了解一下:
下面我们通过代码给大家进行演示:
#include <stdio.h>
#include <stdlib.h>
int main()
{
srand(1);
//srand(2);
int ret = rand();
printf("%d", ret);
return 0;
}
我们可以发现
rand()
是一个产生随机数的函数,而srand
是一个设置随机数种子的函数,通常这两个函数是一起使用的,来完成产生随机数的功能。srand函数
通过参数seed
来改变系统提供的种子值,从而可以使得每次调用rand函数
生成的伪随机数序列不同,从而实现真正意义上的“随机”。
这里,我们介绍一下时间戳,通过百度,我们可以对时间戳有所了解:
我们可以发现,只要时间在变化,那么其所对应的时间戳就在变化,那么我们如何在C语言中获得时间戳呢,这里我们需要给大家介绍一下time()函数
。
我们可以发现time()函数
还存在一个参数,会返回时间戳,但是它的参数是一个指针,我们可以通过参数带回时间戳,这里我们不想用的话,可以用NULL
(0)这个空指针来代替。我们发现time()函数
的类型是time_t
,但是srand()
所需类型是unsigned int
,这里我们就可以进行强制类型转换。
srand((unsigned)time(NULL));
这条指令的意思是利用系统时间来初始化系统随机数的种子值,使得每次运行由于时间不同产生而产生不同的随机数序列。如果想在一个程序中生成随机数序列,只需要在主程序开始处调用
srand((unsigned)time(NULL));
后面直接用rand()函数
就可以了。
接下里,我们就一起来写一下猜数字的游戏吧!
//猜数字游戏
#include <stdio.h>
#include <stdlib.h>//srand()函数和rand()函数所需要的头文件
#include <time.h>//time()函数所需要的头文件
void menu()
{
printf("********************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("********************************\n");
}
//1.生成1~100的随机数
//2.猜数字
void game()
{
int guess = 0;
int ret = rand() % 100 + 1;//生成1~100的随机数,任何一个数%100等于0~99,加上1,就是1~100.
while (1)
{
printf("请猜数字:");
scanf("%d", &guess);
if (guess > ret)
{
printf("猜大了!\n");
}
else if (guess < ret)
{
printf("猜小了!\n");
}
else
{
printf("恭喜你猜对了!\n");
break;
}
};
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//利用系统时间来初始化系统随机数的种子值,使得每次运行由于时间不同产生而产生不同的随机数序列。
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("game over!\n");
break;
case 1:
printf("game start!\n");
game();
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
3.6 goto语句
C语言中,提供了可以随意滥用的
goto语句
和标记跳转的标号。从理论上goto语句
是没有必要的,实践中没有goto语句也可以很容易的写出代码。但在某种场合下,goto语句
还是用得着的,最常见的用法就是终止程序在某种深度嵌套的结构的处理过程。例如:一次跳出两层或多层循环。多层循环这种情况使用break
是达不到目的的,它只能从最内层循环退出到上一层循环。
goto语言真正适合的场景如下:
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
...
error:
if(disaster)
//处理错误情况
下面呢,我们给出goto语句
的示例:
//goto语句
#include <stdio.h>
int main()
{
printf("hehe!\n");
printf("haha!\n");
goto end;
printf("heihei!\n");
end:
printf("hihi!\n");
return 0;
}
注:
goto函数
只能在同一个函数内部跳转,不能跨函数
下面,我们也给出一个用goto语句写出关机程序的例子:(该程序仅供学习使用,不要过度整蛊同学!)
//关机程序
#include <stdio.h>
int main()
{
char input[10] = { 0 };
system("shutdown -s -t 60");
again:
printf("你的电脑将在1分钟内关机,如果输入:我是猪,则可以取消关机程序!\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
printf("关机程序已经取消!\n");
}
else
goto again;
return 0;
}
好啦,关于分支和循环到这里就完结啦,欢迎大家关注、点赞和评论!