news 2026/10/17 7:47:45

纯 Win32 API 实现的中国象棋 Demo:零依赖 GDI 绘图与消息循环教学

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
纯 Win32 API 实现的中国象棋 Demo:零依赖 GDI 绘图与消息循环教学

简介:本资源是一份基于Windows SDK开发象棋程序的完整实践项目,面向C++初学者及Windows平台GUI开发入门者,聚焦GDI绘图、Windows消息机制、棋局状态管理与基础AI逻辑实现等核心技能。压缩包共38个文件,含18个ICO图标资源(用于棋子与界面图标)、6个头文件(.h,定义棋盘结构、函数接口与宏常量)、4个CPP源码文件(实现绘图、思考、标准逻辑等模块)、3个光标文件(.cur,增强交互体验)及配套说明文档(.txt/.htm),整体仅24KB,轻量易读,结构清晰,便于逐模块分析学习。已有123人下载学习,适合通过小而完整的项目理解SDK底层编程范式——从窗口创建、鼠标事件响应、棋子拖放绘制,到将军检测与简易AI走法生成,所有代码均围绕Windows原生API展开,无第三方框架依赖,是掌握传统Win32开发路径的优质入门范例。

1. Windows SDK 象棋:一个不依赖 MFC、Qt 或 .NET 的纯原生 Win32 棋类 Demo,适合想啃透窗口消息循环与 GDI 绘图的新手练手

你可能试过用 Python 写个控制台象棋,也见过 Electron 打包的网页版中国象棋——但当你真正想搞懂「为什么点击棋盘某格,程序就知道该选马还是吃炮」,或者「双击红方‘车’后,怎么只在合法位置画上半透明圆点」,那些封装层就变成了黑匣子。这个 Windows SDK 象棋项目,就是一把没包浆的钢锉:它用纯 C 语言 + Win32 API(CreateWindowEx、GetMessage、BeginPaint、BitBlt 等)从零搭起整个界面,不调用任何第三方 UI 库,连资源文件都只用 .rc 定义图标和菜单。它不追求 AI 对战或网络联机,核心价值是把「鼠标坐标 → 逻辑坐标映射 → 棋子状态更新 → GDI 重绘」这条链路掰开揉碎,每一行代码都对应一个 Win32 编程基本功。如果你正卡在「学完《Windows 程序设计》第五章却写不出完整窗口」,或者想为后续开发 DirectX 游戏打下底层坐标与消息处理直觉,这个项目就是你该下载、编译、断点跟进去跑三遍的起点。


2. 项目结构与技术栈:Win32 API + GDI 绘图 + 纯 C 实现,无外部依赖

2.1 目录结构与文件职责划分

项目解压后共 7 个核心文件,全部位于根目录,无子文件夹:

文件名类型关键职责
chess.cC 源码主窗口过程(WndProc)、消息分发主干、全局变量定义(如g_hInst,g_hWnd)、WinMain 入口
board.cC 源码棋盘逻辑核心:InitBoard()初始化 10×9 数组、IsLegalMove()判断走法合法性(含马腿、象眼、将帅照面等规则)、MakeMove()执行落子并触发重绘
render.cC 源码GDI 渲染中枢:DrawBoard()绘制背景网格与楚河汉界、DrawPiece()根据棋子类型/颜色绘制汉字(“車”“砲”“士”等)、DrawValidMoves()在合法目标格绘制半透明圆点
input.cC 源码鼠标交互层:OnLButtonDown()捕获点击坐标、ScreenToBoard()将客户区像素坐标转为棋盘行列索引(关键转换函数)、HandleClick()协调选子/走子状态机
resource.h头文件资源 ID 定义:菜单项 ID(IDM_NEWGAME, IDM_EXIT)、加速键 ID(VK_F2)、图标 ID(IDI_CHESS)
chess.rc资源脚本定义菜单栏(“游戏”“帮助”)、图标、字符串表(如“新游戏”“退出”)
makefile构建脚本使用 MinGW-w64 的gcc编译:链接user32.lib,gdi32.lib,comctl32.lib,生成chess.exe

