当前位置: 首页 > news >正文

Dev-C++——winAPI贪吃蛇小游戏

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

🚀欢迎互三👉:雾狩 💎💎
🚀关注博主,后期持续更新系列文章
🚀如果有错误感谢请大家批评指出,及时修改
🚀感谢大家点赞👍收藏⭐评论✍

今天水一篇吧……😂老样子,先上效果在这里插入图片描述

一、概述

本文将对“登录 + 贪吃蛇”游戏的代码进行详细剖析。该游戏实现了用户登录和注册功能,同时包含经典的贪吃蛇游戏玩法,还支持游戏存档和加载功能。代码使用了 C++ 语言,并结合了 Windows 操作系统的 API 函数,以实现窗口化的游戏界面和多媒体功能。

二、代码结构与功能模块划分

1. 头文件与命名空间

#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <conio.h>
#include <cwchar>
#include <cctype>
#include <limits>
#include <stdio.h>
#include <cstdlib>
#include <ctime>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")using namespace std;
  • 包含了多个标准库和 Windows 相关的头文件,以实现输入输出、文件操作、随机数生成、时间处理和多媒体功能。
  • using namespace std; 允许直接使用标准库中的类和函数。

2. 结构体定义

// User结构体用于存储用户的基本信息
struct User {string username;string password;
};// SaveData结构体用于存储游戏的存档信息
struct SaveData {string username;time_t playTime;int totalPlayTime;int length;int mode;vector<int> x;vector<int> y;int ax, ay;int dir;unsigned long long tk;
};
  • User 结构体用于存储用户的用户名和密码。
  • SaveData 结构体用于存储游戏的存档信息,包括用户名、存档时间、总游戏时长、蛇的长度、游戏模式、蛇的坐标、苹果的坐标、蛇的移动方向和游戏时间戳。

3. 全局变量定义

vector<User> users;
vector<SaveData> saves;
const string DATA_FILE = "users.dat";
const string SAVE_FILE = "saves.dat";
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);#define SIZE 30
#define WIDTH 20
#define FPS 10
#define SPEED 3HWND m_hwnd;
int g_nWidth = WIDTH * SIZE + 10 + 300, g_nHeight = WIDTH * SIZE + 33;
int map[SIZE][SIZE];
std::vector<int> x, y;
int ax, ay;
int px, py;
int dir;
unsigned long long tk;
bool lock;
time_t startTime;
int currentMode;
int targetLength;
int currentSpeed;
string currentUsername;
bool isPaused = false;
bool isGameOver = false;
  • userssaves 分别存储所有用户信息和游戏存档信息。
  • DATA_FILESAVE_FILE 分别是用户数据文件和游戏存档文件的名称。
  • hConsole 用于控制控制台的输出。
  • 一系列宏定义了游戏地图的大小、方块宽度、帧率和初始速度。
  • 其他全局变量用于存储游戏的各种状态和信息。

4. 辅助函数

4.1 时间转换函数
string getTimeStr(time_t t) {char buffer[80];struct tm* timeinfo = localtime(&t);strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);return string(buffer);
}
  • 将时间戳转换为字符串格式,方便显示存档时间。
4.2 音乐播放函数
void playBackgroundMusic() {mciSendString("close background_music", NULL, 0, NULL);mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_music.mp3" type mpegvideo alias background_music)", NULL, 0, NULL);mciSendString("play background_music repeat", NULL, 0, NULL);isGameOver = false;
}void playVictoryMusic() {mciSendString("close background_music", NULL, 0, NULL);mciSendString("close fail_music", NULL, 0, NULL);mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_victory.mp3" type mpegvideo alias victory_music)", NULL, 0, NULL);mciSendString("play victory_music", NULL, 0, NULL);isGameOver = true;
}void playFailMusic() {mciSendString("close background_music", NULL, 0, NULL);mciSendString("close victory_music", NULL, 0, NULL);mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_fail.mp3" type mpegvideo alias fail_music)", NULL, 0, NULL);mciSendString("play fail_music", NULL, 0, NULL);isGameOver = true;
}void pauseAllMusic() {mciSendString("close background_music", NULL, 0, NULL);mciSendString("close victory_music", NULL, 0, NULL);mciSendString("close fail_music", NULL, 0, NULL);
}
  • 分别用于播放背景音乐、胜利音乐、失败音乐和暂停所有音乐,使用 mciSendString 函数进行多媒体操作。
