【C语言】循环练习和猜数字小游戏

在之前的文中, 我们深入探讨了条件与循环语句的基本概念, 今天我们将围绕这些知识点展开深入学习, 并设计并运行一个简单的猜数字游戏程序作为实践环节, 希望通过这一活动帮助大家巩固所学内容, 提升编程能力
文章目录
- 1. 计算n!值
- 2. 求取1至10所有整数阶乘之和
- 3. 在一个有序排列的数组中定位特定数值
- 4. 编写代码实现多个字符从两端移动并最终汇聚于中心位置
- 5. 模拟用户登录过程,并规定最多三次输入机会
- 6. 猜数字小游戏
-
-
游戏菜单设置
- 随机数设置与游戏流程管理
-
7. 结语
-
如果无聊的话,就来逛逛我的博客栈 吧! 🌹
1.计算n的阶乘
思路:利用循环得到数字,在对其进行累乘。
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
int ret = 1;//不能为0
for(i = 1; i<=n; i++)
{
ret*=i;
}
printf("%d\n", i);
}
代码解释
2.计算1-10的阶乘和
思路1:嵌套的两层循环结构中,外层循环负责计算阶乘数本身(即n!),而内层循环用于累加这些阶乘数的和。为了确保每次外层循环迭代时能够正确获取对应的n!值,在每次外层循环迭代之前必须将临时变量ret清零(即初始化为0)。这样做的目的是为了防止外部操作对ret值产生的干扰影响内层累加结果。
int main()
{
int i = 0;
int n = 1;
int ret = 1;
int sum = 0;
while (n <= 10)
{
ret = 1;//重新赋值为1
for (i = 1; i <= n; i++)
{
ret *= i;//ret的值总被改变
}
sum += ret;
n++;
}
printf("%d\n", sum);
}
代码解释
优化:任何一个数的阶乘也就是前一个数阶乘与当前数相乘的结果进行改进。原先采用的方法需要执行100轮循环操作,在改进后仅需10轮即可完成计算。
#include<stdio.h>
int main()
{
int i = 0;
int n = 1;
int ret = 1;
int sum = 0;
while (n <= 10)
{
ret *= n;
sum += ret;
n++;
}
printf("%d\n", sum);
}
代码解释
3.有序数组中查找具体的某个数字
思路:二分查找
让我们来认识一下二分查找。比如我们在给一件商品定价时经常会采取一种高效的方法来确定价格范围。比如商家告诉我们这件商品的价格区间是在1到100之间,并非从这个范围内逐一排查可能的价格点。我们会采用更高效的方式并首先选择区间的中间位置作为起点。然后根据商家反馈的结果继续缩小范围从而更快地确定出正确的价格值这就是二分查找的基本思路。
以上就是对"二分查找"这一算法思想的具体阐述以及其实现过程简要说明希望这对大家有所帮助!
1.第一次查找

区间设为left=0至right=9。
中间元素的索引mid计算如下:mid=(left+right)/2=4。
由于中间元素位于第4位(索引值为4),其值小于第6位(索引值为5)对应的数值7,则可推断目标元素比当前中间值更大。
更新左边界为mid+1的位置即为left=5。
重新确定新的搜索区间。
2.第二次查找

区间范围设定为left=5和right=9。
中间元素的位置由mid=(left+right)/2计算得出为7。
若中间位置mid对应的值大于所需查找的目标值,则需将查找范围调整至左半部分。
右边界更新为right=mid-1即6。
重新确定查找区间。
3.第三次查找

区间设定为left=5和right=6。
计算中间元素的索引mid=(left+right)/2得到值为5。
当mid的位置小于等于右边界时:如果要查找的目标元素位于中间元素左侧,则需要将右边界调整至中间索引位置。
否则将左边界更新为中间索引位置加一。在本例中:
计算得mid=5
比较发现目标元素小于当前中间元素(因为7位于索引位置6),因此需要将右边界调整到右侧区域。
更新左边界至mid+1即为位置6。此时需要重新调整搜索区间进行后续比较。
4.第四次查找

