【C语言】简单贪吃蛇实现保姆级教学!!!

在这里插入图片描述关注小庄 顿顿解馋૮(˶ᵔ ᵕ ᵔ˶)ა 新年快乐呀小伙伴

引言: 小伙伴们应该都有一个做游戏的梦吧?今天让小庄来用C语言简单实现一下我们的童年邪典贪吃蛇,顺便巩固我们的C语言知识,请安心食用~

文章目录

  • 贪吃蛇效果
  • 一.游戏前工作
    • 🏠 控制台程序
    • 🏠 光标设置
      • 📌 Win32API
    • 🏠 绘制地图
      • 📌 控制台坐标系
      • 📌 控制台坐标的设置
      • 📌 墙壁图案
    • 🏠 帮助信息
    • 🏠 贪吃蛇整体维护
      • 📌蛇的初始化
      • 📌食物创建
      • 📌其他内容初始化
  • 二.游戏运行时工作
    • 🏠 游戏逻辑
    • 🏠 按键检测
    • 🏠 蛇的移动
      • 📌蛇是否吃到食物
      • 📌蛇是否撞墙和咬到自身
  • 三.游戏善后工作
    • 🏠 贪吃蛇资源的释放
    • 🏠 贪吃蛇游戏状态
    • 🏠 拓展

贪吃蛇效果

如下是我们将实现的效果 请看vcr

一.游戏前工作

🏠 控制台程序

平时我们运行程序弹出的黑框框就是控制台程序我们平时可能不在意它的字体颜色,大小等等…我们可以根据我们的游戏进行一些调整

  • 控制台程序标题

sysytem("title 贪吃蛇"); //包含头文件window.h

我们可以根据自己的需要修改标题

  • 控制台程序宽度和高度

ssystem("mode con cols=xxx lines=xxx"); //cols是列长 lines是行长

我们可以根据需求自行调整游戏总界面面长宽


🏠 光标设置

在这里插入图片描述

在我们游戏运行时这个光标一直浮动着不太好,我们有什么办法可以隐藏呢?当然可以这里就要介绍我们的Win32API了~

📌 Win32API

什么是API?

我们发现应用程序会有开启视窗、描绘图形、使⽤周边设备等操作,那这是怎么实现的呢 ? 那就是通过我们操作系统应用程序编程接口(Application Programming Interface),这个接口的服务对象是我们的应用程序.不同的API函数能实现不同的操作.

应用程序接口的提供者是运行库,什么样的运行库提供什么样的API,比如Linux下的Glibc库提供POSIX API;Windows的运行库提供Windows API,最常见的32位Windows提供的API又被称为Win32API

  • GetStdHandle
    GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。
HANDLE GetStdHandle(DWORD nStdHandle);//参数是标准设备
参数取值含义
STD_INPUT_HANDLE标准输入设备
STD_OUTPUT_HANDLE标准输出设备
STD_ERROR_HANDLE标准错误设备

理解:不同设备可以理解为不同的门,不同门对应不同的锁,而我们的句柄相当于打开门的钥匙,有了钥匙才能破门而入探索新世界~

  • CONSOLE_CURSOR_INFO
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

这是一个结构体类型,用于存储控制台光标相关信息

dwSize : 由光标填充的字符单元格的百分⽐光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条,该值范围为0-100超出范围不会报错但不发生变化.

bBisible : 光标的可见性.true为可见,false为不可见.

  • GetConsoleCursorInfo
    这也是个API函数用于检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息.
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
//第一个参数是我们获得的设备句柄,有了句柄才能操作设备;
//第二个参数是_CONSOLE_CURSOR_INFO结构体,我们获得将获得的光标信息放进这个结构体变量里
  • SetConsoleCursorInfo
    设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。存完光标信息我们就能重新设置光标信息了.
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//获取句柄
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

了解这几个API之后我们就能设置我们的光标了~


🏠 绘制地图

我们的地图大致是这样子
在这里插入图片描述

我们知道这里我们就得普及一下控制台程序的坐标系了

📌 控制台坐标系

我们的坐标系向右是x轴,从左向右增长;坐标系向下是y轴,从上至下增长
在这里插入图片描述

📌 控制台坐标的设置

知道了坐标系后,我们得清楚我们打印东西是得看这个光标的,从光标的位置开始打印.所以我们得知道相关知识实现"定向射靶".

  • 控制台屏幕上的坐标COORD