提示:项目未使用 Visual Studio 解决方案(.sln),而是面向命令行构建。这意味着你无需安装庞大 IDE,只要本地有 MinGW-w64(推荐 x86_64-10.2.0-release-win32-seh-rt_v9-rev1)或 Windows SDK 自带的cl.exe,就能直接make编译。这是 Win32 原生开发最轻量的启动姿势。

2.2 技术选型逻辑:为什么坚持纯 SDK?三个硬约束

这个项目的技术栈不是为了“复古”,而是由三个不可妥协的工程约束倒推出来的:

  1. 零运行时依赖:exe 运行时不需 .NET Framework、VC++ Redistributable 或 Qt DLL。chess.exe在 Windows 7 SP1 及以上系统双击即启,适合嵌入教学 U 盘或离线实训环境。
  2. 可调试性优先:所有逻辑都在 C 源码中,没有 MFC 的CView::OnDraw()封装、没有 Qt 的信号槽隐式连接。你在WndProc里下断点,能清晰看到WM_LBUTTONDOWN → WM_PAINT → WM_COMMAND的完整流转。
  3. 教学粒度可控:GDI 绘图(SelectObject,TextOut,Ellipse)比 DirectX 或 OpenGL 更易理解坐标系与设备上下文(HDC)概念;而 Win32 消息循环(GetMessage/TranslateMessage/DispatchMessage)正是理解 Windows GUI 本质的“第一课”。

所以当你看到render.c里反复出现GetDC(hWnd)和ReleaseDC(hWnd, hdc),这不是冗余代码,而是刻意暴露 HDC 生命周期管理——这正是很多新手在切换到 GDI+ 或 Direct2D 前必须亲手踩过的坑。

2.3 核心数据结构:棋盘状态如何用二维数组表达

整个游戏逻辑围绕一个int board[10][9]展开,每个元素存储整数编码的棋子:

// board.c 中定义 #define EMPTY 0 #define RED_KING 1 // 红帅 #define RED_ADVISOR 2 // 红士 #define RED_ELEPHANT 3 // 红象 #define RED_HORSE 4 // 红马 #define RED_CHARIOT 5 // 红车 #define RED_CANNON 6 // 红炮 #define RED_PAWN 7 // 红兵 #define BLACK_KING -1 // 黑将 #define BLACK_ADVISOR -2 // 黑士 // ... 同理,黑方为负值

注意:这种正负号区分红黑的设计,让IsLegalMove()函数能用signbit(board[x][y])快速判断归属方,避免冗长 if-else。更关键的是,它天然支持“吃子”逻辑——移动后直接赋值board[newX][newY] = board[oldX][oldY],旧位置清零,无需额外删除操作。

棋盘坐标系采用标准中国象棋约定:

  • 行(y):0~9,0 为红方底线(帅所在行),9 为黑方底线(将所在行)
  • 列(x):0~8,0 为左边界,8 为右边界
  • 屏幕像素坐标系(客户区)与棋盘逻辑坐标系的转换,由input.c中的ScreenToBoard()完成,其核心是:
// input.c void ScreenToBoard(POINT pt, int* row, int* col) { const int CELL_WIDTH = 40; // 每格宽高 40px(可配置) const int CELL_HEIGHT = 40; const int OFFSET_X = 20; // 左边距 20px(预留边框) const int OFFSET_Y = 20; // 上边距 20px *col = (pt.x - OFFSET_X) / CELL_WIDTH; *row = (pt.y - OFFSET_Y) / CELL_HEIGHT; }

这个函数看似简单,却是整个交互的基础——如果OFFSET_X/Y设错,或CELL_*与render.c中实际绘制尺寸不一致,就会出现“点哪不响应哪”的玄学问题。我们后面避坑章节会重点讲这个。


3. 编译与运行:MinGW-w64 一键构建,绕过 Visual Studio 依赖