4.3 文件操作函数
bool fileExists(const string& filename) {ifstream file(filename);return file.good();
}bool loadUsers() {if (!fileExists(DATA_FILE)) {return true;}ifstream file(DATA_FILE, ios::binary);if (!file) {return false;}char header[4];file.read(header, 4);if (string(header, 4) != "USR1") {file.close();return false;}size_t count;file.read(reinterpret_cast<char*>(&count), sizeof(count));users.clear();for (size_t i = 0; i < count; i++) {User user;size_t len;file.read(reinterpret_cast<char*>(&len), sizeof(len));user.username.resize(len);file.read(&user.username[0], len);file.read(reinterpret_cast<char*>(&len), sizeof(len));user.password.resize(len);file.read(&user.password[0], len);users.push_back(user);}file.close();return true;
}bool saveUsers() {ofstream file(DATA_FILE, ios::binary);if (!file) {return false;}const char* header = "USR1";file.write(header, 4);size_t count = users.size();file.write(reinterpret_cast<const char*>(&count), sizeof(count));for (const auto& user : users) {size_t len = user.username.length();file.write(reinterpret_cast<const char*>(&len), sizeof(len));file.write(user.username.c_str(), len);len = user.password.length();file.write(reinterpret_cast<const char*>(&len), sizeof(len));file.write(user.password.c_str(), len);}file.close();return true;
}
  • fileExists 用于判断文件是否存在。
  • loadUsers 用于加载用户数据,从二进制文件中读取用户信息。
  • saveUsers 用于保存用户数据,将用户信息写入二进制文件。
4.4 用户操作函数
bool registerUser(const string& username, const string& password) {for (char c : username) {if (!isalnum(c) && c != '_' && c != '.') {return false;}}User newUser;newUser.username = username;newUser.password = password;users.push_back(newUser);return saveUsers();
}bool authenticateUser(const string& username, const string& password) {for (const auto& user : users) {if (user.username == username && user.password == password) {return true;}}return false;
}string getPasswordInput() {string password;char ch;while ((ch = _getch()) != '\r') {if (ch == '\b') {if (!password.empty()) {password.pop_back();cout << "\b \b";}} else {password += ch;cout << '*';}}cout << endl;return password;
}
  • registerUser 用于用户注册,验证用户名的合法性并将新用户信息保存到文件中。
  • authenticateUser 用于用户登录验证,检查用户名和密码是否匹配。
  • getPasswordInput 用于获取用户输入的密码,输入时显示星号保护隐私。
4.5 其他辅助函数
void showMessage(const string& message, const string& title, UINT style) {MessageBox(NULL, message.c_str(), title.c_str(), style);
}string trim(const string& str) {size_t first = str.find_first_not_of(" \t\n\r");if (first == string::npos)return "";size_t last = str.find_last_not_of(" \t\n\r");return str.substr(first, (last - first + 1));
}void setConsoleColor(WORD color) {SetConsoleTextAttribute(hConsole, color);
}void setConsoleCursorPosition(short x, short y) {COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(hConsole, coord);
}
  • showMessage 用于显示消息框。
  • trim 用于去除字符串前后的空格。
  • setConsoleColor 用于设置控制台文本颜色。
  • setConsoleCursorPosition 用于设置控制台光标位置。

5. 游戏核心函数

5.1 初始化函数
void init() {srand((unsigned)time(NULL));tk = 0;dir = 2;x.push_back(0);y.push_back(0);px = py = 0;ax = -1;memset(map, 0, sizeof(map));map[0][0] = 1;lock = false;isGameOver = false;
}
  • 初始化游戏的各种状态,包括随机数种子、蛇的位置、苹果的位置和游戏地图等。