COORD是WindowsAPI中定义的⼀个结构体,表示⼀个字符在控制台屏幕上的坐标.

  • SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置.

//设置光标的坐标
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
}

封装成一个函数这样我们就很方便的定向打靶了

📌 墙壁图案

我们地图墙的边界都是由一个个◻组成的,包括我们的蛇身也是由一个个特殊符号组成的,这些符号能直接被打印出来吗?答案是不行,原因是这些宽字符占两个字节我们的char只占一个字节满足不了我们的需求,所以我们需要用wchar_t 和宽字符的输⼊和输出函数以及本地化来进行字符打印

  • <locale.h>本地化
    由于世界文化各有不同,不同地区采用的语言也有所不同,这时我们用C语言输出字符就会出现地区差异,而我们的locale.h头文件提供的函数就很好地为我们解决了这个问题

  • setlocale函数

char* setlocale (int category, const char* locale);

setlocale函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项,以下是部分类项

类项代表意义
LC_ALL所有类别
LC_TIMEstrftime 和 wcsftime 函数
LC_CTYPE字符处理函数
LC_MONETARYlocaleconv 函数返回的货币格式信息

第二个参数只有两个取值

1.“” 表示切换到本地模式,此时可以用本地的字符集等

  • “C” 表示默认的正常方式

本地化后我们就能打印本地区的一些宽字符了

  • 宽字符打印
int main()
{
	setlocale(LC_ALL, "");
	wchar_t ch1 = L'中';
	wchar_t ch2 = L'国';
	wchar_t ch3 = L'★';
	wchar_t ch4 = L'●';
	wprintf(L"%lc\n", ch1);
	wprintf(L"%lc\n", ch2);
	wprintf(L"%lc\n", ch3);
	wprintf(L"%lc\n", ch4);
	printf("ab\n");
	return 0;
}

注意: 宽字符类型前面要加一个L表示宽字符,打印输出用wprintf接收wchar_t类型变量,打印格式为L%lc

明白这些后我们就能绘制地图了,代码如下

void Createmap()
{
	//上  从(0,0)开始宽字符是普通字符的两倍 不断加2
	Setpos(0, 0);
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	Setpos(0, 25);
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		Setpos(0,i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		Setpos(56, i);
		wprintf(L"%lc", WALL);
	}
}

🏠 帮助信息

我们可以在加载游戏地图前提供开始界面和帮助信息,注意设置好光标的位置,代码如下

void Welcomeinfo()
{
	Setpos(37, 13);
	printf("欢迎来到贪吃蛇小游戏ovo");
	Setpos(37, 22);
	system("pause");
	Setpos(33, 13);
	printf("用↑ ↓ ← →移动 shift来加速 ctrl来减速");
	Setpos(37, 22);
	system("pause");
	system("cls");
}

大致效果如下
在这里插入图片描述


🏠 贪吃蛇整体维护

对于贪吃蛇的整体游戏我们有蛇睡眠时间,贪吃蛇这条蛇本身,游戏分数,食物权重等等信息,这时我们可以想到用我们的结构体来储存我们的信息,同时我们蛇可以看作是链表构成,里面存储坐标以及next指针

//游戏状态
enum Status
{
	OK = 1,
	ESC,//正常退出
	KILL_BYWALL,//撞墙
	KILL_BYSELF//自己咬到自己
};
//蛇运动的方向
enum Die
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};

//定义蛇身结点

typedef int Datatype;
typedef struct SnakeNode
{
	//储存坐标
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,* pSnakeNode;

//定义整个贪吃蛇要维护的内容
typedef struct Snake
{
	pSnakeNode snake;//蛇
	pSnakeNode pFood;//食物
	int Score;//当前分数
	int FoodWeight;//每个食物的权重
	int sleeptime;//睡眠时间
	enum Status status;//游戏状态
	enum Dir dir;//蛇运动方向

}Snake,* pSnake;

📌蛇的初始化

对于蛇的初始化我们可以以链表的形式运用尾插链接蛇,值得注意的是蛇的坐标要设置成2的倍数,不然蛇蛇一半身体会卡到墙里,在创建蛇的结点的同时打印蛇身

#define BODY L'●'
#define POS_X 24
#define POS_Y 5
//初始化蛇
void InitSnake(pSnake ps)
{
    //创建蛇 五个结点
	pSnakeNode newhead = NULL;
	pSnakeNode newtail = NULL;
	for (int i = 0; i < 5; i++)
	{//创建新结点并初始化化他们的坐标
		pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("malloc failed");
			exit(1);
		}
		cur->x = POS_X + 2*i;//这里要这个两倍不然会发生覆盖
		cur->y = POS_Y;
		cur->next = NULL;
		Setpos(cur->x,cur->y);
		wprintf(L"%lc", BODY);
	   //链表为空新结点作为头结点
		if (newhead == NULL)
		{
			newtail = newhead = cur;
		}
		else
		{
			///头插
			cur->next = ps->snake;
			ps->snake = cur;
		}
	}
	//其他内容的初始化
	ps->sleeptime = 200;
	ps->Score = 0;
	ps->FoodWeight = 10;
	ps->status = OK;
	ps->dir = UP;
	ps->pFood = NULL;
}