3.1 环境准备:只需两步,5 分钟搞定

第一步:安装 MinGW-w64(推荐)
下载 MinGW-w64 Online Installer ,选择:

  • Architecture:x86_64
  • Threads:posix
  • Exception:seh
  • Version:10.2.0(稳定兼容 Win7+)
    安装路径建议纯英文无空格,例如C:\mingw64,并把C:\mingw64\bin加入系统 PATH。

第二步:验证 gcc 版本
打开 CMD,执行:

gcc --version # 应输出类似:gcc.exe (x86_64-posix-seh-rev1, Built by MinGW-W64 project) 10.2.0

若报错“不是内部命令”,请重启 CMD 并确认 PATH 设置正确。

提示:不要用 MSVC 的cl.exe编译此项目——虽然可行,但makefile中的链接参数(-luser32 -lgdi32)是 GCC 风格,MSVC 需改为/link user32.lib gdi32.lib,徒增配置成本。坚持 MinGW 是本项目“开箱即用”的设计前提。

3.2 编译命令详解:makefile 如何驱动整个流程

项目自带makefile,内容精简到 12 行,我们逐行拆解其作用:

# makefile CC = gcc CFLAGS = -Wall -Wextra -O2 -mwindows LDFLAGS = -luser32 -lgdi32 -lcomctl32 OBJS = chess.o board.o render.o input.o chess.exe: $(OBJS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f *.o chess.exe .PHONY: clean
  • CC = gcc:指定编译器为 GCC
  • CFLAGS = -Wall -Wextra -O2 -mwindows:
    -Wall -Wextra开启全部警告(新手必开,避免隐式类型转换错误);
    -O2二级优化(不影响调试);
    -mwindows关键!告诉链接器生成 Windows GUI 程序(而非控制台),避免运行时弹出黑框。
  • LDFLAGS = -luser32 -lgdi32 -lcomctl32:链接 Win32 核心库,comctl32用于后续可能扩展的公共控件(如状态栏)。
  • OBJS = ...:列出所有需编译的目标文件,chess.o依赖chess.c,以此类推。

执行编译只需一条命令:

make

成功后生成chess.exe,双击即可运行。

3.3 运行时行为与基础操作

启动后你会看到一个 400×440 像素的窗口(客户区),顶部是标准菜单栏(游戏→新游戏/退出,帮助→关于),中央是 10×9 棋盘网格,红黑双方棋子以红色/黑色汉字显示(“帥”“將”“車”“砲”等)。

基础操作逻辑:

  • 单击选子:点击己方棋子,该棋子高亮(边框加粗),同时在其所有合法移动位置绘制半透明灰色圆点(由DrawValidMoves()实现)。
  • 单击走子:再点击一个带圆点的位置,执行移动;若点击非圆点位置,则取消选择。
  • 新游戏:菜单→游戏→新游戏,重置棋盘为初始布局。
  • 退出:菜单→游戏→退出,或 Alt+F4。

注意:该项目暂未实现“将死”判定与胜利提示,属于教学精简版。但board.c中的IsKingInCheck()函数已预留接口,二次开发时可在此基础上扩展。


4. 避坑指南:新手编译与运行时最常遇到的 4 个血泪问题

4.1 现象:编译通过,但运行时窗口一闪而逝,或弹出“找不到 libgcc_s_seh-1.dll”

原因:MinGW-w64 默认动态链接运行时库(libgcc、libstdc++),而目标机器未安装对应 DLL。-mwindows参数虽隐藏控制台,但未解决 DLL 依赖。

解决:修改makefile中的CFLAGS,添加静态链接标志:

CFLAGS = -Wall -Wextra -O2 -mwindows -static-libgcc -static-libstdc++

重新make clean && make。此时生成的chess.exe体积增大(约 1.2MB),但彻底摆脱 DLL 依赖,可在任意 Win7+ 机器运行。

4.2 现象:棋盘绘制错位,网格线偏移,或点击位置与高亮格完全不匹配