5.2 游戏结束处理函数
void gameover() {pauseAllMusic();playFailMusic();MessageBox(m_hwnd, "你输了", "游戏结束", MB_OK);x.clear();y.clear();init();startTime = time(NULL);
}void youwin() {pauseAllMusic();playVictoryMusic();MessageBox(m_hwnd, "你赢了", "游戏结束", MB_OK);x.clear();y.clear();init();startTime = time(NULL);
}
  • gameover 处理游戏失败的情况,播放失败音乐,显示消息框并重新初始化游戏。
  • youwin 处理游戏胜利的情况,播放胜利音乐,显示消息框并重新初始化游戏。
5.3 蛇移动函数
void move(int d) {if (d == 0 && x[0] > 0) {x.insert(x.begin(), x[0] - 1);px = x[x.size() - 1];x.erase(x.begin() + x.size() - 1);y.insert(y.begin(), y[0]);py = y[y.size() - 1];y.erase(y.begin() + y.size() - 1);} else if (d == 0 && x[0] <= 0) gameover();else if (d == 2 && x[0] < SIZE - 1) {x.insert(x.begin(), x[0] + 1);px = x[x.size() - 1];x.erase(x.begin() + x.size() - 1);y.insert(y.begin(), y[0]);py = y[y.size() - 1];y.erase(y.begin() + y.size() - 1);} else if (d == 2 && x[0] >= SIZE - 1) gameover();else if (d == 1 && y[0] > 0) {x.insert(x.begin(), x[0]);px = x[x.size() - 1];x.erase(x.begin() + x.size() - 1);y.insert(y.begin(), y[0] - 1);py = y[y.size() - 1];y.erase(y.begin() + y.size() - 1);} else if (d == 1 && y[0] <= 0) gameover();else if (d == 3 && y[0] < SIZE - 1) {x.insert(x.begin(), x[0]);px = x[x.size() - 1];x.erase(x.begin() + x.size() - 1);y.insert(y.begin(), y[0] + 1);py = y[y.size() - 1];y.erase(y.begin() + y.size() - 1);} else if (d == 3 && y[0] >= SIZE - 1) gameover();
}
  • 根据蛇的移动方向更新蛇的位置,如果蛇撞到边界则游戏失败。
5.4 游戏状态更新函数
void update() {if (isPaused || isGameOver) return;if (tk % currentSpeed == 0) {move(dir);lock = false;}if (x[0] == ax && y[0] == ay) {x.push_back(px);y.push_back(py);ax = -1;}if (currentMode != 5 && x.size() >= static_cast<size_t>(targetLength)) {youwin();return;}if (x.size() >= SIZE * SIZE) {youwin();return;}memset(map, 0, sizeof(map));for (size_t i = 0; i < x.size(); i++) {if (map[x[i]][y[i]] == 0) map[x[i]][y[i]] = 1;else {gameover();return;}}if (ax == -1) {ax = rand() % SIZE;ay = rand() % SIZE;while (map[ax][ay] == 1) {ax = rand() % SIZE;ay = rand() % SIZE;}}map[ax][ay] = 2;tk++;
}
  • 更新游戏的状态,包括蛇的移动、吃苹果、判断游戏胜利或失败、生成新的苹果等。
5.5 窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {switch (Message) {case WM_DESTROY: {PostQuitMessage(0);break;}case WM_KEYDOWN: {if (!lock && !isGameOver) {if (wParam == VK_UP && (dir - 0) % 2 != 0) {dir = 0;lock = true;} else if (wParam == VK_DOWN && (dir - 2) % 2 != 0) {dir = 2;lock = true;} else if (wParam == VK_LEFT && (dir - 1) % 2 != 0) {dir = 1;lock = true;} else if (wParam == VK_RIGHT && (dir - 3) % 2 != 0) {dir = 3;lock = true;}}if (wParam == VK_ESCAPE) {int result = MessageBox(hwnd, "确定要退出游戏吗?", "确认退出", MB_YESNO | MB_ICONQUESTION);if (result == IDYES) {PostQuitMessage(0);}}if (wParam == VK_SPACE) {isPaused = !isPaused;if (isPaused) {pauseAllMusic();} else {if (!isGameOver) {playBackgroundMusic();}}}if (wParam == VK_RETURN && isGameOver) {init();startTime = time(NULL);playBackgroundMusic();}break;}default:return DefWindowProc(hwnd, Message, wParam, lParam);}return 0;
}
  • 处理窗口的各种消息,包括窗口销毁、按键按下等。根据不同的按键操作,更新蛇的移动方向、暂停或继续游戏、退出游戏等。