📌食物创建

对于食物我们也可以看成链表的一个结点但有几点需要注意:

  1. 食物的坐标要是随机 可以用rand函数解决
  2. 食物的坐标不能与墙壁重叠且食物坐标也要是二的倍数保证不卡墙
  3. 食物坐标不能与蛇身重叠 可以遍历链表进行比较不满足重新生成
void CreateFood(pSnake ps)
{
	//先产生坐标 
	int x = 0;
	int y = 0;
	//横坐标要是偶数
	again:
	do
	{
		x = rand() % 53 + 2;	//2-54 --> 0~52+2
		y = rand() % 24 + 1 ;   //1~24
	} while (x%2!=0);
	//不能超出墙 同时不能跟这个蛇的坐标重叠
	pSnakeNode cur = ps->snake;
	while (cur)
	{
		if (x == cur->x &&y == cur->y)
		{
			goto again;//与蛇重叠重新产生
		}
		cur = cur->next;
	 }
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror("CreateFood()::malloc()");
		return;
	}
	pFood->x = x;
	pFood->y = y;
	ps->pFood = pFood;
	Setpos(x,y);
	wprintf(L"%lc", FOOD);
}

📌其他内容初始化

除了蛇之外我们还有游戏状态,蛇睡眠时间以及分数等等

ps->snake = newtail;
	//其他内容的初始化
	ps->sleeptime = 200;
	ps->Score = 0;
	ps->FoodWeight = 10;
	ps->status = OK;
	ps->dir = UP;
	ps->pFood = NULL;

二.游戏运行时工作

🏠 游戏逻辑

准备好前置工作后我们就可以着手准备我们的游戏运行工作了,首先我们需要确定游戏的大体逻辑

大体逻辑:玩家根据游戏提示按键—>根据按键判断下步该做什么工作 : 在状态OK下实现蛇的移动 pause状态则是暂停 ESC状态是正常退出 也就是说状态不是OK就是退出程序(一个循环)


🏠 按键检测

我们在C语言中怎么检测我们的按键情况呢?这要请出我们一个新的API函数GetAsyncKeyState

  • GetAsyncKeyState
SHORT GetAsyncKeyState
(
   int vKey
);
//将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态

返回值:返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态抬起;如果最低位被置为1则说明,该按键被按过,否则为0。

所以我们可以通过检测函数返回值的最低位是否为1来检测按键情况,可以用一个宏封装

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

基于这个API函数和我们的游戏逻辑就能实现我们游戏运行基本的代码

void pause(pSnake ps)
{
	while (1)
	{
		if (KEY_PRESS(VK_SPACE))
		{
			return;
		}
	}
}

void GameRun(pSnake ps)
{
	//帮助信息
	Helpinfo();
	//游戏luoji
	do 
	{
		Setpos(64, 10);
		printf("得分:%d ", ps->Score);
		printf("每个⻝物得分:%d分", ps->FoodWeight);
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
		{
			//保证相对方向的不冲突
			ps->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE) && ps->dir != DOWN)
		{
			//退出
			ps->status = ESC;
		}
		else if (KEY_PRESS(VK_SPACE) && ps->dir != DOWN)
		{
		   //暂停
			pause(ps);
		}
		else if (KEY_PRESS(VK_CONTROL) && ps->dir != DOWN)
		{
		   //减速
			if (ps->sleeptime < 350)
			{
				ps->sleeptime += 30;
				ps->FoodWeight -= 2;
				if (ps->sleeptime == 350)
				{
					ps->FoodWeight = 1;
				}
			}
		}
		else if (KEY_PRESS(VK_SHIFT) && ps->dir != DOWN)
		{
		   //加速 就是减少睡眠时间
			if (ps->sleeptime >= 50)
			{
				ps->sleeptime -= 30;
				ps->FoodWeight += 2;
			}
		}
		//休眠
		Sleep(ps->sleeptime);
		//移动
		SnakeMove(ps);
	} while (ps->status == OK);

}

