精华内容
下载资源
问答
  • C语言如何在指针中隐藏数据详解
    2021-05-21 02:14:49

    前言

    编写 C 语言代码时,指针无处不在。我们可以稍微额外利用指针,在它们内部暗中存储一些额外信息。为实现这一技巧,我们利用了数据在内存中的自然对齐特性。

    内存中的数据并非保存在任意地址。处理器通常按照其字大小相同的块读取内存数据;那么考虑到效率因素,编译器会按照块大小的整数倍对内存中的实体进行地址对齐。因此在 32 位的处理器上,一个 4 字节整型数据肯定存放在内存地址能被4整除的地方。

    下面,假设系统中整型数据和指针大小均为 4 字节。

    现在有一个指向整型的指针。如上所述,整型数据可以存放在内存地址 0x1000 或者 0x1004 或者 0x1008,但是决不会存放在 0x1001 或者0x1002 或者 0x1003 或者其他不能被4整除的任何地址。所有是4整数倍的二进制数都是以 00 结尾。实际上,这意味着对于所有指向整型的指针,它的最后两位总是 0。

    那么有 2 比特没有承载任何信息。此处的技巧是将我们的数据放置到这两个比特中,在需要时使用,并在通过指针解引用来访问内存前删除它们。

    由于 C 标准对指针位操作的支持不是很好,所以我们将指针保存为一个无符号整型数据。

    下面是一段简短的简单代码片段。完整的代码查看 github 代码仓库中的hide-data-in-ptr。

    void put_data(int *p, unsigned int data)

    {

    assert(data < 4);

    *p |= data;

    }

    unsigned int get_data(unsigned int p)

    {

    return (p & 3);

    }

    void cleanse_pointer(int *p)

    {

    *p &= ~3;

    }

    int main(void)

    {

    unsigned int x = 701;

    unsigned int p = (unsigned int) &x;

    printf("Original ptr: %un", p);

    put_data(&p, 3);

    printf("ptr with data: %un", p);

    printf("data stored in ptr: %un", get_data(p));

    cleanse_pointer(&p);

    printf("Cleansed ptr: %un", p);

    printf("Dereferencing cleansed ptr: %un", *(int*)p);

    return 0;

    }

    代码输出如下:

    Original ptr:  3216722220

    ptr with data: 3216722223

    data stored in ptr: 3

    Cleansed ptr:  3216722220

    Dereferencing cleansed ptr: 701

    我们可以在指针中存储任何可以用两个比特位表示的数据。使用 put_data() 函数,设置指针的最低两位为要存储的数据。该数据可以使用get_data() 函数获取。此处除了最后两位所有的位都被覆盖为零,于是我们隐藏的数据就显示出来。

    cleanse_pointer() 函数将最低两位置零,保证指针安全地解引用。注意虽然有些 CPU(像 Intel 允许我们访问未对齐内存地址,但其余 CPU(像 ARM)会出现访问错误。所以,要牢记在解引用前保证指针指向已对齐内存地址。

    这在实际中有应用吗?

    是的,有应用。查看 Linux 内核中红黑树的实现(链接:https://github.com/torvalds/linux/blob/master/include/linux/rbtree.h)。

    树的结点定义如下:

    struct rb_node {

    unsigned long  __rb_parent_color;

    struct rb_node *rb_right;

    struct rb_node *rb_left;

    } __attribute__((aligned(sizeof(long))));

    此处 unsigned long __rb_parent_color 存储了如下信息:

    父节点的地址

    结点的颜色

    色彩的表示用 0 代表红色,1 代表黑色。

    和前面的例子一样,该数据隐藏在父指针“无用的”比特位中。

    下面看一下父指针和色彩信息是如何获取的:

    /* in rbtree.h */

    #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))

    /* in rbtree_augmented.h */

    #define __rb_color(pc) ((pc) & 1)

    #define rb_color(rb) __rb_color((rb)->__rb_parent_color)

    内存中每一比特都很珍贵,咱们永远不要浪费。——(本文作者)

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    更多相关内容
  • 登录密码隐藏代码
  • 一、C语言贪吃蛇代码实现前言 设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言。 贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏,也...

    一、C语言贪吃蛇代码实现前言

    设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言。

    贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏,也就是实现贪吃蛇的基本功能,比如在地图中,用“↑↓←→”控制移动蛇的方向,吃掉食物之后,蛇身体会变长等等。。。。

    首先我们得分析,游戏中我们会碰见的一些情况。

    ①蛇的部分,蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,如果把蛇比做顺序表或者链表,在之后吃到食物的时候,身子肯定会变长,这就涉及到插入的操作,所以为了更高的效率,我们用链表实现我们的蛇的部分,最初我们把蛇身子按照四个结点打印在屏幕。

    ②蛇的移动,在屏幕上面蛇的移动看起来是整个身子向前方平移一个单位,但是其原理是我们在屏幕的另一个地方把蛇从新打印一遍,又把之前的蛇身子去除掉。

    ③食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长,也就是蛇的节点数增加一个。

    ④蛇在其中的几种状态,正常状态:蛇头节点的坐标没有和墙的坐标以及自己身子的坐标重合,

    被自己杀死:蛇头的坐标和蛇身子的坐标重合,撞墙:蛇头的坐标和墙的坐标重合。穿墙:蛇不会撞墙而死。

    二、C语言贪吃蛇代码实现游戏说明

    1、大 量 C语言项目游戏 源 代 码  获 取 可 以 关 注 微 信 公 众 号:“ C和C加加 ” 回复:“TC”即可

    2、C语言贪吃蛇游戏界面当中没有打印相关的按键说明,这里先逐一列出C语言贪吃蛇游戏按键说明

    • 按方向键上下左右,可以实现蛇移动方向的改变。

    • 短时间长按方向键上下左右其中之一,可实现蛇向该方向的短时间加速移动。

    • 按空格键可实现暂停,暂停后按任意键继续游戏。

    • 按Esc键可直接退出游戏。

    • 按R键可重新开始游戏。

    除此之外,本游戏还拥有计分系统,可保存玩家的历史最高记录。

    三、C语言贪吃蛇代码游戏效果展示

    贪吃蛇游戏当中蛇的移动速度可以进行调整,动图当中把速度调得较慢(速度太快导致动图上蛇身显示不全),下面给出的代码当中将蛇的速度调整到了合适的位置,大家可以试试

    四、C语言贪吃蛇游戏代码大全如下

    大家可以将以下代码复制到自己的编译器当中运行:

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    #include <time.h>
    #include <conio.h>
    
    #define ROW 22 //游戏区行数
    #define COL 42 //游戏区列数
    
    #define KONG 0 //标记空(什么也没有)
    #define WALL 1 //标记墙
    #define FOOD 2 //标记食物
    #define HEAD 3 //标记蛇头
    #define BODY 4 //标记蛇身
    
    #define UP 72 //方向键:上
    #define DOWN 80 //方向键:下
    #define LEFT 75 //方向键:左
    #define RIGHT 77 //方向键:右
    #define SPACE 32 //暂停
    #define ESC 27 //退出
    
    //蛇头
    struct Snake
    {
    	int len; //记录蛇身长度
    	int x; //蛇头横坐标
    	int y; //蛇头纵坐标
    }snake;
    
    //蛇身
    struct Body
    {
    	int x; //蛇身横坐标
    	int y; //蛇身纵坐标
    }body[ROW*COL]; //开辟足以存储蛇身的结构体数组
    
    int face[ROW][COL]; //标记游戏区各个位置的状态
    
    //隐藏光标
    void HideCursor();
    //光标跳转
    void CursorJump(int x, int y);
    //初始化界面
    void InitInterface();
    //颜色设置
    void color(int c);
    //从文件读取最高分
    void ReadGrade();
    //更新最高分到文件
    void WriteGrade();
    //初始化蛇
    void InitSnake();
    //随机生成食物
    void RandFood();
    //判断得分与结束
    void JudgeFunc(int x, int y);
    //打印蛇与覆盖蛇
    void DrawSnake(int flag);
    //移动蛇
    void MoveSnake(int x, int y);
    //执行按键
    void run(int x, int y);
    //游戏主体逻辑函数
    void Game();
    
    int max, grade; //全局变量
    int main()
    {
    #pragma warning (disable:4996) //消除警告
    	max = 0, grade = 0; //初始化变量
    	system("title 贪吃蛇"); //设置cmd窗口的名字
    	system("mode con cols=84 lines=23"); //设置cmd窗口的大小
    	HideCursor(); //隐藏光标
    	ReadGrade(); //从文件读取最高分到max变量
    	InitInterface(); //初始化界面
    	InitSnake(); //初始化蛇
    	srand((unsigned int)time(NULL)); //设置随机数生成起点
    	RandFood(); //随机生成食物
    	DrawSnake(1); //打印蛇
    	Game(); //开始游戏
    	return 0;
    }
    
    //隐藏光标
    void HideCursor()
    {
    	CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
    	curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
    	curInfo.bVisible = FALSE; //将光标设置为不可见
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
    	SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
    }
    //光标跳转
    void CursorJump(int x, int y)
    {
    	COORD pos; //定义光标位置的结构体变量
    	pos.X = x; //横坐标
    	pos.Y = y; //纵坐标
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
    	SetConsoleCursorPosition(handle, pos); //设置光标位置
    }
    //初始化界面
    void InitInterface()
    {
    	color(6); //颜色设置为土黄色
    	for (int i = 0; i < ROW; i++)
    	{
    		for (int j = 0; j < COL; j++)
    		{
    			if (j == 0 || j == COL - 1)
    			{
    				face[i][j] = WALL; //标记该位置为墙
    				CursorJump(2 * j, i);
    				printf("■");
    			}
    			else if (i == 0 || i == ROW - 1)
    			{
    				face[i][j] = WALL; //标记该位置为墙
    				printf("■");
    			}
    			else
    			{
    				face[i][j] = KONG; //标记该位置为空
    			}
    		}
    	}
    	color(7); //颜色设置为白色
    	CursorJump(0, ROW);
    	printf("当前得分:%d", grade);
    	CursorJump(COL, ROW);
    	printf("历史最高得分:%d", max);
    }
    //颜色设置
    void color(int c)
    {
    	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
    	//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
    }
    //从文件读取最高分
    void ReadGrade()
    {
    	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
    	if (pf == NULL) //打开文件失败
    	{
    		pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
    		fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
    	}
    	fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
    	fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
    	fclose(pf); //关闭文件
    	pf = NULL; //文件指针及时置空
    }
    //更新最高分到文件
    void WriteGrade()
    {
    	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
    	if (pf == NULL) //打开文件失败
    	{
    		printf("保存最高得分记录失败\n");
    		exit(0);
    	}
    	fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
    	fclose(pf); //关闭文件
    	pf = NULL; //文件指针及时置空
    }
    //初始化蛇
    void InitSnake()
    {
    	snake.len = 2; //蛇的身体长度初始化为2
    	snake.x = COL / 2; //蛇头位置的横坐标
    	snake.y = ROW / 2; //蛇头位置的纵坐标
    	//蛇身坐标的初始化
    	body[0].x = COL / 2 - 1;
    	body[0].y = ROW / 2;
    	body[1].x = COL / 2 - 2;
    	body[1].y = ROW / 2;
    	//将蛇头和蛇身位置进行标记
    	face[snake.y][snake.x] = HEAD;
    	face[body[0].y][body[0].x] = BODY;
    	face[body[1].y][body[1].x] = BODY;
    }
    //随机生成食物
    void RandFood()
    {
    	int i, j;
    	do
    	{
    		//随机生成食物的横纵坐标
    		i = rand() % ROW;
    		j = rand() % COL;
    	} while (face[i][j] != KONG); //确保生成食物的位置为空,若不为空则重新生成
    	face[i][j] = FOOD; //将食物位置进行标记
    	color(12); //颜色设置为红色
    	CursorJump(2 * j, i); //光标跳转到生成的随机位置处
    	printf("●"); //打印食物
    }
    //判断得分与结束
    void JudgeFunc(int x, int y)
    {
    	//若蛇头即将到达的位置是食物,则得分
    	if (face[snake.y + y][snake.x + x] == FOOD)
    	{
    		snake.len++; //蛇身加长
    		grade += 10; //更新当前得分
    		color(7); //颜色设置为白色
    		CursorJump(0, ROW);
    		printf("当前得分:%d", grade); //重新打印当前得分
    		RandFood(); //重新随机生成食物
    	}
    	//若蛇头即将到达的位置是墙或者蛇身,则游戏结束
    	else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
    	{
    		Sleep(1000); //留给玩家反应时间
    		system("cls"); //清空屏幕
    		color(7); //颜色设置为白色
    		CursorJump(2 * (COL / 3), ROW / 2 - 3);
    		if (grade > max)
    		{
    			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
    			WriteGrade();
    		}
    		else if (grade == max)
    		{
    			printf("与最高记录持平,加油再创佳绩", grade);
    		}
    		else
    		{
    			printf("请继续加油,当前与最高记录相差%d", max - grade);
    		}
    		CursorJump(2 * (COL / 3), ROW / 2);
    		printf("GAME OVER");
    		while (1) //询问玩家是否再来一局
    		{
    			char ch;
    			CursorJump(2 * (COL / 3), ROW / 2 + 3);
    			printf("再来一局?(y/n):");
    			scanf("%c", &ch);
    			if (ch == 'y' || ch == 'Y')
    			{
    				system("cls");
    				main();
    			}
    			else if (ch == 'n' || ch == 'N')
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				exit(0);
    			}
    			else
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				printf("选择错误,请再次选择");
    			}
    		}
    	}
    }
    //打印蛇与覆盖蛇
    void DrawSnake(int flag)
    {
    	if (flag == 1) //打印蛇
    	{
    		color(10); //颜色设置为绿色
    		CursorJump(2 * snake.x, snake.y);
    		printf("■"); //打印蛇头
    		for (int i = 0; i < snake.len; i++)
    		{
    			CursorJump(2 * body[i].x, body[i].y);
    			printf("□"); //打印蛇身
    		}
    	}
    	else //覆盖蛇
    	{
    		if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
    		{
    			//将蛇尾覆盖为空格即可
    			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
    			printf("  ");
    		}
    	}
    }
    //移动蛇
    void MoveSnake(int x, int y)
    {
    	DrawSnake(0); //先覆盖当前所显示的蛇
    	face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
    	face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
    	//蛇移动后各个蛇身位置坐标需要更新
    	for (int i = snake.len - 1; i > 0; i--)
    	{
    		body[i].x = body[i - 1].x;
    		body[i].y = body[i - 1].y;
    	}
    	//蛇移动后蛇头位置信息变为第0个蛇身的位置信息
    	body[0].x = snake.x;
    	body[0].y = snake.y;
    	//蛇头的位置更改
    	snake.x = snake.x + x;
    	snake.y = snake.y + y;
    	DrawSnake(1); //打印移动后的蛇
    }
    //执行按键
    void run(int x, int y)
    {
    	int t = 0;
    	while (1)
    	{
    		if (t == 0)
    			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
    		while (--t)
    		{
    			if (kbhit() != 0) //若键盘被敲击,则退出循环
    				break;
    		}
    		if (t == 0) //键盘未被敲击
    		{
    			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
    			MoveSnake(x, y); //移动蛇
    		}
    		else //键盘被敲击
    		{
    			break; //返回Game函数读取键值
    		}
    	}
    }
    //游戏主体逻辑函数
    void Game()
    {
    	int n = RIGHT; //开始游戏时,默认向后移动
    	int tmp = 0; //记录蛇的移动方向
    	goto first; //第一次进入循环先向默认方向前进
    	while (1)
    	{
    		n = getch(); //读取键值
    		//在执行前,需要对所读取的按键进行调整
    		switch (n)
    		{
    		case UP:
    		case DOWN: //如果敲击的是“上”或“下”
    			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    			break;
    		case LEFT:
    		case RIGHT: //如果敲击的是“左”或“右”
    			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    		case SPACE:
    		case ESC:
    		case 'r':
    		case 'R':
    			break; //这四个无需调整
    		default:
    			n = tmp; //其他键无效,默认为上一次蛇移动的方向
    			break;
    		}
    	first: //第一次进入循环先向默认方向前进
    		switch (n)
    		{
    		case UP: //方向键:上
    			run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
    			tmp = UP; //记录当前蛇的移动方向
    			break;
    		case DOWN: //方向键:下
    			run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
    			tmp = DOWN; //记录当前蛇的移动方向
    			break;
    		case LEFT: //方向键:左
    			run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
    			tmp = LEFT; //记录当前蛇的移动方向
    			break;
    		case RIGHT: //方向键:右
    			run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
    			tmp = RIGHT; //记录当前蛇的移动方向
    			break;
    		case SPACE: //暂停
    			system("pause>nul"); //暂停后按任意键继续
    			break;
    		case ESC: //退出
    			system("cls"); //清空屏幕
    			color(7); //颜色设置为白色
    			CursorJump(COL - 8, ROW / 2);
    			printf("  游戏结束  ");
    			CursorJump(COL - 8, ROW / 2 + 2);
    			exit(0);
    		case 'r':
    		case 'R': //重新开始
    			system("cls"); //清空屏幕
    			main(); //重新执行主函数
    		}
    	}
    }
    

    五、C语言贪吃蛇游戏代码详解

    1、贪吃蛇C语言实现游戏框架构建

    首先定义游戏界面的大小,定义游戏区行数和列数。

     

    这里将蛇活动的区域称为游戏区,将分数提示的区域称为提示区(提示区占一行)。

     

    此外,我们还需要两个结构体用于表示蛇头和蛇身。蛇头结构体当中存储着当前蛇身的长度以及蛇头的位置坐标。

     

    蛇身结构体当中存储着该段蛇身的位置坐标。

     

    同时我们需要一个二维数组来标记游戏区各个位置的状态(空、墙、食物、蛇头以及蛇身)。

     

    为了增加代码的可读性,最好运用宏来定义各个位置的状态,而不是在代码中用干巴巴的数字对各个位置的状态进行切换。

     

    当然,为了代码的可读性,我们最好也将需要用到的按键的键值用宏进行定义。

    2、隐藏光标

    隐藏光标比较简单,定义一个光标信息的结构体变量,然后对光标信息进行赋值,最后用这个光标信息的结构体变量进行光标信息设置即可。

    3、光标跳转

    光标跳转,也就是让光标跳转到指定位置进行输出。与隐藏光标的操作步骤类似,先定义一个光标位置的结构体变量,然后设置光标的横纵坐标,最后用这个光标位置的结构体变量进行光标位置设置即可。

    4、初始化界面

    初始化界面完成游戏区“墙”的打印,和提示区的打印即可。

    在打印过程中需要注意两点:

    在cmd窗口中一个小方块占两个单位的横坐标,一个单位的纵坐标。

    光标跳转函数CursorJump接收的是光标将要跳至位置的横纵坐标。

    例如,要用CursorJump函数跳转至 i 行 j 列(以一个小方块为一个单位),就等价于让光标跳转至坐标(2*j,i)处。

    //初始化界面
    void InitInterface()
    {
    	color(6); //颜色设置为土黄色
    	for (int i = 0; i < ROW; i++)
    	{
    		for (int j = 0; j < COL; j++)
    		{
    			if (j == 0 || j == COL - 1)
    			{
    				face[i][j] = WALL; //标记该位置为墙
    				CursorJump(2 * j, i);
    				printf("■");
    			}
    			else if (i == 0 || i == ROW - 1)
    			{
    				face[i][j] = WALL; //标记该位置为墙
    				printf("■");
    			}
    			else
    			{
    				face[i][j] = KONG; //标记该位置为空
    			}
    		}
    	}
    	color(7); //颜色设置为白色
    	CursorJump(0, ROW);
    	printf("当前得分:%d", grade);
    	CursorJump(COL, ROW);
    	printf("历史最高得分:%d", max);
    }
    

    注意: 在初始化界面的同时,记得对游戏区相应位置的状态进行标记。

    5、C语言贪吃蛇游戏颜色设置

    颜色设置函数的作用是,将此后输出的内容颜色都更为所指定的颜色,接收的参数c是颜色代码,十进制颜色代码表如下:

     

    设置颜色函数在其头文件当中的声明如下:

     

    6、初始化蛇

    初始化蛇时将蛇身的长度初始化为2,蛇头的起始位置在游戏区的中央,蛇头向右依次是第0个蛇身、第1个蛇身。

     在初始化蛇的信息后,记得对游戏区该位置的状态进行标记。

    7、随机生成食物

    随机在游戏区生成食物,需要对生成后的坐标进行判断,只有该位置为空才能在此生成食物,否则需要重新生成坐标。食物坐标确定后,需要对游戏区该位置的状态进行标记。

    8、打印蛇与覆盖蛇

    打印蛇和覆盖蛇这里直接使用一个函数来实现,若传入参数flag为1,则打印蛇;若传入参数为0,则用空格覆盖蛇。

    打印蛇:

    先根据结构体变量snake获取蛇头的坐标,到相应位置打印蛇头。

    然后根据结构体数组body依次获取蛇身的坐标,到相应位置进行打印即可。

    覆盖蛇:

    用空格覆盖最后一段蛇身即可。

    但需要注意在覆盖前判断覆盖的位置是否为(0,0)位置,因为当得分后蛇身长度增加,需要覆盖当前的蛇(进而打印长度增加后的蛇),而此时新加蛇身还未进行赋值(编译器一般默认初始化为0),我们根据最后一段蛇身获取到的坐标便是(0,0),则会用空格对(0,0)位置的墙进行覆盖,需要看完后面的移动蛇函数的实现后再进行理解。(也可以先将该判断去掉,观察蛇吃到食物后(0,0)位置墙的变化再进行分析)

    //打印蛇与覆盖蛇
    void DrawSnake(int flag)
    {
    	if (flag == 1) //打印蛇
    	{
    		color(10); //颜色设置为绿色
    		CursorJump(2 * snake.x, snake.y);
    		printf("■"); //打印蛇头
    		for (int i = 0; i < snake.len; i++)
    		{
    			CursorJump(2 * body[i].x, body[i].y);
    			printf("□"); //打印蛇身
    		}
    	}
    	else //覆盖蛇
    	{
    		if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
    		{
    			//将蛇尾覆盖为空格即可
    			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
    			printf("  ");
    		}
    	}
    }
    

    9、移动蛇

    移动蛇函数的作用就是先覆盖当前所显示的蛇,然后再打印移动后的蛇。

    参数说明:

    x:蛇移动后的横坐标相对于当前蛇的横坐标的变化。

    y:蛇移动后的纵坐标相对于当前蛇的纵坐标的变化。

    蛇移动后,各种信息需要变化:

    最后一段蛇身在游戏区当中需要被重新标记为空。

    蛇头位置在游戏区当中需要被重新标记为蛇身。

    存储蛇身坐标信息的结构体数组body当中,需要将第i段蛇身的坐标信息更新为第i-1段蛇身的坐标信息,而第0段,即第一段蛇身的坐标信息需要更新为当前蛇头的坐标信息。

    蛇头的坐标信息需要根据传入的参数x和y,进行重新计算。

    10、游戏主体逻辑函数

    主体逻辑:

    首先第一次进入该函数,默认蛇向右移动,进而执行run函数。

    直到键盘被敲击,再从run函数返回到Game函数进行按键读取。

    读取到键值后需要对读取到的按键进行调整(这是必要的)。

    调整后再进行按键执行,然后再进行按键读取,如此循环进行。

    按键调整机制:

    如果敲击的是“上”或“下”键,并且上一次蛇的移动方向不是“左”或“右”,那么将下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

    如果敲击的是“左”或“右”键,并且上一次蛇的移动方向不是“上”或“下”,那么将下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

    如果敲击的按键是空格、Esc、r或是R,则不作调整。

    其余按键无效,下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

    //游戏主体逻辑函数
    void Game()
    {
    	int n = RIGHT; //开始游戏时,默认向后移动
    	int tmp = 0; //记录蛇的移动方向
    	goto first; //第一次进入循环先向默认方向前进
    	while (1)
    	{
    		n = getch(); //读取键值
    		//在执行前,需要对所读取的按键进行调整
    		switch (n)
    		{
    		case UP:
    		case DOWN: //如果敲击的是“上”或“下”
    			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    			break;
    		case LEFT:
    		case RIGHT: //如果敲击的是“左”或“右”
    			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    		case SPACE:
    		case ESC:
    		case 'r':
    		case 'R':
    			break; //这四个无需调整
    		default:
    			n = tmp; //其他键无效,默认为上一次蛇移动的方向
    			break;
    		}
    	first: //第一次进入循环先向默认方向前进
    		switch (n)
    		{
    		case UP: //方向键:上
    			run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
    			tmp = UP; //记录当前蛇的移动方向
    			break;
    		case DOWN: //方向键:下
    			run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
    			tmp = DOWN; //记录当前蛇的移动方向
    			break;
    		case LEFT: //方向键:左
    			run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
    			tmp = LEFT; //记录当前蛇的移动方向
    			break;
    		case RIGHT: //方向键:右
    			run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
    			tmp = RIGHT; //记录当前蛇的移动方向
    			break;
    		case SPACE: //暂停
    			system("pause>nul"); //暂停后按任意键继续
    			break;
    		case ESC: //退出
    			system("cls"); //清空屏幕
    			color(7); //颜色设置为白色
    			CursorJump(COL - 8, ROW / 2);
    			printf("  游戏结束  ");
    			CursorJump(COL - 8, ROW / 2 + 2);
    			exit(0);
    		case 'r':
    		case 'R': //重新开始
    			system("cls"); //清空屏幕
    			main(); //重新执行主函数
    		}
    	}
    }
    

    11、执行按键

    参数说明:

    x:蛇移动后的横坐标相对于当前蛇的横坐标的变化。

    y:蛇移动后的纵坐标相对于当前蛇的纵坐标的变化。

    给定一定的时间间隔,若在该时间间隔内键盘被敲击,则退出run函数,返回Game函数进行按键读取。若未被敲击,则先判断蛇到达移动后的位置后是否得分或是游戏结束,然后再移动蛇的位置。

    若键盘一直未被敲击,则就会一直执行run函数当中的while函数,蛇就会一直朝一个方向移动,直到游戏结束。

    //执行按键
    void run(int x, int y)
    {
    	int t = 0;
    	while (1)
    	{
    		if (t == 0)
    			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
    		while (--t)
    		{
    			if (kbhit() != 0) //若键盘被敲击,则退出循环
    				break;
    		}
    		if (t == 0) //键盘未被敲击
    		{
    			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
    			MoveSnake(x, y); //移动蛇
    		}
    		else //键盘被敲击
    		{
    			break; //返回Game函数读取键值
    		}
    	}
    }
    

    12、判断得分与结束

    判断得分:

    若蛇头即将到达的位置是食物,则得分。得分后需要将蛇身加长,并且更新当前得分,除此之外,还需要重新生成食物。

    判断结束:

    若蛇头即将到达的位置是墙或者蛇身,则游戏结束。游戏结束后比较本局得分和历史最高得分,给出相应的提示语句,并且询问玩家是否再来一局,可自由发挥。

    //判断得分与结束
    void JudgeFunc(int x, int y)
    {
    	//若蛇头即将到达的位置是食物,则得分
    	if (face[snake.y + y][snake.x + x] == FOOD)
    	{
    		snake.len++; //蛇身加长
    		grade += 10; //更新当前得分
    		color(7); //颜色设置为白色
    		CursorJump(0, ROW);
    		printf("当前得分:%d", grade); //重新打印当前得分
    		RandFood(); //重新随机生成食物
    	}
    	//若蛇头即将到达的位置是墙或者蛇身,则游戏结束
    	else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
    	{
    		Sleep(1000); //留给玩家反应时间
    		system("cls"); //清空屏幕
    		color(7); //颜色设置为白色
    		CursorJump(2 * (COL / 3), ROW / 2 - 3);
    		if (grade > max)
    		{
    			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
    			WriteGrade();
    		}
    		else if (grade == max)
    		{
    			printf("与最高记录持平,加油再创佳绩", grade);
    		}
    		else
    		{
    			printf("请继续加油,当前与最高记录相差%d", max - grade);
    		}
    		CursorJump(2 * (COL / 3), ROW / 2);
    		printf("GAME OVER");
    		while (1) //询问玩家是否再来一局
    		{
    			char ch;
    			CursorJump(2 * (COL / 3), ROW / 2 + 3);
    			printf("再来一局?(y/n):");
    			scanf("%c", &ch);
    			if (ch == 'y' || ch == 'Y')
    			{
    				system("cls");
    				main();
    			}
    			else if (ch == 'n' || ch == 'N')
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				exit(0);
    			}
    			else
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				printf("选择错误,请再次选择");
    			}
    		}
    	}
    }
    

    注意: 若本局得分大于历史最高得分,需要更新最高分到文件。

    13、从文件读取最高分

    首先需要使用fopen函数打开“贪吃蛇最高得分记录.txt”文件,若是第一次运行该代码,则会自动创建该文件,并将历史最高记录设置为0,之后再读取文件当中的历史最高记录存储在max变量当中,并关闭文件即可。

     

    14、更新最高分到文件

    首先使用fopen函数打开“贪吃蛇最高得分记录.txt”,然后将本局游戏的分数grade写入文件当中即可(覆盖式)。

    15、主函数

    有了以上函数的支撑,写出主函数是相当简单的,但需要注意以下三点:

    全局变量grade需要在主函数内初始化为0,不能在全局范围初始化为0,因为当玩家按下R键进行重玩时我们需要将当前分数grade重新设置为0。

    随机数的生成起点建议设置在主函数当中。

    主函数当中的#pragma语句是用于消除类似以下警告的:

     

    六、穿墙版贪吃蛇C语言代码

    1、游戏效果展示

    个人认为穿墙版的贪吃蛇比普通版贪吃蛇更好玩,你觉得呢?

    2、贪吃蛇C语言游戏代码

    以下代码可以直接运行,欢迎试玩:

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    #include <time.h>
    #include <conio.h>
    
    #define ROW 23 //界面行数
    #define COL 42 //界面列数
    
    #define KONG 0 //标记空(什么也没有)
    #define FOOD 1 //标记食物
    #define HEAD 2 //标记蛇头
    #define BODY 3 //标记蛇身
    
    #define UP 72 //方向键:上
    #define DOWN 80 //方向键:下
    #define LEFT 75 //方向键:左
    #define RIGHT 77 //方向键:右
    #define SPACE 32 //暂停
    #define ESC 27 //退出
    
    //蛇头
    struct Snake
    {
    	int len; //记录蛇身长度
    	int x; //蛇头横坐标
    	int y; //蛇头纵坐标
    }snake;
    
    //蛇身
    struct Body
    {
    	int x; //蛇身横坐标
    	int y; //蛇身纵坐标
    }body[ROW*COL]; //开辟足以存储蛇身的结构体数组
    
    int face[ROW][COL]; //标记界面当中各个位置的信息
    
    //隐藏光标
    void HideCursor();
    //光标跳转
    void CursorJump(int x, int y);
    //初始化界面
    void InitInterface();
    //颜色设置
    void color(int c);
    //从文件读取最高分
    void ReadGrade();
    //更新最高分到文件
    void WriteGrade();
    //初始化蛇
    void InitSnake();
    //随机生成食物
    void RandFood();
    //判断得分与结束
    void JudgeFunc(int x, int y);
    //打印蛇/覆盖蛇
    void DrawSnake(int flag);
    //移动蛇
    void MoveSnake(int x, int y);
    //执行按键
    void run(int x, int y);
    //游戏主体逻辑函数
    void Game();
    
    int max, grade; //全局变量
    int main()
    {
    #pragma warning (disable:4996) //消除警告
    	max = 0, grade = 0; //初始化变量
    	system("title 贪吃蛇"); //设置cmd窗口名称
    	system("mode con cols=84 lines=23"); //设置cmd窗口大小
    	HideCursor(); //隐藏光标
    	ReadGrade(); //从文件读取最高分
    	InitInterface(); //初始化界面
    	InitSnake(); //初始化蛇
    	srand((unsigned int)time(NULL)); //设置随机数生成起点
    	RandFood(); //随机生成食物
    	DrawSnake(1); //打印蛇
    	Game(); //开始游戏
    	return 0;
    }
    
    //隐藏光标
    void HideCursor()
    {
    	CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
    	curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
    	curInfo.bVisible = FALSE; //将光标设置为不可见
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
    	SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
    }
    //光标跳转
    void CursorJump(int x, int y)
    {
    	COORD pos; //定义光标位置的结构体变量
    	pos.X = x; //横坐标
    	pos.Y = y; //纵坐标
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
    	SetConsoleCursorPosition(handle, pos); //设置光标位置
    }
    //初始化界面
    void InitInterface()
    {
    	color(7); //颜色设置为白色
    	CursorJump(0, 0);
    	printf("当前得分:%d", grade);
    	CursorJump(COL, 0);
    	printf("历史最高得分:%d", max);
    	color(11); //颜色设置为浅蓝色
    	for (int i = 1; i < ROW; i++)
    	{
    		for (int j = 0; j < COL; j++)
    		{
    			if (i == 1 && j != 0 && j != COL - 1) //打印游戏区的上界
    			{
    				CursorJump(2 * j, i);
    				printf("__");
    			}
    			else if (i == ROW - 1 && j != 0 && j != COL - 1) //打印游戏区的下界
    			{
    				CursorJump(2 * j, i);
    				printf("▔▔");
    			}
    			else if (j == 0 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的左界
    			{
    				CursorJump(2 * j, i);
    				printf(" >");
    			}
    			else if (j == COL - 1 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的右界
    			{
    				CursorJump(2 * j, i);
    				printf("< ");
    			}
    			else
    			{
    				face[i][j] = KONG; //其余位置标记为空(非常必要)
    			}
    		}
    	}
    }
    //颜色设置
    void color(int c)
    {
    	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
    }
    //从文件读取最高分
    void ReadGrade()
    {
    	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
    	if (pf == NULL) //打开文件失败
    	{
    		pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
    		fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
    	}
    	fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
    	fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
    	fclose(pf); //关闭文件
    	pf = NULL; //文件指针及时置空
    }
    //更新最高分到文件
    void WriteGrade()
    {
    	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
    	if (pf == NULL) //打开文件失败
    	{
    		printf("保存最高得分记录失败\n");
    		exit(0);
    	}
    	fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
    	fclose(pf); //关闭文件
    	pf = NULL; //文件指针及时置空
    }
    //初始化蛇
    void InitSnake()
    {
    	snake.len = 2; //蛇身长度初始化为2
    	snake.x = COL / 2; //蛇头位置的横坐标
    	snake.y = ROW / 2; //蛇头位置的纵坐标
    	//蛇身坐标的初始化
    	body[0].x = COL / 2 - 1;
    	body[0].y = ROW / 2;
    	body[1].x = COL / 2 - 2;
    	body[1].y = ROW / 2;
    	//将蛇头和蛇身位置进行标记
    	face[snake.y][snake.x] = HEAD;
    	face[body[0].y][body[0].x] = BODY;
    	face[body[1].y][body[1].x] = BODY;
    }
    //随机生成食物
    void RandFood()
    {
    	int i, j;
    	do
    	{
    		//随机生成食物的横纵坐标
    		i = rand() % ROW;
    		j = rand() % COL;
    		//若食物生成位置不在游戏区,或者生成食物的位置不为空,则重新生成
    	} while (i <= 1 || i == ROW - 1 || j == 0 || j == COL - 1 || face[i][j] != KONG); 
    	face[i][j] = FOOD; //将食物位置进行标记
    	color(9); //颜色设置为深蓝色
    	CursorJump(2 * j, i);
    	printf("●");
    }
    //判断得分与结束
    void JudgeFunc(int x, int y)
    {
    	int nextX = snake.x + x;
    	int nextY = snake.y + y;
    
    	if (nextX == COL - 1)
    		nextX = 1;
    	if (nextX == 0)
    		nextX = COL - 2;
    
    	if (nextY == ROW - 1)
    		nextY = 2;
    	if (nextY == 1)
    		nextY = ROW - 2;
    	//若即将到达的位置是食物,则得分
    	if (face[nextY][nextX] == FOOD)
    	{
    		snake.len++; //蛇身加长
    		grade += 10; //更新当前得分
    		color(7); //颜色设置为白色
    		CursorJump(0, 0);
    		printf("当前得分:%d", grade); //重新打印当前得分
    		RandFood(); //重新随机生成食物
    	}
    	//若即将到达的位置是蛇身,则游戏结束
    	else if (face[nextY][nextX] == BODY)
    	{
    		Sleep(1000); //留给玩家反应时间
    		system("cls"); //清空屏幕
    		color(7); //颜色设置为白色
    		CursorJump(2 * (COL / 3), ROW / 2 - 3);
    		if (grade > max)
    		{
    			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
    			WriteGrade();
    		}
    		else if (grade == max)
    		{
    			printf("与最高记录持平,加油再创佳绩", grade);
    		}
    		else
    		{
    			printf("请继续加油,当前与最高记录相差%d", max - grade);
    		}
    		CursorJump(2 * (COL / 3), ROW / 2);
    		printf("GAME OVER");
    		while (1) //询问玩家是否再来一局
    		{
    			char ch;
    			CursorJump(2 * (COL / 3), ROW / 2 + 3);
    			printf("再来一局?(y/n):");
    			scanf("%c", &ch);
    			if (ch == 'y' || ch == 'Y')
    			{
    				system("cls");
    				main();
    			}
    			else if (ch == 'n' || ch == 'N')
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				exit(0);
    			}
    			else
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 4);
    				printf("选择错误,请再次选择");
    			}
    		}
    	}
    }
    //打印蛇与覆盖蛇
    void DrawSnake(int flag)
    {
    	if (flag == 1) //打印蛇
    	{
    		color(10); //颜色设置为绿色
    		CursorJump(2 * snake.x, snake.y);
    		printf("■"); //打印蛇头
    		//打印蛇身
    		for (int i = 0; i < snake.len; i++)
    		{
    			CursorJump(2 * body[i].x, body[i].y);
    			printf("■");
    		}
    	}
    	else //覆盖蛇
    	{
    		if (body[snake.len - 1].x != 0) //防止len++后(0, 0)位置所显示的信息被覆盖
    		{
    			//将蛇尾覆盖为空格即可
    			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
    			printf("  ");
    		}
    	}
    }
    //移动蛇
    void MoveSnake(int x, int y)
    {
    	DrawSnake(0); //先覆盖当前所显示的蛇
    	face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
    	face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
    	//蛇移动后各个蛇身位置坐标需要更新
    	for (int i = snake.len - 1; i > 0; i--)
    	{
    		body[i].x = body[i - 1].x;
    		body[i].y = body[i - 1].y;
    	}
    	//蛇移动后蛇头位置信息变为第0个蛇身的位置信息
    	body[0].x = snake.x;
    	body[0].y = snake.y;
    	//蛇头的位置更改
    	snake.x = snake.x + x;
    	if (snake.x == COL - 1) //越过右界
    		snake.x = 1;
    	else if (snake.x == 0) //越过左界
    		snake.x = COL - 2;
    	snake.y = snake.y + y;
    	if (snake.y == ROW - 1) //越过下界
    		snake.y = 2;
    	else if (snake.y == 1) //越过上界
    		snake.y = ROW - 2;
    	face[snake.y][snake.x] = HEAD; //对蛇头位置进行标记
    	DrawSnake(1); //打印移动后的蛇
    }
    //执行按键
    void run(int x, int y)
    {
    	int t = 0;
    	while (1)
    	{
    		if (t == 0)
    			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
    		while (--t)
    		{
    			if (kbhit() != 0) //若键盘被敲击,则退出循环
    				break;
    		}
    		if (t == 0) //键盘未被敲击
    		{
    			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
    			MoveSnake(x, y); //移动蛇
    		}
    		else //键盘被敲击
    		{
    			break; //返回Game函数读取键值
    		}
    	}
    }
    //游戏主体逻辑函数
    void Game()
    {
    	int n = RIGHT; //开始游戏时,默认向后移动
    	int tmp = 0; //记录蛇的移动方向
    	goto first; //第一次进入循环先向默认方向前进
    	while (1)
    	{
    		n = getch(); //读取键值
    		//在执行前,需要对所读取的按键进行调整
    		switch (n)
    		{
    		case UP:
    		case DOWN: //如果敲击的是“上”或“下”
    			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    			break;
    		case LEFT:
    		case RIGHT: //如果敲击的是“左”或“右”
    			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
    			{
    				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
    			}
    		case SPACE:
    		case ESC:
    		case 'r':
    		case 'R':
    			break; //这四个无需调整
    		default:
    			n = tmp; //其他键无效,默认为上一次蛇移动的方向
    			break;
    		}
    	first: //第一次进入循环先向默认方向前进
    		switch (n)
    		{
    		case UP: //方向键:上
    			if (snake.y - 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
    			{
    				run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
    				tmp = UP; //记录当前蛇的移动方向
    			}
    			break;
    		case DOWN: //方向键:下
    			if (snake.y + 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
    			{
    				run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
    				tmp = DOWN; //记录当前蛇的移动方向
    			}
    			break;
    		case LEFT: //方向键:左
    			if (snake.x - 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
    			{
    				run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
    				tmp = LEFT; //记录当前蛇的移动方向
    			}
    			break;
    		case RIGHT: //方向键:右
    			if (snake.x + 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
    			{
    				run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
    				tmp = RIGHT; //记录当前蛇的移动方向
    			}
    			break;
    		case SPACE: //暂停
    			system("pause>nul"); //暂停后按任意键继续
    			break;
    		case ESC: //退出
    			system("cls"); //清空屏幕
    			color(7); //颜色设置为白色
    			CursorJump(COL - 8, ROW / 2);
    			printf("  游戏结束  ");
    			CursorJump(COL - 8, ROW / 2 + 2);
    			exit(0);
    		case 'r':
    		case 'R': //重新开始
    			system("cls"); //清空屏幕
    			main(); //重新执行主函数
    		}
    	}
    }
    

    3、游戏代码详解

    跟前面的大同小异这里就不细说了,感兴趣的对照看一下就明白了。

    七、大量C语言项目游戏源代码获 取

    可 以 关 注 微 信 公 众 号:“ C和C加加 ” 回复:“TC”即可

    展开全文
  • C语言隐藏密码输入

    2022-03-08 11:31:33
    这里主要用到的是头文件...代码 #include <stdio.h> #include <string.h> #include <conio.h> void hiddenInput(){ char a[20]; int i; printf("请输入密码:"); for(i=0;i<20;i++){ a[i]=

    这里主要用到的是头文件 #include <conio.h>下的getch()函数

    getch()函数的主要功能是,读取一个输入的字符但是并不会在屏幕上显示出

    代码

    #include <stdio.h>
    #include <string.h>
    #include <conio.h>
    
    void hiddenInput(){
    	char a[20];
    	int i;
    	printf("请输入密码:");
    	for(i=0;i<20;i++){
    		a[i]=getch();//getch()从控制台输入一各字符但是不显示 
    		/*
    			当在第i个字符输入退格键时,会把第i-1个字符赋值为'\0',
    			并将i指向i-2 ,经过循环的i++后,i就指向了刚刚赋值为 '\0'的位置 
    		*/ 
    		if(a[i]=='\b' && i>0){
    			a[--i]='\0';
    			i--;
    		} 
    		//如果在最开始就按退格键,就让i=-1 
    		if(a[i]=='\b' && i==0){
    			i=-1;
    		}
    		//如果输入的是回车,就退出输入循环 
    		if(a[i]=='\r'){
    			break;
    		}
    	}
    	a[i]='\0';
    	printf("\n您输入的密码是:"); 
    	puts(a);
    }
    int main(){
    	hiddenInput();
    	return 0;
    }
    

    运行结果截图

    在这里插入图片描述

    总结:

    可以运用一些判断条件实现输入的过程可以删除前面输入的部分,
    这个代码不足的地方是没有实现:输入密码以"星号"显示,删除的时候 "星号"也会被删除。

    展开全文
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼#include #include #include #define I 20#define R 340int main(){int i,j,e;int a;for(i=1,a=I;ifor(j=(int) ( I-sqrt(I*I-(a-i)*(a-i)) );j>0;j--)printf(" ");for...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

    #include

    #include

    #include

    #define I 20

    #define R 340

    int main()

    {

    int i,j,e;

    int a;

    for(i=1,a=I;i

    for(j=(int) ( I-sqrt(I*I-(a-i)*(a-i)) );j>0;j--)

    printf(" ");

    for(e=1;e<=2*sqrt(I*I-(a-i)*(a-i));e++)

    printf("\3");

    for(j=(int) ( 2*( I-sqrt(I*I-(a-i)*(a-i)) ) );j>0;j--)

    printf(" ");

    for(e=1;e<=2*sqrt(I*I-(a-i)*(a-i));e++)

    printf("\3");

    printf("\n");

    }

    for(i=1;i<80;i++)

    {

    if(i==25){

    printf(" I LOVE YOU! ");

    i+=30;

    }

    printf("\3");

    }

    printf("\n");

    for(i=1;i<=R/2;i++){

    if(i%2||i%3)continue;

    for(j=(int) ( R-sqrt(R*R-i*i) );j>0;j--)

    printf(" ");

    for(e=1;e<=2*( sqrt(R*R-i*i) - (R-2*I) );e++)

    printf("\3");

    printf("\n");

    }

    long time;

    for(; ;)

    {

    system("color a");

    for(time=0;time<99999999;time++);

    system("color b");

    for(time=0;time<99999999;time++);

    system("color c");

    for(time=0;time<99999999;time++);

    system("color d");

    for(time=0;time<99999999;time++);

    system("color e");

    for(time=0;time<99999999;time++);

    system("color f");

    for(time=0;time<99999999;time++);

    system("color 0");

    for(time=0;time<99999999;time++);

    system("color 1");

    for(time=0;time<99999999;time++);

    system("color 2");

    for(time=0;time<99999999;time++);

    system("color 3");

    for(time=0;time<99999999;time++);

    system("color 4");

    for(time=0;time<99999999;time++);

    system("color 5");

    for(time=0;time<99999999;time++);

    system("color 6");

    for(time=0;time<99999999;time++);

    system("color 7");

    for(time=0;time<99999999;time++);

    system("color 8");

    for(time=0;time<99999999;time++);

    system("color 9");

    for(time=0;time<99999999;time++);

    system("color ab");

    for(time=0;time<99999999;time++);

    system("color ac");

    for(time=0;time<99999999;time++);

    system("color ad");

    for(time=0;time<99999999;time++);

    system("color ae");

    for(time=0;time<99999999;time++);

    system("color af");

    for(time=0;time<99999999;time++);

    }

    return 0;

    }

    效果是两种彩色 心图,

    展开全文
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼首先你可以看下面的WScript.Echo("嘿,谢谢你打开我哦,我等你很久拉!"&TSName)WScript.Echo("你是可爱的XX童鞋吗?")WScript.Echo("哈,我想你拉,这你都不知道吗?...
  • C语言隐藏输入

    2021-10-19 13:52:00
    使用libncurses.so实现隐藏输入,即用*掩盖终端输入密码明文
  • C语言隐藏控制台窗口

    2020-05-27 20:18:37
    隐藏C语言程序运行的黑窗口,在VS的开头加入下面的预编译命令: (预编译,Linker链接,windows模式,黑窗口是dos模式) #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") 就可以...
  • C语言隐藏输入的代码

    千次阅读 2017-04-01 21:03:35
    有些系统或软件输入密码时看不见我们所输入的字符,这里写一下这个简单代码: 运行后,只要不输入‘x’,就别想继续运行,而输入的字符也全部都看不见! #include #include #include #include int main()...
  • C语言写的三层BP神经网络代码,用fisheriris数据集训练和测试网络,也可以用于其他分类问题。 一个输入层,一个隐藏层,一个输出层,各层的节点数都可以设置。属性数据归一化采用Z-score算法,标签数据归一化采用Min...
  • C语言基础知识入门(大全

    万次阅读 多人点赞 2021-05-11 18:34:11
    一.C语言入门 C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好,可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼/*程序功能:计算一个正整数n的阶乘,目前最大能运算10000的阶乘,可秒杀。程序意义:加强自己对于大数的处理。说明:此程序对乘法和除法还未做任何优化,如果用上位运算...
  • C语言如何隐藏输入密码

    千次阅读 2021-05-21 06:20:42
    如何在C语言程序框里面实现像输入QQ密码,输进去密码显示出来的就是*号,然后按退格键可以删除*号。首先我们要知道如何输入字符串,我们经常使用scanf这个函数来输入,但是这是对字符...自定义函数/*隐藏用户输入*/voi...
  • 发个C代码(简单动画演示)

    千次阅读 2021-05-21 17:10:17
    该楼层疑似违规已被系统折叠隐藏此楼查看此楼/********************************//*TurboC2.0下运行通过*//*简单的图像演示*//********************************/#include#include#include#include#include#...
  • 分享一段有趣的小代码

    千次阅读 2021-05-20 18:42:31
    该楼层疑似违规已被系统折叠隐藏此楼查看此楼#include#include#include//数字雨typedef struct{int x, y;char ch;}STU;STU st[5000];//出现位置void gotoxy(int x, int y){HANDLE hout;COORD pos;pos.X = x;pos.Y = ...
  • C语言写的走迷宫的代码

    千次阅读 2021-05-19 05:32:50
    } void hidden()//隐藏光标 { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cci; GetConsoleCursorInfo(hOut,&cci); cci.bVisible=0;//赋1为显示,赋0为隐藏 SetConsoleCursorInfo(hOut,&...
  • 但在有些场合需隐藏鼠标,使之不可见,以免影响屏幕画面的整体效果,如屏幕保护程序运行时,鼠标就是不可见的。在VB中可通过调用Windows API函数ShowCursor()来非常轻松地实现鼠标的隐藏。方法如下:方法一:首先在...
  • 贪吃蛇(C语言实现)

    万次阅读 多人点赞 2021-08-17 20:47:21
    文章目录游戏说明游戏效果展示游戏代码游戏代码详解游戏框架构建隐藏光标光标跳转初始化界面初始化蛇颜色设置随机生成食物打印蛇与覆盖蛇移动蛇执行按键判断得分与结束游戏主体逻辑函数从文件读取最高分更新最高分到...
  • C语言实现控制台窗口隐藏

    千次阅读 2019-04-10 21:28:51
     通过以下两个函数来实现窗口的隐藏效果。不仅仅只用于控制台窗口,其他的也可以。 FindWindow函数 功能  FindWindow函数的功能是获取一个顶层窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。另外,这个...
  • C语言实现密码隐藏

    2021-05-03 17:19:49
    if (mysql_real_connect(&m_sql, "localhost", "root", mima, "worker", 3306, NULL, NULL) ==NULL) //mysql 初始对象不要用指针 用指针的话看不到 错误代码 或者还没有足够对库函数的了解 MSQL ss ; MSQL * XX则看...
  • C语言猜数字游戏代码

    2021-05-20 09:46:41
    该楼层疑似违规已被系统折叠隐藏此楼查看此楼#include#include#includeintEachDiff(inta[],intn){inti,j;for(i=0;ifor(j=i+1;jif(a[i]==a[j])return0;elsereturn1;}intJudgeA(inta[],intb[],intn){inti,result=0;for...
  • 俄罗斯方块-C语言-完整代码

    千次阅读 2021-01-24 19:38:46
    //光标亮度为1-100 //设置光标隐藏 SetConsoleCursorInfo(Output,&info);//提取光标地址 SetConsoleTitle("俄罗斯方块");//控制台句柄 startGame(manager,control);//调用游戏 } //游戏池 void printPoolBorder() { ...
  • 隐藏进程 C语言

    2012-11-06 16:20:29
    c语言实现隐藏进程 很不错的代码 调试可以运行

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,135
精华内容 23,254
关键字:

c语言隐藏代码大全