原因:input.c中的ScreenToBoard()坐标转换参数(OFFSET_X/Y,CELL_WIDTH/HEIGHT)与render.c中DrawBoard()的实际绘制尺寸不一致。

解决:统一检查两处代码。render.c中DrawBoard()的关键绘制代码如下:

// render.c void DrawBoard(HDC hdc) { const int CELL_W = 40, CELL_H = 40; const int OFFSET_X = 20, OFFSET_Y = 20; // 绘制 10 行 × 9 列网格线... for (int i = 0; i <= 10; i++) { // 横线 11 条 MoveToEx(hdc, OFFSET_X, OFFSET_Y + i * CELL_H, NULL); LineTo(hdc, OFFSET_X + 9 * CELL_W, OFFSET_Y + i * CELL_H); } // ... 纵线同理 }

确保input.c中ScreenToBoard()的CELL_*和OFFSET_*与之完全相同。这是 Win32 GDI 开发中最经典的“坐标不同步”翻车点,务必手动核对,不要相信注释。

4.3 现象:点击棋子无反应,WndProc中WM_LBUTTONDOWN消息未被捕获

原因:窗口注册时未正确设置CS_HREDRAW | CS_VREDRAW风格,或CreateWindowEx的dwStyle参数遗漏WS_VISIBLE。

解决:检查chess.c中WinMain里的窗口创建代码:

// chess.c hwnd = CreateWindowEx( 0, // dwExStyle szWindowClass, // lpClassName L"Windows SDK 象棋", // lpWindowName WS_OVERLAPPEDWINDOW | WS_VISIBLE, // dwStyle ← 必须含 WS_VISIBLE! CW_USEDEFAULT, CW_USEDEFAULT, // x, y 480, 520, // width, height(含边框) NULL, NULL, hInstance, NULL);

同时确认RegisterClassEx中wcex.style = CS_HREDRAW | CS_VREDRAW;已设置。缺少CS_HREDRAW/VREDRAW会导致窗口缩放时无法自动重绘,间接影响鼠标捕获。

4.4 现象:中文棋子显示为方块(□)或乱码

原因:DrawPiece()中TextOut使用的字体不支持 GB2312 或 Unicode,或未正确设置字符集。

解决:render.c中DrawPiece()的字体创建必须指定GB2312字符集:

// render.c HFONT hFont = CreateFont( 24, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, GB2312_CHARSET, // ← 关键!必须是 GB2312_CHARSET OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Microsoft YaHei");

若系统无微软雅黑,可降级为L"SimSun"(宋体)。切勿使用ANSI_CHARSET,否则中文必然乱码。


5. 核心模块代码深度解析:从鼠标点击到棋子重绘的完整链路

5.1input.c:鼠标事件如何被翻译成棋盘坐标

input.c是整个交互的入口,其OnLButtonDown函数是第一个被触发的环节:

// input.c void OnLButtonDown(HWND hWnd, int x, int y, UINT keyFlags) { POINT pt = {x, y}; int row, col; ScreenToBoard(pt, &row, &col); // ← 第一步:像素→逻辑坐标 // 边界检查:确保点击在 0~9 行、0~8 列内 if (row < 0 || row > 9 || col < 0 || col > 8) return; int piece = g_board[row][col]; // ← 第二步:查棋盘状态 if (piece == EMPTY) { // 点击空格:尝试走子 if (g_selectedRow != -1 && g_selectedCol != -1) { if (IsLegalMove(g_selectedRow, g_selectedCol, row, col)) { MakeMove(g_selectedRow, g_selectedCol, row, col); g_selectedRow = g_selectedCol = -1; // 重置选择 InvalidateRect(hWnd, NULL, TRUE); // ← 第三步:触发重绘 } } } else { // 点击棋子:尝试选子 if (IsOwnPiece(piece, g_turn)) { // g_turn 为 1=红方回合,-1=黑方 g_selectedRow = row; g_selectedCol = col; InvalidateRect(hWnd, NULL, TRUE); } } }