注意:1.要保证按键方向不冲突 2.蛇休眠时间和食物权重不能为负数


🏠 蛇的移动

接下来就是整个游戏的运行的重头戏了

📌蛇是否吃到食物

分析:
对于我们的蛇,它吃到食物蛇长就会增加,也就是结点会增多,那么我们需要判断每次执行按键命令时我们是否会吃到食物。如果吃到食物的话我们就根据移动方向新插入一个结点同时创建新食物;如果没吃到食物我们仍然插入新结点,但尾结点打印空格再释放掉

实现代码如下

int IsFood(pSnake ps,pSnakeNode cur)
{
	//判断新结点是否与食物的坐标重合
	if (cur->x == ps->pFood->x && cur->y == ps->pFood->y)
		return 1;
	else
		return 0;
}
void Eatfood(pSnake ps,pSnakeNode cur)
{
	//吃到食物直接头插
	cur->next = ps->snake;
	ps->snake = cur;
	pSnakeNode del = ps->snake;
	while (del)
	{
		Setpos(del->x, del->y);
		wprintf(L"%lc", BODY);
		del = del->next;
	}
	//增加总分
	ps->Score += ps->FoodWeight;
	//释放食物
	free(ps->pFood);
	//创建新食物
	CreateFood(ps);
}

void NotEatfood(pSnake ps, pSnakeNode cur)
{
	//仍然尾插
	cur->next = ps->snake;
	ps->snake = cur;
	//最后一个不打印
	pSnakeNode del = ps->snake;
	while (del->next->next)
	{
		Setpos(del->x, del->y);
		wprintf(L"%lc", BODY);
		del = del->next;
	}
	Setpos(del->x,del->y);
	wprintf(L"%lc", BODY);
	//最后一个位置打印空格并释放
	Setpos(del->next->x, del->next->y);
	printf("  ");
	free(del->next);
	del->next = NULL;
}
//蛇的移动
void SnakeMove(pSnake ps)
{
	//创建 新节点
	pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (cur == NULL)
	{
		perror("malloc failed");
		exit(1);
	 }
	cur->next = NULL;
	//根据方向确定新结点的坐标
	switch (ps->dir)
	{
	  case UP:
		  cur->x = ps->snake->x;
		  cur->y = ps->snake->y - 1;
		break;
	  case DOWN:
		  cur->x = ps->snake->x;
		  cur->y = ps->snake->y + 1;
		break;
	  case LEFT:
		  cur->x = ps->snake->x - 2;
		  cur->y = ps->snake->y;
		break;
	  case RIGHT:
		  cur->x = ps->snake->x + 2;
		  cur->y = ps->snake->y;
		break;
	}
	//是否吃到食物
	if (IsFood(ps, cur))
	{
		//吃到食物
		Eatfood(ps,cur);
	}
	else
	{
		//没吃到食物
		NotEatfood(ps,cur);
	}
	//是否撞墙和咬到自己
	Kill_ByWall();
	Kill_BySelf()}

📌蛇是否撞墙和咬到自身

在进行上下左右移动时,我们可能会咬到自己或撞墙,这时游戏就会结束了我们需要进行游戏状态的更新

void Kill_ByWall(pSnake ps)
{
	pSnakeNode pcur = ps->snake;
	if (pcur->x == 0 || pcur->x == 56 || pcur->y == 0 || pcur->y == 26)
	{  //蛇头坐标不能等于这个墙壁的
		ps->status = KILL_BYWALL;
		return;
	}
	return;
}
void Kill_BySelf(pSnake ps)
{
	//遍历链表看蛇头是不是跟蛇身每个结点的坐标相等
	pSnakeNode cur = ps->snake;
	while (cur)
	{
		if (ps->snake->x == cur->x && ps->snake->y == cur->y)
		{
			ps->status = KILL_BYSELF;
			return;
		}
		cur = cur->next;
	}
	return;
}

三.游戏善后工作

退出循环后我们需要释放游戏资源同时根据游戏状态确定提示信息

🏠 贪吃蛇资源的释放

	//释放游戏资源
	pSnakeNode pcur = ps->snake;
	while (pcur)
	{
		pSnakeNode pNext = pcur->next;
		free(pcur);
		pcur = pNext;
	}

