C语言/C++常见习题问答集锦(十九)之C语言与漫天飞雪

C语言/C++常见习题问答集锦(十九)之C语言与漫天飞雪

程序之美

在这里插入图片描述

说到雪,还是蛮有感慨的,让我想到了宋代张孝祥的一首<<虞美人·雪花一尺江南北>>
虞美人·雪花一尺江南北
[宋] 张孝祥
雪花一尺江南北。薪尽炊无粟。
老仙活国拭刀圭。十万人家生意、与春回。
天公一笑酬阴德。赐与长生籍。
今朝雪霁寿尊前。看我双亲都是、地行仙。
北京的冬天很少下雪,能来场大雪可谓是件喜人之事。作为一名码农,我们怎么能袖手旁观,撸起袖子加油干,在电脑的世界,在程序的空间,构造出一片迷人的漫天飞雪吧。

言归正传…
用c语言编写一个程序,输出下列雪花图案!

#include "stdio.h"
#define ROW 14
#define COL 12
int main()
{
    char ch[ROW][COL]; //多定义一行一列,0行和0列不用
    int i,j;
    for(i=0;i<ROW;i++)
        for(j=0;j<COL;j++)
            ch[i][j]=' ';      //初始化为空格
 
    ch[1][4]=ch[13][4]='*';
 
    for(i=2;i<=8;i+=2)
    {
        ch[3][i]='*';
        ch[11][i]='*';
    }
    for(i=3;i<=7;i+=2)
    {
        ch[5][i]='*';
        ch[9][i]='*';
    }
    for(i=1;i<=11;i+=2)
    {
        ch[7][i]='*';
    }
 
    printf("  ");
    for(i=1;i<COL;i++)
        printf(" %d",i);
    printf("\n");
    for(i=1;i<ROW;i++)
    {
        printf("%-2d",i);
        for(j=1;j<COL;j++)
        {
            printf(" %c",ch[i][j]);
        }
        printf("\n");
    }
    return 0;
}

在这里插入图片描述

看到这个题目于是想着有没有一个漫天飞雪的程序,于是由于好奇,就在网上一顿乱找,还真的发现有爱好者,已经实现了相关功能,将代码赋值到VS中简单调整后,运行下,效果还可以,和大家分享。如下图:
在这里插入图片描述
源码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

	/*
	* 清除屏幕的shell 命令/控制台命令,还有一些依赖平台的实现
	* 如果定义了 __GNUC__ 就假定是 使用gcc 编译器,为Linux平台
	*    否则 认为是 Window 平台
	*/
#if defined(__GNUC__)
	//下面是依赖 Linux 实现
#include <unistd.h>
#define sleep_ms(m) \
	usleep(m * 1000)

	//向上移动光标函数 Linux
	static void __curup(int height)
{
	int i = -1;
	while (++i<height)
		printf("\033[1A"); //先回到上一行  
}
#else 

	// 创建等待函数 1s 60 帧 相当于 16.7ms => 1帧, 我们取16ms
	// 咱么的这屏幕 推荐 1s 25帧吧 40ms
	// 这里创建等待函数 以毫秒为单位 , 需要依赖操作系统实现
#include <Windows.h>
#define sleep_ms(m) \
	Sleep(m)

	//向上移动光标
	static void __curup(int height)
{
	COORD cr = {0,0};
	// GetStdHandle(STD_OUTPUT_HANDLE) 获取屏幕对象, 设置光标
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cr);
}
#endif /*__GNUC__ 跨平台的代码都很丑陋 */

// 定义初始屏幕的宽高像素宏
#define _INT_WIDTH        (100)
#define _INT_HEIGHT        (50)
// 屏幕刷新帧的速率
#define _INT_FRATE        (40)
// 雪花飘落的速率,相对于 屏幕刷新帧 的倍数
#define _INT_VSNOW        (10)