- 范围确定为 left=6 至 right=6
- 中间元素的位置由公式计算得出 mid=(left+right)/2 的结果
- 计算得中间索引位置对应的数值为7
- 中间索引位置处的值与目标值相同
- 查找操作完成
在该查找过程中总共进行了四次查询,在时间上相较于逐项遍历所有元素所需的十次查询而言更加高效。然而该方法仍存在局限性,在应用时必须针对有序数组进行操作。
根据题目,实现代码:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int k = 7;
int left = 0;//左下标
int right = sz - 1;//右下标
while (left <= right)//注意等于号
{
int mid = (left + right) / 2;//中间元素下标
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("找到了,下标是%d\n", mid);
break;
}
}
//注意break,避免低级错误
if (left > right)
printf("找不到\n");
return 0;
}
代码解释
注意点:
应确保起始位置的left和right索引正确设置;
while循环须依据特定条件进行迭代;
在循环过程中需动态调整中间元素的索引位置,并反复计算其值;
若在循环过程中使用break终止,则应在后续逻辑中避免直接输出结果。
计算平均值的优化:
int mid = left + (right - left) / 2;
代码解释
4. 编写代码,演示多个字符从两端移动,向中间汇聚
题目大意:hello-0w0-anduin!比如上面提到的字符串,请按照从全被#覆盖的形式逐渐向中心位置汇聚以最终显示整个字符串
思路:为了整合两个字符串的内容,在每次迭代中将当前段的第一字符与最后一字符添加到一个全由#标记的新字符串中,并采用循环结构实现两段文本的合并;直到合并过程结束。
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "hello-0w0-anduin!";
char str2[] = "#################";
int len = strlen(str1);
int left = 0;
int right = len - 1;
while(left <= right)
{
str2[left] = str1[left];
str2[right] = str1[right];
printf("%s\n",str2);
left++;
right--;
}
return 0;
}
代码解释

5. 模拟用户登录,输入限制三次
该思路基于strcmp函数来进行字符串比较操作。通过将目标密码与用户输入进行对比分析以确定其有效性。如果用户的初始尝试获得成功反馈,则应立即确认输入无误;如果连续三次输入均不正确,则应分别给出错误提示并终止程序运行。本题设定的初始密码为:"exploreranduin"。
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
char password[50] = { 0 };
for(i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", password);
if(strcmp(password,"exploreranduin")==0)
{
printf("输入正确!\n");
break;
}
else
{
printf("密码错误,请重新输入!\n");
}
}
//正确 or 错误
if(i==3)
{
printf("三次密码均错误,退出程序!\n");
}
return 0;
}
代码解释

6. 猜数字小游戏
题目概述:
电脑随机生成一个数字(1~100);
参与者在玩数字猜测游戏时,若其猜测结果为较小的数字,则会收到提示"猜测结果偏低";若其猜测结果为较大的数字,则会收到提示"猜测结果偏高";直到其猜测正确为止。
游戏可以一直玩。
思路:
- 布置菜单
- 随机数的设置
- 游戏过程
布置菜单
函数形式允许用户选择1/0,并在main函数中配置相应的参数;从题目的要求来看,游戏必须运行至少一次,并且采用do...while循环来实现这一功能。
表现形式:
#include<stdio.h>
//菜单函数
void menu()
{
printf("**********************************\n");
printf("*********** 1.play ***************\n");
printf("*********** 0.exit ***************\n");
printf("**********************************\n");
}
int main()
{
int input = 0;
//选择输入
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;
}
代码解释
随机数的设置
所需工具 : rand、srand、时间戳、time
rand - 随机数的生成

转到定义观察RAND_MAX的值:32767
rand返回值范围:0~32767

定义区域 - game函数内
表现形式:
#include<stdlib.h>
void game()
{
int ret = rand();
printf("%d\n", ret);
}
代码解释
结合菜单部分代码并运行查看效果:
运行2次(由于结果相同,只贴第一次结果):

