C语言趣味代码(一)

C语言相关知识点的博客和大家分享完了,接下来我想开始数据结构相关的博客,在此之前呢,有的小伙伴问过我学完C语言的相关知识,我能干些什么呢?只有刷题吗?这不禁让我反思:在我们学习的过程中,我们所有学习时用到的书籍都是围绕某一知识点介绍、拓展,千篇一律的示例代码,好像从没出现一些新颖的代码。所以我想用3篇左右的博客跟大家分享一下用我们学过的知识实现的有趣代码,然后再开始后面的数据结构的博客。

1. 心算训练

什么是心算呢?心算是 一种在不使用纸张或任何其他非思维工具的情况下解决数学问题的技能。心算能力是指仅用头脑进行数学计算的能力。无需计算器等外部工具的帮助。虽然在现代世界中这似乎是一项无用的技能,但它实际上非常有用,并且对于提高解决问题和决策的技能大有帮助。下面我们先介绍可能会用到的相关函数,然后再进行实现。

1.1 操纵时间

首先我们编写一个能够操纵时间,显示动态画面的程序:

cpp 复制代码
#include<stdio.h>
#include<time.h>
int sleep(unsigned long x)
{
	clock_t c1 = clock(), c2;
	do 
	{
		if ((c2 = clock()) == (clock_t)-1)
		{
			return 0;
		}
	} while (1000.0 * (c2 - c1) / CLOCKS_PER_SEC<x);
	return 1;
}
int main()
{
	int i;
	clock_t c;
	for (i = 10; i > 0; i--)
	{
		printf("\r%2d", i);
		fflush(stdout);
		sleep(1000);
	}
	printf("\r\aEND! \n");
	c = clock();
	printf("程序从开始到结束运行了%.1f秒\n", (double)c / CLOCKS_PER_SEC);
	return 0;
}

因为我不太会电脑录制,就给大家看一下最终结果吧,感兴趣的可以自己试试。

在这个程序中我们使用了clock函数用来求程序开始运行到结束所花费的时间。

|-----|----------------------------------------------------------------------------------------------------------------------------------|
| 函数名 | clock函数 |
| 头文件 | #include<time.h> |
| 格式 | clock_t clock(void) |
| 功能 | 求处理器调用某个进程所花费的时间 |
| 返回值 | 从定义与程序启动相关的编程环境的时间点起,用处理系统的最佳值逼近返回程序占用处理器的时间。为了以秒为计量单位,必须用本函数的返回值除以CLOCK_FER_SEC宏的值,如果无法获取处理器调用该进程花费的时间,或无法显示数值是,就返回(clock_t)-1。 |

可以使用<time.h>头文件吧返回值的类型clock_t型定义成等同于算术类型,clock_t主要等同于那种类型(unsigned,unsigned long....)要取决于编程环境。在某些环境中clock_t型并不等同于unsigned型,程序在这些编程环境中,比一定能顺畅运行。除此之外,因为不同的硬件和OS会导致时钟的精准度也不同,所以clock_t型表示数值的单位也取决于编程环境,因此通常用<time.h>头文件定义CLOCK_FER_SEC这一对象宏,CLOCK_FER_SEC直接翻译过来就是"每秒的时钟数"。如果时钟的精度在0.001秒,那么一秒内就有1000个时钟,这个宏的值就定义为1000。

cpp 复制代码
#define CLOCK_FER_SRC 1000//定义示例,根据编程环境的不同有所差距

在上面的程序中,用一位小数的实数值,表示程序启动后花费的时间。

cpp 复制代码
c = clock();
printf("程序从开始到结束运行了%.1f秒\n", (double)c / CLOCKS_PER_SEC);

上面这部分代码把c强制类型转换成了double型,来求出经过的秒数,这是因为"整数/整数"的运算中会舍去小数部分。另外,因为clock函数返回的值只用于求经过的时间,所以不加入变量c也能实现(这样一来变量c也就可以省去了):

cpp 复制代码
c = clock();
printf("程序从开始到结束运行了%.1f秒\n", (double)clock() / CLOCKS_PER_SEC);

讲了如何计算程序启动后经过的时间,下面来教大家如何计算**处理特定部分所需要的时间。因为程序不接受错误的答案,所以在回答正确之前程序都不会改变。**我们试着实现一下:提示的问题是要把三个三位数的整数相加,输出答案后,画面上会显示解题共花费了多长时间:

cpp 复制代码
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
int main()
{
	int a, b, c;//要进行加法运算的数值
	int x;//读取的值
	clock_t start, end;//开始时间和结束时间
	double req_time;//所需时间
	srand(time(NULL));
	a = 100 + rand() % 900;
	b = 100 + rand() % 900;
	c = 100 + rand() % 900;
	printf("%d+%d+%d的计算结果是多少:", a, b, c);
	start = clock();
	while (1)
	{
		scanf("%d", &x);
		if (x == a + b + c)
			break;
		printf("\a回答错误!\n请重新输入");
	}
	end = clock();
	req_time = (double)(end - start) / CLOCKS_PER_SEC;
	printf("用时%.1f秒", req_time);
	if (req_time > 30.0)
		printf("花费时间太长了\n");
	if (req_time > 15.0)
		printf("还不错\n");
	else
		printf("好厉害\n");
	return 0;
}

我们运行一下来看看吧(我算的有点慢):

这里面的while语句是为了不接受错误答案而设置的循环,因为控制表达式的是1,所以循环会一直执行下去,当回答正确时,程序会通过break语句强行中断while循环。如果不想使用brak语句,也可以使用下面的do..while语句来中断循环:

cpp 复制代码
do
{
  scanf("%d",&x);
  if(x!=a+b+c)
    printf("\a回答错误!\n请重新输入:");
}while(x!=a+b+c);

在上面的代码中,为了将处理暂停一段时间,我们使用了sleep函数:

cpp 复制代码
int sleep(unsigned long x)
{
	clock_t c1 = clock(), c2;
	do
	{
		if ((c2 = clock()) == (clock_t)-1)
		{
			return 0;
		}
	} while (1000.0 * (c2 - c1) / CLOCKS_PER_SEC < x);
	return 1;
}

首先程序一开始调用了clock函数,把程序启动后经过的时间存入c1,然后运行do语句,在每次循环时调用clock函数,把获取的数值赋给c2。程序中while语句括号里面会以毫秒为单位表示sleep函数开始运行后经过的时间,当这个数值大于等于x时,通过do语句进行的循环就结束了。上文所说,clock函数"如果无法获取处理器调用该进程花费的时间,或无法显示数值是,就返回(clock_t)-1"。这里是把int型的整数值-1强制类型转换成clock_t类型后的值,绝不是"clock_t-1后的值"。当clock函数返回表示错误的-1时,sleep函数会通过返回0来通知调用发生了问题。注意:sleep函数会占用CPU,与其说"运行函数花了多长时间",但不如说"sleep函数让CPU这个大脑持续运行了多少时间"。

1.2 最终实现

这是一个把3个3位数连续相加的心算训练程序,程序最后会显示进行10次心算所需要的时间:

cpp 复制代码
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
int main()
{
	int stage;//次数
	int a, b, c;
	int x;
	int n;//空白的宽度
	clock_t start, end;
	srand(time(NULL));
	printf("心算训练开始!\n");
	start = clock();
	for (stage = 0; stage < 10; stage++)
	{
		a = 100 + rand() % 900;
		b = 100 + rand() % 900;
		c = 100 + rand() % 900;
		n = rand() % 15;
		printf("%d%*s+%*s%d%*s+%*s%d=", a, n, "", n, "", b, n, "", n, "", c);
		do
		{
			scanf_s("%d", &x);
			if (x == a + b + c)
				break;
			printf("\a回答错误!\n请重新回答");
		} while (1);
	}
	end = clock();
	printf("一共用时%.1f秒", (double)(end - start) / CLOCKS_PER_SEC);
	return 0;
}

其中,我们把空白字符的个数n定位0~14的随机数。我们命令printf函数"至少用n位数表示空的字符串",程序就会显示n个空白字符串。数值间留有空白,可以训练我们的心算能力。

2. 猜拳游戏

现在我们要编写一个提供两位玩家对战的"猜拳游戏"。当然,这里所说的"两位玩家"是指计算机和玩家,即游戏采用人机对战的模式。:

2.1 流程分析

  1. 确定计算机要出的手势。
  2. 显示"石头剪刀布",然后玩家输入自己要出的手势。
  3. 进行输赢判断。,显示结果。
  4. 询问是否继续,如果玩家希望继续,就回到1。

下面我们详细设计一下各个步骤:

1.用随机数确定计算机所出的手势:

之所以要先确定计算机出的手势,再读取玩家的手势,是为了避免计算机作弊。

2.如果用"石头","剪刀","布"的字符串来进行手势输入,可能会产生输入错误,例如一不小心打错字,变成"势头","见到"等。因此我们把"石头","剪刀","布"这三个手势分别对应数字0,1,2(类型设为int型)。如果玩家的手势和计算机的手势能用相同的数值表示出来,就保持了一致性,会方便,这样一来,也确定了在1的设计中未解决的手势的数值。