/*
* 错误处理宏,msg必须是""括起来的字符串常量
* __FILE__        : 文件全路径
* __func__        : 函数名
* __LINE__        : 行数行
* __VA_ARGS__    : 可变参数宏,
* ##表示直接连接, 例如 a##b <=> ab
*/
#define cerr(msg,...) \
	fprintf(stderr, "[%s:%s:%d]" msg "\n",__FILE__,__func__,__LINE__,##__VA_ARGS__);

/*
*  屏幕结构体, 具有 宽高
*  frate  : 绘制一帧的周期, 单位是 毫秒
*  width  : 屏幕的宽,基于窗口的左上角(0,0)
*  height : 屏幕的高
*  pix    : 用一维模拟二维 主要结构如下
*             0 0 0 1 0 0 1 0 1 0
*             0 1 0 1 0 1 0 1 2 0
*             . . .
*             => 0表示没像素, 1表示1个像素,2表示2个像素....
*/
struct screen {
	int frate; // 也可以用 unsigned 结构
	int width;
	int height;
	char *pix;
};

/*
* 创建一个 屏幕结构指针 返回
*
* int frate    : 绘制一帧的周期
* int width    : 屏幕宽度
* int height    : 屏幕高度
* return        : 指向屏幕结构的指针
* */
struct screen* screen_create(int frate, int width, int height);

/*
* 销毁一个 屏幕结构指针, 并为其置空
* struct screen** : 指向 屏幕结构指针的指针, 二级销毁一级的
* */
void screen_destory(struct screen** pscr);

/**
* 屏幕绘制函数,主要生成一个雪花效果
*
* struct screen* : 屏幕数据
* return          : 0表示可以绘制了,1表示图案不变
*/
int screen_draw_snow(struct screen* scr);

/**
* 屏幕绘制动画效果, 绘制雪花动画
*
* struct screen* : 屏幕结构指针
*/
void screen_flash_snow(struct screen* scr);

// 主函数,主业务在此运行
int main(int argc, char *argv[])
{
	struct screen* scr = NULL;

	//创建一个屏幕对象
	scr = screen_create(_INT_FRATE, _INT_WIDTH, _INT_HEIGHT);
	if (NULL == scr)
		exit(EXIT_FAILURE);

	//绘制雪花动画
	screen_flash_snow(scr);

	//销毁这个屏幕对象
	screen_destory(&scr);

	return 0;
}

/*
* 创建一个 屏幕结构指针 返回
*
* int frate    : 绘制一帧的周期
* int width    : 屏幕宽度
* int height    : 屏幕高度
* return        : 指向屏幕结构的指针
* */
struct screen*
	screen_create(int frate, int width, int height)
{
	struct screen *scr = NULL;

	if (frate<0 || width <= 0 || height <= 0) {
//		cerr("[WARNING]check is frate<0 || width<=0 || height<=0 err!");
		return NULL;
	}

	//后面是 为 scr->pix 分配的内存 width*height
	scr = (screen *)malloc(sizeof(struct screen) + sizeof(char)*width*height);
	if (NULL == scr) {
//		cerr("[FATALG]Out of memory!");
		return NULL;
	}
	scr->frate = frate;
	scr->width = width;
	scr->height = height;
	//减少malloc次数,malloc消耗很大,内存泄露呀,内存碎片呀
	scr->pix = ((char *)scr) + sizeof(struct screen);

	return scr;
}

/*
* 销毁一个 屏幕结构指针, 并为其置空
* struct screen** : 指向 屏幕结构指针的指针, 二级销毁一级的
* */
void
	screen_destory(struct screen** pscr)
{
	if (NULL == pscr || NULL == *pscr)
		return;
	free(*pscr);
	// 避免野指针
	*pscr = NULL;
}

//构建开头 的雪花,下面宏表示每 _INT_SHEAD 个步长,一个雪花,需要是2的幂
//static 可以理解为 private, 宏,位操作代码多了确实难读
#define _INT_SHEAD (1<<2)
static void __snow_head(char* snow, int len)
{
	int r = 0;

	//数据需要清空
	memset(snow, 0, len);
	for (;;) {
		//取余一个技巧 2^3 - 1 = 7 => 111 , 并就是取余数
		int t = rand() & (_INT_SHEAD - 1);
		if (r + t >= len)
			break;
		snow[r + t] = 1;
		r += _INT_SHEAD;
	}
}
#undef _INT_SHEAD

//通过 上一个 scr->pix[scr->width*(idx-1)] => scr->pix[scr->width*idx]
//下面的宏 规定 雪花左右摇摆 0 向左一个像素, 1 表示 不变, 2表示向右一个像素
#define _INT_SWING (3)
static void __snow_next(struct screen* scr, int idx)
{
	int width = scr->width;
	char* psnow = scr->pix + width*(idx - 1);
	char* snow = psnow + width;
	int i, j, t; // i索引, j保存下一个瞬间雪花的位置,t 临时补得,解决雪花重叠问题


	//为当前行重置
	memset(snow, 0, width);
	//通过上一次雪花位置 计算下一次雪花位置
	for (i = 0; i<width; ++i) {
		for (t = psnow[i]; t>0; --t) { // 雪花可以重叠
			// rand()%_INT_SWING - 1 表示 雪花 横轴的偏移量,相对上一次位置
			j = i + rand() % _INT_SWING - 1;
			j = j<0 ? width - 1 : j >= width ? 0 : j; // j如果越界了,左边越界让它到右边,右边越界到左边
			++snow[j];
		}
	}
}

/**
* 屏幕绘制函数,主要生成一个雪花效果
*
* struct screen* : 屏幕数据
* return          : 0表示可以绘制了,1表示图案不变
*/
int
	screen_draw_snow(struct screen* scr)
{
	// 静态变量,默认初始化为0,每次都共用
	static int __speed = 0;
	int idx;

	if (++__speed != _INT_VSNOW)
		return 1;

	//下面 就是 到了雪花飘落的时刻了 既 __speed == _INT_VSNOW
	__speed = 0;

	//这里重新构建雪花界面,先构建头部,再从尾部开始构建
	for (idx = scr->height - 1; idx > 0; --idx)
		__snow_next(scr, idx);

	//构建头部
	__snow_head(scr->pix, scr->width);

	return 0;
}

//buf 保存scr 中pix 数据,构建后为 (width+1)*height, 后面宏是雪花图案
#define _CHAR_SNOW '*'
static void __flash_snow_buffer(struct screen* scr, char* buf)
{
	int i, j, rt;
	int height = scr->height, width = scr->width;
	int frate = scr->frate; //刷新的帧频率    

	//每次都等一下
	for (;;sleep_ms(frate)) {
		//开始绘制屏幕
		rt = screen_draw_snow(scr);
		if (rt)
			continue;

		for (i = 0;i<height; ++i) {
			char* snow = scr->pix + i*width;
			for (j = 0; j<width; ++j)
				buf[rt++] = snow[j] ? _CHAR_SNOW : ' ';
			buf[rt++] = '\n';
		}
		buf[rt - 1] = '\0';

		//正式绘制到屏幕上
		puts(buf);

		//清空老屏幕,屏幕光标回到最上面
		__curup(height);
	}
}
#undef _CHAR_SNOW

/**
* 屏幕绘制动画效果, 绘制雪花动画
*
* struct screen* : 屏幕结构指针
*/
void
	screen_flash_snow(struct screen* scr)
{
	char* buf = NULL;
	// 初始化随机数种子,改变雪花轨迹
	srand((unsigned)time(NULL));

	buf = (char*)malloc(sizeof(char)*(scr->width + 1)*scr->height);
	if (NULL == buf) {
//		cerr("[FATAL]Out of memory!");
		exit(EXIT_FAILURE);
	}

	__flash_snow_buffer(scr, buf);

	//1.这里理论上不会执行到这,没加控制器. 2.对于buf=NULL,这种代码 可以省掉,看编程习惯
	free(buf);
	buf = NULL;
}

此代码为跨平台的,用VC和gcc都是可以编译运行的,有兴趣的小伙伴可以将代码拷贝下来,试着运行下,今天在搜找雪花代码的同时,发现python+opencv写出来的雪景更是迷人,有机会了我也整理出来两段和大家分享。
好了,今天就说这么多了,希望小伙伴们能从这篇文章中学到东西,也真心希望能够帮助正在苦学C语言的小伙伴们,你们的成长是我最大的幸福。很感谢您能够在百忙之中浏览我的文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值