🏠 贪吃蛇游戏状态

//根据不同游戏状态进行提示
	switch (ps->status)
	{
	case ESC:
		Setpos(12,9);
		printf("游戏正常退出..");
		Setpos(12, 10);
		printf("游戏总分为%d", ps->Score);
		break;
	case KILL_BYWALL:
		Setpos(12, 9);
		printf("很遗憾你撞墙了!");
		Setpos(12, 10);
		printf("游戏总分为%d", ps->Score);
		break;
	case KILL_BYSELF:
		Setpos(12, 9);
		printf("很遗憾你咬到自己了!");
		Setpos(12, 10);
		printf("游戏总分为%d", ps->Score);
		break;
	}

🏠 拓展

到这里我们基本上我们的游戏了,除此之外我们可以进行拓展,比如把三个过程函数封装进循环里实现多次游玩

void testSnake(void)

{
	int input = 0;
	do
	{
		Snake s;
		//游戏运行前
		GamePre(&s);
		//游戏运行后
		GameRun(&s);
		//游戏
		GameEnd(&s);
		Setpos(0, 28);
		printf("你是否想再来一把(1/0):");
		scanf("%d", &input);
	} while (input);
	Setpos(0, 29);
}

除此之外我们可以运用win提供的颜色API该变蛇的颜色,还有可以加入背景音乐等等…

PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC);

第一个参数:资源文件–>新建–>资源文件–>Accelerator–>导入你的.wav文件
接着打开resource.h就能找到第一个参数啦
在这里插入图片描述
最后看下效果~
在这里插入图片描述


最后到这里分享内容就结束啦,小伙伴们能学到知识的话不妨来个一键三连,祝大家2024万事顺利~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/389382.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 开发一个密码管理app

密码管理app 介绍 最近发现自己的账号密码真的是太多了&#xff0c;各种网站&#xff0c;系统&#xff0c;公司内网的&#xff0c;很多站点在登陆的时候都要重新设置密码或者通过短信或者邮箱重新设置密码&#xff0c;真的很麻烦 所以准备开发一个app用来记录这些站好和密码…

使用TinyXML-2解析XML文件

一、XML介绍 当我们想要在不同的程序、系统或平台之间共享信息时&#xff0c;就需要一种统一的方式来组织和表示数据。XML&#xff08;EXtensible Markup Language&#xff0c;即可扩展标记语言&#xff09;是一种用于描述数据的标记语言&#xff0c;它让数据以一种结构化的方…

《区块链公链数据分析简易速速上手小册》第1章:区块链基础(2024 最新版)

文章目录 1.1 区块链技术概览&#xff1a;深入探究与实用案例1.1.1 区块链的核心概念1.1.2 重点案例&#xff1a;供应链管理1.1.3 拓展案例 1&#xff1a;数字身份验证1.1.4 拓展案例 2&#xff1a;智能合约在房地产交易中的应用 1.2 主流公链介绍1.2.1 公链的核心概念1.2.2 重…

深入理解lambda表达式