逻辑说明:

  • ScreenToBoard()是坐标翻译的唯一入口,其精度决定交互体验。
  • g_selectedRow/Col是全局状态变量,记录当前选中的棋子位置(-1 表示未选)。
  • IsOwnPiece()通过piece * g_turn > 0判断归属(红方g_turn=1,黑方g_turn=-1),简洁高效。
  • InvalidateRect(..., TRUE)是关键:它向系统发送WM_PAINT消息,强制重绘整个客户区,从而触发render.c的绘制逻辑。

5.2board.c:走法合法性校验的实现细节

IsLegalMove()是规则引擎的核心,以“马走日”为例,展示其如何处理“蹩马腿”:

// board.c bool IsLegalMove(int fromRow, int fromCol, int toRow, int toCol) { int piece = g_board[fromRow][fromCol]; if (piece == EMPTY) return false; int dRow = toRow - fromRow; int dCol = toCol - fromCol; // 红马(5)与黑马(-5)统一处理 if (abs(piece) == RED_HORSE) { // 检查是否为“日”字:(dRow,dCol) ∈ {(±1,±2), (±2,±1)} if (!((abs(dRow)==1 && abs(dCol)==2) || (abs(dRow)==2 && abs(dCol)==1))) return false; // 蹩马腿检查:马脚位置必须为空 int legRow = fromRow + dRow/2; // 整数除法,如 (2,1)→(1,0.5)→(1,0) int legCol = fromCol + dCol/2; if (g_board[legRow][legCol] != EMPTY) return false; // 目标位置:不能吃己方棋子 int targetPiece = g_board[toRow][toCol]; if (targetPiece != EMPTY && ((piece > 0) == (targetPiece > 0))) return false; return true; } // 其他棋子(车、炮、士等)的规则分支... return false; }

参数说明:

  • fromRow/Col:起始坐标(已知选中棋子位置)
  • toRow/Col:目标坐标(鼠标点击位置转换而来)
  • dRow/dCol:行列差值,用于快速匹配“日”“田”“一”等模式
  • legRow/legCol:马腿坐标,利用整数除法自动取中(如从 (0,0) 走到 (2,1),马腿为 (1,0))

注意:此实现未处理“马被拴住”(马脚被己方棋子挡住但目标格是敌方)的边界情况,属于教学简化。真实项目需增加IsOwnPiece(g_board[legRow][legCol], piece > 0)判断。

5.3render.c:GDI 绘图如何分层渲染棋盘、棋子与提示点

render.c的OnPaint函数是重绘总调度:

// render.c void OnPaint(HWND hWnd) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // 1. 绘制背景(棋盘网格 + 楚河汉界) DrawBoard(hdc); // 2. 绘制所有棋子 DrawAllPieces(hdc); // 3. 若有选中棋子,绘制其合法移动点 if (g_selectedRow != -1 && g_selectedCol != -1) { DrawValidMoves(hdc, g_selectedRow, g_selectedCol); } EndPaint(hWnd, &ps); }

其中DrawValidMoves()的实现体现了 GDI 的“半透明”技巧(虽无 Alpha 混合,但用灰度模拟):