仅凭 rand 函数在每一次运行中产生的结果都是一致的。
一旦玩家发现这些规律后,则会导致游戏的意义不再存在。
这引出了一个问题:如何才能使 rand 函数在每一次运行中都能生成不同的随机数序列?
观察一下rand函数的一段描述:

在之前的讨论中提到了srand函数的作用及其在生成伪随机数方面的应用。如果您还想深入了解这一机制的运作原理和实际应用价值,请继续阅读我们的详细解析。
srand - 结合rand生成不固定的随机数

表现形式:
#include<stdlib.h>
void game()
{
srand(100);
//srand(200);
int ret = rand();
printf("%d\n",ret);
}
代码解释
当srand接收的输入数据发生变化时,在同一个代码块中相应地生成的随机数值也会随之变化;因为每次传递给它的输入数据保持不变的原因,因此生成的随机数值也会保持一致。
结合菜单部分代码运行查看效果:
srand(100):

srand(200):

为了使srand生成符合预期的随机数值,在调用该函数之前必须传递一个无符号整型参数;然而我们最初的目的并非依赖外部输入来决定其运行方式;具体来说,在游戏结束时;每次运行程序都会产生不同的数值;这样可以确保每个运行周期内产生的数值各不相同;这有助于提升整体系统的稳定性与一致性;因此我们需要引入一种能够持续更新的状态变量
我们了解随着时间不断流逝的变化规律,请问是否可以用时间来作为这个随机值的替代品呢?答案是可以肯定的,请注意这个随机值被称为时间戳。
时间戳 - 向srand提供随时变化的随机值
概念:当前时间和计算机起始时间(1970年1月1日0时0分0秒)之间的差值
time - 接收时间戳
表现形式:

观察返回值time_t的类型:

注意点:
time函数始终返回一个整数值。为了满足srand函数的需求,在调用该函数之前必须为其提供一个无符号整数值。通过显式类型转换操作就可以轻松实现这一目标。由于srand函数在程序运行期间不会频繁调用(一般只会被调用一次),因此我们可以将其定义放置在main函数体中以提高代码效率。
随机函数返回值在零到三万二千七百六十七之间。对于游戏来说,这意味着提升难度。因此我们可以通过限定数据在零点一到一百之间来增加游戏的亲和力。而实现这一目标的方法是让ret接收的数据取模一百后再加一(因为任何数模一百的结果都在零到九十九之间)。
结合以上两点,核心代码表现形式为:
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
void game()
{
int ret = rand()%100 + 1;
printf("%d\n", ret);
}
int main()
{
srand((unsigned int)time(NULL));
return 0;
}
代码解释
结合其余代码运行结果:

到此,随机数的生成问题就解决了。
游戏过程
要点:
- 游戏具有连贯性,并且循环必须持续不断进行
- 多路条件判断
- 猜测正确时需设立出口以终止猜测过程;切勿无限地反复猜测
表现形式:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//游戏实现
void game()
{
int guess = 0;//输出猜测值
int ret = rand() % 100 + 1;//生成一个随机数
//猜数字
while (1)
{
printf("请输入数字:>");
scanf("%d", &guess);
if (guess > ret)
{
printf("猜大了\n");
}
else if (guess < ret)
{
printf("猜小了\n");
}
else
{
printf("恭喜你,猜对了!\n");
break;//注意游戏结束
}
}
代码解释
完整游戏展示:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu()
{
printf("**********************************\n");
printf("*********** 1.play ***************\n");
printf("*********** 0.exit ***************\n");
printf("**********************************\n");
}
void game()
{
int guess = 0;
int ret = rand() % 100 + 1;
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 1:
game();
break;
case 0:
break;
default:
printf("选择错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
代码解释
运行结果:

7. 结语
下面将介绍循环练习及猜数字小游戏的完整玩法。在本次讨论中关于分支与循环的内容已全部涵盖,在本章的学习后相信您已经掌握了相关知识。之后anduin将恢复正常的更新频率,更多深度分析即将呈现。
如果看完之后如您觉得本文内容值得参考,请别忘了点赞、评论与收藏。
希望通过本文能让您对相关技术有更深入的理解。