深入理解ASP.NET Core中的中间件和Lambda表达式 var builder WebApplication.CreateBuilder(args); var app builder.Build(); app.Use(async (context, next) > { // Add code before request. await next(context);// Add code after request.}); 这段C#代码是用于设…

杨中科 .netcore 依赖注入

1.概念 概念 生活中的“控制反转”:自己发电和用电网的电。 依赖注入(Dependency Injection&#xff0c;Dl)是控制反转:(Inversion of Control&#xff0c;l0c)思想的实现方式。 依赖注入简化模块的组装过程&#xff0c;降低模块之间的耦合度 自己发电的代码 var connSetti…

Peter算法小课堂—哈希与哈希表

额……字符串我们是第一次学&#xff0c;给大家铺一些基础的不能再基础的基础&#xff0c; 字符串比较大小 字符串大小的比较&#xff0c;不是以字符串的长度直接决定&#xff0c;而是从最左边第一个字符开始比较&#xff0c;大者为大&#xff0c;小者为小&#xff0c;若相等…

HTTP缓存技术

大家好我是苏麟 , 今天说说HTTP缓存技术 . 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) HTTP缓存技术 HTTP 缓存有哪些实现方式? 对于一些具有重复性的 HTTP 请求&#xff0c;比如每次请求得到的数据都一样的&#xff0c;我们可以把这对「请求-响…

OpenHarmony—UIAbility组件生命周期

概述 当用户打开、切换和返回到对应应用时&#xff0c;应用中的UIAbility实例会在其生命周期的不同状态之间转换。UIAbility类提供了一系列回调&#xff0c;通过这些回调可以知道当前UIAbility实例的某个状态发生改变&#xff0c;会经过UIAbility实例的创建和销毁&#xff0c;…

OS设备管理

设备管理 操作系统作为系统资源的管理者&#xff0c;其提供的功能有&#xff1a;处理机管理、存储器管理、文件管理、设备管理。其中前三个管理都是在计算机的主机内部管理其相对应的硬件。 I/O设备 I/O即输入/输出。I/O设备即可以将数据输入到计算机&#xff0c;或者可以接收…

【华为数通HCIP | 网络工程师】H12-831刷题日记 题目+解析(2)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

2007-2021年上市公司内控信息披露指数/上市公司内部控制信息披露指数数据

2007-2021年上市公司内控信息披露指数/上市公司内部控制信息披露指数数据 1、时间&#xff1a;2007-2021年 2、范围&#xff1a;上市公司 3、指标&#xff1a;证券代码、证券简称、辖区、证监会行业、申万行业、内部环境、风险评估、控制活动、信息与沟通、内部监督、内部控…

软考28-上午题-哈希表和堆

一、哈希表 将关键字作为自变量&#xff0c;使用哈希函数H(key)&#xff0c;得到该记录的存储地址。 这一映射过程&#xff0c;称为哈希造表、散列&#xff1b;所得的存储位置 哈希地址、散列地址。 1-1、冲突的定义 两个关键字K1和K2&#xff0c;K1 ! K2&#xff0c;&…

行测线上考试答案查找?推荐你使用这七个公众号和工具 #学习方法#经验分享

合理利用学习辅助工具和资料&#xff0c;可以帮助大学生更好地组织学习内容、掌握知识点和提升学术水平。 1.快解题 这是一个网站 是一款服务于职业考证的考试搜题软件,拥有几千万不同考试医学考试题库和执业医师试题库,通过章节练习,模拟试题,历年真题等练习来让不同的用户…

BLDC驱动刹车电路、能量泄放电路

不同STM32的性能; APM2.8飞控整合资料&#xff1a; APM2.8飞控说明书 GitBook BLDC的制动首先要考虑MOS的泄放电阻的选择&#xff0c;参考前面博客。 刹车电阻制动&#xff1a; 如图所示就是一种通过功率电阻耗散电机制动过程中产生电能的电路。因为功率电阻在这个电路中起…

开什么店最稳定轻松?适合一个人开的实体店推荐

在创业的道路上&#xff0c;很多人都希望找到一种稳定轻松的开店方式。 作为一名资深的鲜奶吧创业者&#xff0c;我将分享我的经验和见解&#xff0c;希望能给那些想开实体店的朋友们一些启示&#xff01;&#xff01; 我开鲜奶吧已经有 5 年时间了&#xff0c;目前经营的是鲜…

Leetcode-103. 二叉树的锯齿形层序遍历

这个年和树过不去啦啦啦&#xff01; 题目&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&…

C#入门及进阶|数组和集合(六):集合概述

1.集合概述 数组是一组具有相同名称和类型的变量集合&#xff0c;但是数组初始化后就不便于再改变其大小&#xff0c;不能实现在程序中动态添加和删除数组元素&#xff0c;使数组的使用具有很多局限性。集合能解决数组存在的这个问题&#xff0c;下面我们来学习介绍集合…

TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

红队笔记Day2 -->上线不出网机器

今天就来讲一下在企业攻防中如何上线不出网的机器&#xff01;&#xff01; 1.基本网络拓扑 基本的网络拓扑就是这样 以下是对应得的P信息&#xff0c;其中的52网段充当一个内网的网段&#xff0c;而111充当公网网段 先ping一下&#xff0c;确保外网ping不通内网&#xff0c;内…

threejs之使用shader实现雷达扫描

varying vec2 vUv; uniform vec3 uColor; uniform float uTime;mat2 rotate2d(float _angle){return mat2(cos(_angle),-sin(_angle),sin(_angle),cos(_angle)); }void main(){vec2 newUv rotate2d(uTime*6.18)*(vUv-0.5);float angle atan(newUv.x,newUv.y);// 根据uv坐标获…