5.6 游戏画面渲染函数
void render() {HDC hDC = GetDC(m_hwnd);HDC memDC = CreateCompatibleDC(0);HBITMAP bmpBack = CreateCompatibleBitmap(hDC, g_nWidth, g_nHeight);SelectObject(memDC, bmpBack);HPEN penBack = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));SelectObject(memDC, penBack);HBRUSH brushBack = CreateSolidBrush(RGB(255, 255, 255));SelectObject(memDC, brushBack);RECT rcClient;GetClientRect(m_hwnd, &rcClient);FillRect(memDC, &rcClient, brushBack);HBRUSH brushHead = CreateSolidBrush(RGB(255, 0, 0));HBRUSH brushBody = CreateSolidBrush(RGB(0, 128, 0));HBRUSH brushApple = CreateSolidBrush(RGB(0, 0, 255));int dw = WIDTH;int rows = SIZE;int cols = SIZE;for (int r = 0; r < rows; ++r) {for (int c = 0; c < cols; ++c) {if (map[r][c] == 1) {if (r == x[0] && c == y[0]) {SelectObject(memDC, brushHead);} else {SelectObject(memDC, brushBody);}} else if (map[r][c] == 2) {SelectObject(memDC, brushApple);} else {SelectObject(memDC, brushBack);}Rectangle(memDC, c * dw, r * dw, (c + 1)*dw, (r + 1)*dw);}}time_t currentTime = time(NULL);int elapsedTime = static_cast<int>(currentTime - startTime);int hours = elapsedTime / 3600;int minutes = (elapsedTime % 3600) / 60;int seconds = elapsedTime % 60;char info[200];sprintf(info, "蛇的长度: %u\n游戏时长: %02d:%02d:%02d\n难度: %d\n目标长度: %d\n\n规则:\n",static_cast<unsigned>(x.size()), hours, minutes, seconds, currentMode, targetLength);switch (currentMode) {case 1:strcat(info, "简单模式:蛇的长度达到15即可通关。\n");break;case 2:strcat(info, "普通模式:蛇的长度达到30即可通关。\n");break;case 3:strcat(info, "困难模式:蛇的长度达到60即可通关。\n");break;case 4:strcat(info, "地狱模式:蛇的长度达到80即可通关。\n");break;case 5:strcat(info, "无尽模式:蛇占满全屏即可通关。\n");break;}strcat(info, "\n操作步骤:\n");strcat(info, "方向键:控制蛇的移动方向。\n");strcat(info, "Space键:暂停/继续游戏。\n");strcat(info, "Esc键:退出游戏。\n");if (isGameOver) {strcat(info, "\n游戏已结束,按Enter键重新开始。\n");}SetBkMode(memDC, TRANSPARENT);SetTextColor(memDC, RGB(0, 0, 0));RECT infoRect = {SIZE * WIDTH + 20, 20, g_nWidth - 20, g_nHeight - 20};DrawText(memDC, info, -1, &infoRect, DT_LEFT | DT_TOP);DeleteObject(brushHead);DeleteObject(brushBody);DeleteObject(brushApple);BitBlt(hDC, 0, 0, g_nWidth, g_nHeight, memDC, 0, 0, SRCCOPY);DeleteObject(penBack);DeleteObject(brushBack);DeleteObject(bmpBack);DeleteDC(memDC);ReleaseDC(m_hwnd, hDC);
}
  • 渲染游戏画面,包括绘制蛇、苹果、游戏地图和显示游戏信息。使用双缓冲技术,避免画面闪烁。
5.7 游戏循环线程函数
DWORD WINAPI startLoop(LPVOID) {while (1) {update();render();Sleep(1000 / FPS);}return 0L;
}
  • 游戏的主循环,不断更新游戏状态和渲染画面,通过 Sleep 函数控制游戏帧率。

6. 游戏模式和存档相关函数

