⽤c语⾔写代码_编程代码:⽤C语⾔来实现下雪效果,这个冬
天,雪花很美
fprintf作用前⾔
1.本⽂主要围绕 如何 在 控制台上 下起 ⼀场 只有⾃⼰能看见的雪
2.是个简易跨平台的,主要是C语⾔
3.动画 采⽤ 1s 40帧, 雪花具有 x轴速度和y轴速度
4.⽐较简单,可以给学⽣作为C语⾔结课作业吧.
正⽂
1.1 先简单处理跨平台
本⽂写作动机,还是感谢⼀下⼤学的启蒙⽼师,让我知道了有条路叫做程序员,可以作为⼯作⽣存下去.那就上代码了.
⾸先代码定位 是 ⾯向 简单跨平台,⾄少让 gcc 和 vs 能够跑起来.
其实跨平台都是嚼, 说⽩了就是⼀些丑陋的宏. 真希望所有系统合⼆为⼀,采⽤统⼀的标准api 设计,但这是不可能的,就相当于很早之前的电视制式⼀样.
那么我们先看 围绕跨平台的宏
#include #include #include #include /** 时间 : 2015年12⽉26⽇11:43:22
* 描述 : 应该算过节吧,今天,写了个雪花特效代码,
* *//** 清除屏幕的shell 命令/控制台命令,还有⼀些依赖平台的实现
* 如果定义了 __GNUC__ 就假定是使⽤gcc 编译器,为Linux平台
* 否则认为是 Window 平台*/#ifdefined(__GNUC__)//下⾯是依赖 Linux 实现#include #definesleep_ms(m) usleep(m *1000)//向上移动光标函数 Linuxstaticvo {
inti = -1;
while(++i
printf("033[1A");//先回到上⼀⾏ }#else// 创建等待函数 1s 60 帧相当于 16.7ms => 1帧, 我们取16ms// 咱么的这屏幕推荐 1s 25帧吧 40ms// 这⾥创建等待函数以{
COORD cr = {0,0};
// GetStdHandle(STD_OUTPUT_HANDLE) 获取屏幕对象,设置光标 SetConsoleCursorPosition(G
etStdHandle(STD_OUTPUT_HANDLE), cr);
}#endif/*__GNUC__ 跨平台的代码都很丑陋 */
⾸先是 sleep_ms 这个宏, 传⼊⼀个毫秒数,让操作系统等待.
对于__curup 实现的不好. 功能是 让 控制台当前光标移动到 上⾯的 height 位置,对于 window直接移动到第⼀⾏(0,0)位置.
上⾯⼀共⽤了 5个头⽂件 还是容易的代码. string.h 主要⽤的是 memset 函数, 让⼀段内存初始化,⽤0填充.
对于time.h 主要是为了 初始化时间种⼦,⽅便每次运⾏都不⼀样.
// 初始化随机数种⼦,改变雪花轨迹srand((unsigned)time(NULL));
1.2 再说主业务代码
这⾥程序员运⾏的主业务,先说⼀说这⾥⽤的数据结构 如下
// 定义初始屏幕的宽⾼像素宏#define_INT_WIDTH (100)#define_INT_HEIGHT (50)// 屏幕刷
新帧的速率#define_INT_FRATE (40)// 雪花飘落的速率,相* __FILE__ : ⽂件全路径
* __func__ : 函数名
* __LINE__ : ⾏数⾏
* __VA_ARGS__ : 可变参数宏,
* ##表⽰直接连接, 例如 a##b <=> ab*/#definecerr(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 {
intfrate;// 也可以⽤ unsigned 结构int width;
int height;
char*pix;
};
创建了⼀个绘图对象 struct screen 这⾥ 构建这个结构体的时候⽤了下⾯⼀个技巧
//后⾯是 为 scr->pix 分配的内存 width*heightscr =malloc(sizeof(structscreen) +sizeof(char)*width*height);
⼀次分配两个内存空间.下⾯是主要实现的api 对象
/** 创建⼀个屏幕结构指针返回
*
* int frate : 绘制⼀帧的周期
* int width : 屏幕宽度
* int height : 屏幕⾼度
* return : 指向屏幕结构的指针
* */structscreen* screen_create(intfrate,intwidth,int height);/** 销毁⼀个屏幕结构指针, 并为其置空
* struct screen** : 指向屏幕结构指针的指针, ⼆级销毁⼀级的
* */voidscreen_destory(structscreen** pscr);/**
* 屏幕绘制函数,主要⽣成⼀个雪花效果
*
* struct screen* : 屏幕数据
* return : 0表⽰可以绘制了,1表⽰图案不变*/intscreen_draw_snow(structscreen* scr);/**
* 屏幕绘制动画效果, 绘制雪花动画
*
* struct screen* : 屏幕结构指针*/voidscreen_flash_snow(structscreen* scr);
创建销毁, 绘制⼀个雪花界⾯, 绘制雪花动画效果的api. 其实都很相似,⽤opengl 库, 主要让我们省略了需要单独和操作系统显⽰层打交道⼯作.
这⾥介绍⼀下,个⼈ ⼀个 简单避免 野指针的 的⽅法, 具体看下⾯实现
/** 销毁⼀个屏幕结构指针, 并为其置空
* struct screen** : 指向屏幕结构指针的指针, ⼆级销毁⼀级的
* */voidscreen_destory(structscreen** pscr)
{
if(NULL == pscr || NULL == *pscr)
return;
free(*pscr);
// 避免野指针*pscr = NULL;
}
在执⾏之后置空,因为C程序员对NULL⼀定要敏感,形成条件反射. 和⼤家开个玩笑 ,
请问 :
C 语⾔中, NULL , 0,'0',"0",false有什么异同 ?
欢迎同⾏,在招聘的时候问问,应聘初级开发⼯作者. 为什么C需要扣的那么细. 因为其它语⾔.你不明⽩是什么,
你可以⽤的很好. 但是C你写的代码,如果不知道会有怎样的结果,那么 线上就⼀⼤⽚服务器直接崩掉.⽽且还很难出
问题所在. 因为C很简单,越简单就是越复杂.就越需要专业的维护⼈员.导致它成了'玩具'.
最后看⼀下 主业务
// 主函数,主业务在此运⾏intmain(intargc,char*argv[])
{
structscreen* scr = NULL;
//创建⼀个屏幕对象scr = screen_create(_INT_FRATE, _INT_WIDTH, _INT_HEIGHT);
if(NULL == scr)
exit(EXIT_FAILURE);
//绘制雪花动画 screen_flash_snow(scr);
//销毁这个屏幕对象screen_destory(&scr);
return0;
}
还是⾮常容易看懂的, 创建⼀个屏幕对象,绘制雪花效果.销毁屏幕对象.
1.3 说⼀写 接⼝的实现细节
先看⼏个简单的api 实现,创建和销魂代码如下,很直⽩.
/** 创建⼀个屏幕结构指针返回
*
* int frate : 绘制⼀帧的周期
* int width : 屏幕宽度
* int height : 屏幕⾼度
* return : 指向屏幕结构的指针
* */structscreen* screen_create(intfrate,intwidth,int height)
{
structscreen *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*heightscr =malloc(sizeof(structscreen) +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** : 指向屏幕结构指针的指针, ⼆级销毁⼀级的
* */voidscreen_destory(structscreen** pscr)
{
if(NULL == pscr || NULL == *pscr)
return;
free(*pscr);
// 避免野指针*pscr = NULL;
}
后⾯说⼀下 如何 绘制 屏幕中雪花
主要算法 是
a.有个屏幕 w x h
b.屏幕从上⾯第⼀⾏ 出雪花 , 出雪花 位置是随机的[0,w], 但是有个距离,这个距离内只有⼀个雪花
c.下⼀⾏ 雪花 依赖上⼀⾏雪花的⽣成, 每个雪花在可以飘动的时候, 只能 在[-1,1] 范围内
d.实现动画 效果 就是 每画⼀帧就等待 ⼀段时间
下⾯看具体⼀点的 a
//创建⼀个屏幕对象scr = screen_create(_INT_FRATE, _INT_WIDTH, _INT_HEIGHT);
scr对象就是我们的创建屏幕. _INT_WIDTH 和 _INT_HEIGHT 就是屏幕⼤⼩. 对于_INT_FRATE 表⽰绘制⼀帧时间. b实现 代码如下:
/
/构建开头的雪花,下⾯宏表⽰每 _INT_SHEAD 个步长,⼀个雪花,需要是2的幂//static 可以理解为 private, 宏,位操作代码多了确实难读#define_INT_SHEAD (1<<2)stat {
intr =0;
//数据需要清空memset(snow,0, len);
for (;;) {
//取余⼀个技巧 2^3 - 1 = 7 => 111 , 并就是取余数intt = rand() & (_INT_SHEAD -1);
if(r + t >= len)
break;
snow[r + t] =1;
r += _INT_SHEAD;
}
}#undef_INT_SHEAD
技巧如上,可以看说明. 这⾥ 科普⼀下, 对于 for(;;) {} 和 while(true) {} 异同.
for(;;) {} 和 while(true) {} 这两段代码转成汇编是⼀样的, 不⼀样 的是 强加的意愿. 第⼀个 希望 跳过 检测步骤 速度更快⼀点.
再扩展⼀点.
//另⼀种循环语句, goto 还是很强⼤实⽤的__for_loop:
if(false)
goto __for_break;
goto __for_loop;
__for_break:
可以再扩展深⼀点, 还有⼀种 api ⽐ 这个goto 还NB. 有机会分享. 特别强⼤, 是异常处理程序本质.
对于c.
/
/通过上⼀个 scr->pix[scr->width*(idx-1)] => scr->pix[scr->width*idx]//下⾯的宏规定雪花左右摇摆 0 向左⼀个像素, 1 表⽰不变, 2表⽰向右⼀个像素#define_INT_SW {
intwidth = scr->width;
char* psnow = scr->pix + width*(idx -1);
char* snow = psnow + width;
inti, j, t;// i索引, j保存下⼀个瞬间雪花的位置,t 临时补得,解决雪花重叠问题
//为当前⾏重置memset(snow,0, width);
//通过上⼀次雪花位置计算下⼀次雪花位置for(i =0; 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];
}
}
}
下⼀⾏雪花 依赖 上⼀⾏雪花, 这⾥ 有点像插⼊排序.
整体的绘制代码 如下
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论