3.根据计算机和玩家的手势判断胜负:

此处使用变量human和comp来分别表示玩家和计算机的手势,下面为手势和胜负的关系:

平局情况:

|-------|------|------------|------------------|
| human | comp | human-comp | (human-comp+3)%3 |
| 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 |
| 2 | 2 | 0 | 0 |

玩家失败情况:

|-------|------|------------|------------------|
| human | comp | human-comp | (human-comp+3)%3 |
| 0 | 2 | -2 | 1 |
| 1 | 0 | 1 | 1 |
| 2 | 1 | 1 | 1 |

玩家胜利的情况:

|-------|------|------------|------------------|
| human | comp | human-comp | (human-comp+3)%3 |
| 0 | 1 | -1 | 2 |
| 1 | 2 | -1 | 2 |
| 2 | 0 | 2 | 2 |

上面的3个表格中汇总了双方的手势数值、human减去comp后的值、判断表达式(human-comp+3)%3的值。如果human和comp的值相等就算作"平局",此时human-comp的值为0。失败和胜利的情况同理。

2.2 程序的实现

2.2.1 用switch语句实现

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
	int human;//玩家的手势
	int comp;//电脑的手势
	int judge;//胜负
	int retry;//再来一次?
	srand(time(NULL));
	printf("猜拳游戏开始!\n");
	do
	{
		comp = rand() % 3;
		printf("\n\a石头剪刀布   (0)石头(1)剪刀(2)布:");
		scanf("%d", &human);
		printf("我出的是:");//显示计算机出的手势
		switch (comp)
		{
		   case 0:
			   printf("石头");
			   break;
		   case 1:
			   printf("剪刀");
			   break;
		   case 2:
			   printf("布");
			   break;
		}
		printf("。\n");
		judge = (human - comp + 3) % 3;
		switch (judge)
		{
		   case 0:
			   printf("平局");
			   break;
		   case 1:
			   printf("你输了");
			   break;
		   case 2:
			   printf("你赢了");
			   break;
		}
		printf("还要再来一次吗?(0)否(1)是");
		scanf("%d", &retry);
	} while (retry == 1);
	return 0;
}

我们来运行看一下:

2.2.2 让计算机"后出"

为了让计算机赢,我们可以让计算机比玩家后出。具体实现代码 :

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
	int i;
	int human;
	int comp;
	int judge;
	int retry;
	char* hd[] = { "石头","剪刀","布" };
	printf("猜拳游戏开始!\n");
	do
	{
		do 
		{
			printf("\n\a石头剪刀布   ");
			for (i = 0; i < 3; i++)
				printf("(%d)%s", i, hd[i]);
			printf(":");
			scanf("%d", &human);
		} while (human < 0 || human>2);
		comp = (human + 2) % 3;//计算机后出
		printf("我出%s,你出%s.\n", hd[comp], hd[human]);
		judge = (human - comp + 3) % 3;
		switch (judge)
		{
			  case 0:
				printf("平局\n");
				break;
		      case 1:
			    printf("你输了\n");
				break;
			  case 2:
				printf("你赢了\n");
				break;
		}
		printf("再来一次吗?   (0)否(1)是");
		scanf("%d", &retry);
	} while (retry == 1);
	return 0;
}

运行一下看看结果:

这个程序可以拿给朋友玩,在旁边看他一直输。

相关推荐
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇26】C#面向对象动态多态——接口(Interface)、接口里氏替换原则、密封方法(`sealed` )
java·开发语言·unity·c#·游戏引擎·里氏替换原则
@菜鸟进阶记@1 小时前
java根据Word模板实现动态填充导出
java·开发语言
卖芒果的潇洒农民1 小时前
Lecture 6 Isolation & System Call Entry
java·开发语言
AAA.建材批发刘哥1 小时前
Linux快速入门-Linux文件系统管理
linux·运维·服务器·c语言·学习方法
SomeB1oody2 小时前
【Rust自学】6.1. 定义枚举
开发语言·后端·rust
AC使者2 小时前
5820 丰富的周日生活
数据结构·算法
SomeB1oody2 小时前
【Rust自学】5.3. struct的方法(Method)
开发语言·后端·rust
cwj&xyp2 小时前
Python(二)str、list、tuple、dict、set
前端·python·算法
Kisorge3 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
轻口味4 小时前
命名空间与模块化概述
开发语言·前端·javascript