6.1 选择游戏模式函数
int selectGameMode(const string&) {int mode;system("cls");setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 3);cout << "===== 选择游戏模式 =====" << endl;setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(15, 5);cout << "1. 简单模式 (长度达到15通关)" << endl;setConsoleCursorPosition(15, 6);cout << "2. 普通模式 (长度达到30通关)" << endl;setConsoleCursorPosition(15, 7);cout << "3. 困难模式 (长度达到60通关)" << endl;setConsoleCursorPosition(15, 8);cout << "4. 地狱模式 (长度达到80通关)" << endl;setConsoleCursorPosition(15, 9);cout << "5. 无尽模式 (占满全屏通关)" << endl;setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 11);cout << "=======================" << endl;setConsoleCursorPosition(15, 13);cout << "请选择: ";while (!(cin >> mode) || mode < 1 || mode > 5) {cin.clear();cin.ignore(numeric_limits<streamsize>::max(), '\n');showMessage("输入无效,请输入1 - 5之间的数字!", "错误", MB_ICONERROR);setConsoleCursorPosition(15, 13);cout << "请选择: ";}cin.ignore();return mode;
}
  • 让用户选择游戏模式,根据用户输入返回相应的模式编号。
6.2 选择游戏存档函数
int selectSaveGame(const string& username) {vector<SaveData> userSaves;if (fileExists(SAVE_FILE)) {ifstream file(SAVE_FILE, ios::binary);if (file) {char header[4];file.read(header, 4);if (string(header, 4) == "SAV1") {size_t count;file.read(reinterpret_cast<char*>(&count), sizeof(count));saves.clear();for (size_t i = 0; i < count; i++) {SaveData save;size_t len;file.read(reinterpret_cast<char*>(&len), sizeof(len));save.username.resize(len);file.read(&save.username[0], len);file.read(reinterpret_cast<char*>(&save.playTime), sizeof(save.playTime));file.read(reinterpret_cast<char*>(&save.totalPlayTime), sizeof(save.totalPlayTime));file.read(reinterpret_cast<char*>(&save.length), sizeof(save.length));file.read(reinterpret_cast<char*>(&save.mode), sizeof(save.mode));file.read(reinterpret_cast<char*>(&len), sizeof(len));save.x.resize(len);for (int& val : save.x) {file.read(reinterpret_cast<char*>(&val), sizeof(val));}file.read(reinterpret_cast<char*>(&len), sizeof(len));save.y.resize(len);for (int& val : save.y) {file.read(reinterpret_cast<char*>(&val), sizeof(val));}file.read(reinterpret_cast<char*>(&save.ax), sizeof(save.ax));file.read(reinterpret_cast<char*>(&save.ay), sizeof(save.ay));file.read(reinterpret_cast<char*>(&save.dir), sizeof(save.dir));file.read(reinterpret_cast<char*>(&save.tk), sizeof(save.tk));saves.push_back(save);}}file.close();}}for (const auto& save : saves) {if (save.username == username) {userSaves.push_back(save);}}if (userSaves.empty()) {showMessage("没有可用的存档,将开始新游戏。", "提示", MB_ICONINFORMATION);return -1;}system("cls");setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 3);cout << "===== 选择存档 =====" << endl;for (size_t i = 0; i < userSaves.size(); i++) {setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(15, static_cast<short>(5 + i));cout << i + 1 << ". 存档时间: " << getTimeStr(userSaves[i].playTime)<< " 游玩总时长: " << userSaves[i].totalPlayTime << " 秒"<< " 长度: " << userSaves[i].length<< " 模式: " << userSaves[i].mode << endl;}setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, static_cast<short>(6 + userSaves.size()));cout << "0. 开始新游戏" << endl;setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, static_cast<short>(8 + userSaves.size()));cout << "=======================" << endl;setConsoleCursorPosition(15, static_cast<short>(10 + userSaves.size()));cout << "请选择: ";int choice;while (!(cin >> choice) || choice < 0 || static_cast<size_t>(choice) > userSaves.size()) {cin.clear();cin.ignore(numeric_limits<streamsize>::max(), '\n');showMessage("输入无效,请输入正确的存档编号!", "错误", MB_ICONERROR);setConsoleCursorPosition(15, static_cast<short>(10 + userSaves.size()));cout << "请选择: ";}cin.ignore();if (choice == 0) {return -1;} else {x = userSaves[choice - 1].x;y = userSaves[choice - 1].y;ax = userSaves[choice - 1].ax;ay = userSaves[choice - 1].ay;dir = userSaves[choice - 1].dir;tk = userSaves[choice - 1].tk;currentMode = userSaves[choice - 1].mode;startTime = time(NULL) - userSaves[choice - 1].totalPlayTime;switch (currentMode) {case 1: targetLength = 15; currentSpeed = 5; break;case 2: targetLength = 30; currentSpeed = 3; break;case 3: targetLength = 60; currentSpeed = 2; break;case 4: targetLength = 80; currentSpeed = 1; break;case 5: targetLength = SIZE * SIZE; currentSpeed = 3; break;}return choice - 1;}
}
  • 让用户选择游戏存档,如果有可用存档,显示存档信息供用户选择;如果没有可用存档,提示用户开始新游戏。选择存档后,加载存档信息。