void DrawValidMoves(HDC hdc, int row, int col) { const int CELL_W = 40, CELL_H = 40; const int OFFSET_X = 20, OFFSET_Y = 20; HBRUSH hBrush = CreateSolidBrush(RGB(180, 180, 180)); // 浅灰色刷子 HPEN hPen = CreatePen(PS_SOLID, 1, RGB(120, 120, 120)); // 灰色边框 for (int r = 0; r < 10; r++) { for (int c = 0; c < 9; c++) { if (IsLegalMove(row, col, r, c)) { int x = OFFSET_X + c * CELL_W + CELL_W/2 - 5; int y = OFFSET_Y + r * CELL_H + CELL_H/2 - 5; // 绘制 10×10 像素圆点 SelectObject(hdc, hBrush); SelectObject(hdc, hPen); Ellipse(hdc, x, y, x+10, y+10); } } } DeleteObject(hBrush); DeleteObject(hPen); }

关键点:

  • Ellipse绘制圆点,尺寸固定为 10×10 像素,居中于格子中心。
  • RGB(180,180,180)的浅灰与棋盘底色(白色)形成柔和对比,既提示又不刺眼。
  • 每次调用都新建/删除 GDI 对象,避免资源泄漏——这是 Win32 GDI 编程的铁律。

6. 二次开发实战:给原生象棋加上“悔棋”功能与自定义棋子样式

6.1 悔棋功能:用栈结构保存历史步数

原项目无悔棋,但board.c中的MakeMove()已是原子操作,扩展成本极低。我们只需增加一个移动历史栈:

第一步:定义历史结构体与栈

// board.h(新增头文件) typedef struct { int fromRow, fromCol; int toRow, toCol; int captured; // 被吃的棋子(用于还原) } MoveRecord; #define MAX_UNDO 50 extern MoveRecord g_moveHistory[MAX_UNDO]; extern int g_undoTop; void PushMove(int fromRow, int fromCol, int toRow, int toCol, int captured); MoveRecord PopMove(); bool CanUndo();

第二步:修改MakeMove(),记录每一步

// board.c int g_undoTop = -1; MoveRecord g_moveHistory[MAX_UNDO]; void MakeMove(int fromRow, int fromCol, int toRow, int toCol) { int captured = g_board[toRow][toCol]; // 执行移动... g_board[toRow][toCol] = g_board[fromRow][fromCol]; g_board[fromRow][fromCol] = EMPTY; // 记录到栈 PushMove(fromRow, fromCol, toRow, toCol, captured); } void PushMove(int fromRow, int fromCol, int toRow, int toCol, int captured) { if (g_undoTop < MAX_UNDO - 1) { g_undoTop++; g_moveHistory[g_undoTop] = (MoveRecord){ fromRow, fromCol, toRow, toCol, captured }; } }

第三步:实现UndoMove()并绑定菜单

// board.c bool UndoMove() { if (!CanUndo()) return false; MoveRecord last = PopMove(); // 还原:目标格变回原棋子,起始格恢复被吃棋子(或清空) g_board[last.fromRow][last.fromCol] = g_board[last.toRow][last.toCol]; g_board[last.toRow][last.toCol] = last.captured; return true; } MoveRecord PopMove() { return g_moveHistory[g_undoTop--]; } bool CanUndo() { return g_undoTop >= 0; }

第四步:在chess.c的WndProc中响应菜单命令

// chess.c case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_UNDO: // 新增菜单项 ID if (UndoMove()) { InvalidateRect(hWnd, NULL, TRUE); // 可选:播放音效或弹窗提示 } break; // ... 其他 case } break;

提示:IDM_UNDO需在resource.h中定义,并在chess.rc的菜单资源中添加。这是 Win32 菜单编程的标准流程,一次配好,终身受用。

6.2 自定义棋子样式:从汉字切换为 PNG 图片

原项目用TextOut绘制汉字,要换 PNG,需引入LoadImage与TransparentBlt:

第一步:加载图片资源

// render.c HBITMAP g_hbmRedKing, g_hbmBlackKing; // 全局位图句柄 void LoadPieceImages() { g_hbmRedKing = (HBITMAP)LoadImage( NULL, L"red_king.png", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); g_hbmBlackKing = (HBITMAP)LoadImage( NULL, L"black_king.png", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); }

第二步:修改DrawPiece(),用TransparentBlt替代TextOut

void DrawPiece(HDC hdc, int row, int col, int piece) { const int CELL_W = 40, CELL_H = 40; const int OFFSET_X = 20, OFFSET_Y = 20; int x = OFFSET_X + col * CELL_W + 5; // 左边距 5px int y = OFFSET_Y + row * CELL_H + 5; // 上边距 5px HBITMAP hbm = NULL; switch (abs(piece)) { case RED_KING: hbm = (piece > 0) ? g_hbmRedKing : g_hbmBlackKing; break; // ... 其他棋子 } if (hbm) { HDC memDC = CreateCompatibleDC(hdc); HBITMAP hOld = (HBITMAP)SelectObject(memDC, hbm); TransparentBlt(hdc, x, y, 30, 30, memDC, 0, 0, 30, 30, RGB(255,0,255)); SelectObject(memDC, hOld); DeleteDC(memDC); } }

关键参数说明:

  • TransparentBlt最后一个参数RGB(255,0,255)是 Magenta(品红),作为透明色键。PNG 图片需用此色填充背景,才能实现透明效果。
  • 30×30是图片实际尺寸,需与棋盘格留白(x+5,y+5)匹配,避免溢出。

从那以后我每次做 Win32 图形项目,都会先写一个LoadPieceImages()和DrawPiece()的 stub,哪怕暂时用TextOut占位——因为图片替换只是改两行代码的事,而架构提前对齐,能省下后期重构的八成时间。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:46:53

氧化钇有什么特性?耐高温、抗烧结与潜在抑烟应用

在稀土氧化物家族中&#xff0c;氧化钇&#xff08;Y₂O₃&#xff09;以耐高温和抗烧结性能著称。它不像氧化铈那样有变价能力&#xff0c;也不像氧化镧那样常用于协效阻燃&#xff0c;但在高温结构材料和陶瓷领域&#xff0c;氧化钇有着不可替代的位置。高熔点与热稳定性氧化…

作者头像 李华
网站建设 2026/10/17 7:46:03

动态数组实现栈与队列:扩容缩容策略与工程实践

前几天做一个小工具&#xff0c;里面需要缓存一批待处理的消息&#xff0c;先发进来的先处理&#xff0c;后发进来的排队等。手边没有合适的第三方库&#xff0c;我索性自己写了一个队列。写的时候发现&#xff0c;最顺手的实现方式还是数组——准确地说&#xff0c;是动态数组…

作者头像 李华
网站建设 2026/10/17 7:45:49

React 自定义 Hook:useKeyPress 实现优雅的键盘监听与快捷键管理

做前端时间久的同学应该都有这种体会&#xff1a;页面只要涉及键盘操作&#xff0c;代码就容易变得又散又乱。尤其是那种中后台管理系统&#xff0c;表格里要支持上下左右移动选中行、CtrlC复制、Delete删除、F2编辑&#xff0c;一整套操作写下来&#xff0c;每个页面都塞一段w…

作者头像 李华
网站建设 2026/10/17 7:45:40

端侧智能体落地指南:4GB设备上的FunctionGemma函数调用实践

最近我把一个量化的 FunctionGemma 3B 模型塞进了一块 4GB 内存的开发板&#xff0c;让它从一个“只会聊天”的模型&#xff0c;变成了一个真的能执行动作的端侧智能体&#xff1a;能读日历、建日程、查天气、控制智能灯。这个过程踩了不少坑&#xff0c;也把“对话”到“行动”…

作者头像 李华
网站建设 2026/10/17 7:45:11

AI原生应用意图理解全攻略:从数据构造到服务化落地

做AI原生应用这一年多&#xff0c;我最大的感触是&#xff1a;很多团队把精力全砸在模型参数和Prompt模板上,却把一个最基础、最决定体验的模块给看轻了——用户意图理解。你产品功能再强&#xff0c;模型再大&#xff0c;用户一句话进来&#xff0c;你连他到底想干什么都没接住…

作者头像 李华
网站建设 2026/10/17 7:44:57

SSL/TLS协议核心解析:握手流程、证书体系与排障实战

1. SSL 协议的核心定位&#xff1a;网络传输安全的基石1.1 网络传输中的安全痛点先聊一个最直接的场景。你在浏览器里输入账号密码&#xff0c;点击登录&#xff0c;数据从你的电脑发往服务器。这段旅程如果走的是一条没有任何保护的路&#xff0c;中间任何一个节点——路由器、…

作者头像 李华