6.3 保存游戏存档函数
void saveGame(const string& username) {int result = MessageBox(m_hwnd, "是否要保存存档?", "保存存档", MB_YESNO | MB_ICONQUESTION);if (result == IDYES) {time_t currentTime = time(NULL);int elapsedTime = static_cast<int>(currentTime - startTime);SaveData save;save.username = username;save.playTime = currentTime;save.totalPlayTime = elapsedTime;save.length = static_cast<int>(x.size());save.mode = currentMode;save.x = x;save.y = y;save.ax = ax;save.ay = ay;save.dir = dir;save.tk = tk;for (auto it = saves.begin(); it != saves.end(); ) {if (it->username == username) {it = saves.erase(it);} else {++it;}}saves.push_back(save);// 后续应添加将saves保存到文件的代码}
}
  • 询问用户是否保存存档,如果用户选择是,创建一个新的存档对象并保存到 saves 向量中,同时删除该用户的旧存档。

三、完整代码

下载地址
解压后,跟着操作步骤.txt下载源代码

四、总结

该代码实现了一个功能丰富的登录 + 贪吃蛇游戏,包括用户注册、登录、游戏存档和加载、多种游戏模式等功能。通过合理的结构体定义和函数封装,代码具有较好的模块化和可维护性。同时,使用 Windows API 实现了窗口化的游戏界面和多媒体功能,提升了游戏的用户体验。不过,代码中保存游戏存档的部分还未完成将 saves 向量保存到文件的操作,需要进一步完善。

http://www.lryc.cn/news/594414.html

相关文章:

  • codepen使用
  • 网鼎杯2020青龙组notes复现
  • AG32:解锁MCU+FPGA应用新姿势,功能与实战全解析
  • 《杜甫传》读书笔记与经典摘要(一)
  • 桑科草原一景
  • RabbitMQ:解锁高效消息传递的密码[特殊字符]
  • C++STL之stack和queue
  • 【pandoc实践】如何将wordpress文章批量导出为Markdown格式
  • Spring Boot 自动装配用法
  • 从0开始学linux韦东山教程Linux驱动入门实验班(4)
  • Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」
  • 零基础 “入坑” Java--- 十三、再谈类和接口
  • KOSMOS-2: 将多模态大型语言模型与世界对接
  • 算法训练营day25 回溯算法④ 补充联系题目 332.重新安排行程、51. N皇后、37. 解数独
  • PID控制原理分析及应用(稳态误差详细分析)(一)
  • 30天打牢数模基础-卷积神经网络讲解
  • STM32-第八节-TIM定时器-4(编码器接口)
  • 2025 年科技革命时刻表:四大关键节点将如何重塑未来?
  • 【高等数学】第四章 不定积分——第五节 积分表的使用
  • 【实战1】手写字识别 Pytoch(更新中)
  • RTC外设详解
  • Vuex 核心知识详解:Vue2Vue3 状态管理指南
  • Qt--Widget类对象的构造函数分析
  • 【vue-7】Vue3 响应式数据声明:深入理解 reactive()
  • 2024年青少年信息素养大赛图形化编程小低组初赛真题(含答案)
  • ZooKeeper学习专栏(二):深入 Watch 机制与会话管理
  • C语言:深入理解指针(2)
  • 网络地址和主机地址之间进行转换的类
  • 剑指offer66_不用加减乘除做加法
  • Spring Boot 订单超时自动取消的 3 种主流实现方案