news 2026/10/24 1:17:10

C/C++图形编程入门:用EasyX库做小游戏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C/C++图形编程入门:用EasyX库做小游戏

很多人在学完C语言、C++的语法基础之后,都会遇到同一个尴尬:printf能打印出漂亮的字符,却做不出一个“有画面”的程序——没有窗口,没有鼠标,没有能动的方块,更别提做游戏了。我当年也是卡在这条线上,直到碰上了EasyX这个图形库,才算真正把“学过的C/C++”变成了“能用的C/C++”。

这篇东西就是围绕EasyX图形库基础来写的。它不是什么高深引擎的入门,也不是复杂框架的剖析,而是解决一个最现实的需求:用你最熟悉的C/C++语言,以最靠近底层的方式,打开一个窗口、画点东西、响应键盘鼠标、跑起来一个游戏循环。这篇文章适合谁?刚学完C/C++想接触图形编程的同学、学校课程里要求做课设但不知道从哪入手的兄弟、还有那些在控制台里玩腻了想换点新鲜感的自学者。我尽量把常见配置、核心函数用法、还有我踩过的坑都讲清楚,保证你看完能自己敲出第一个小游戏。

1. 项目概述:EasyX是什么,为什么是它

1.1 核心需求解析

先说清楚EasyX到底是个什么东西。它本质上是针对Windows平台的一个图形库,把Windows原生API(Win32 GDI)里那些复杂的窗口创建、设备上下文、绘图调用封装成一套更接近初学者习惯的C/C++函数接口。也就是说,它解决的不是“怎么用图形库”,而是“怎么让只会C/C++的人也能做图形程序”这件事。

为什么初学者特别容易卡在图形这一步?因为Windows窗口程序设计本身的门槛和游戏逻辑无关——你要先处理“窗口类注册”“消息循环”“设备上下文”这一堆和画图无关的样板代码,才能开始画一个小圆点。这对抱着“我想做个游戏”的人来说,完全是劝退的。EasyX的价值就在这,它把这些样板代码全部藏起来了。你只需要包含头文件、调用initgraph、画图、监听输入,剩下的交给库里头的默认实现。

我以前带过一个社团的同学,他用Dev-C++写学生管理系统都很溜,一打开Win32空项目整个人懵了,连入口函数都找不到该写在哪。换成EasyX之后半小时就画出了会动的小球。所以我说,EasyX不是最强大的,但它是最适合从“会语法”过渡到“有作品”的那座桥。

1.2 影响范围与典型应用场景

EasyX能做的图形程序范围,远超很多人的想象。它虽然简单,但并不是玩具。在我见过和做过的项目里,有几类特别典型:

  • 教学演示类:排序算法可视化、数据结构图表演示、数学模型绘制。这类不需要太高的刷新率,重点是把抽象的过程可视化成图形,EasyX的画线、填充、文字接口用起来非常顺手。
  • 2D小游戏类:俄罗斯方块、贪吃蛇、打砖块、飞机大战,这些经典练手项目是EasyX用户量最大的一类。它支持贴图、透明处理、双缓冲绘图,2D小游戏的常规需求基本都覆盖了。
  • 交互式工具类:简易画板、波形显示、模拟面板。EasyX的消息函数能挂接鼠标和键盘,做点小工具足够用。
  • 课程设计类:每年期末都会有一大批“XX管理系统(图形版)”的项目出现,其实就是在控制台的程序外面套了一层图形界面的壳。

影响范围上,EasyX官方文档和社区资源都很成熟,网上能查到大量源码示例。我个人的体会是,它特别适合用来训练“把逻辑和界面分开”的思维:游戏逻辑是纯C/C++代码,渲染和交互交给图形库接口,这个分层习惯对以后学任何图形框架都有帮助。

2. 环境准备:安装配置与工程选型

2.1 安装流程与版本选择

EasyX的安装极其简单,去官网下载安装包,里面是一个对应版本的文件,下载后运行,它会自动检测你机器上安装的Visual C++版本,勾选对应版本后点安装就完成了。整个过程不需要配置环境变量,不需要复制DLL到系统目录,它对新手友好得不像一个图形库。

不过这里有几个细节,我吃过亏所以特意说一下:

  • 如果你机器上装了两三个版本的Visual Studio(比如VS2017和VS2022并存),安装程序会列出所有检测到的版本,建议全部都勾上。不然你写了代码,用没装库的那个版本编译,会报一堆找不到头文件的错。
  • 64位系统上跑EasyX工程,项目属性里的“平台”选x64还是Win32都能运行。库本身对两种平台都支持,但如果你混用第三方库,就要注意匹配。
  • 我自己做项目习惯统一用Release + x64编译,发布给别人的时候也省事。

安装完之后,要验证安装成功,最简单的办法是新建一个空项目,写上下面这段代码,如果能弹出窗口并显示文字,就说明环境没问题:

#include <graphics.h> #include <conio.h> int main() { initgraph(640, 480); settextstyle(40, 20, _T("宋体")); outtextxy(100, 100, _T("EasyX 环境测试 OK")); _getch(); closegraph(); return 0; }

2.2 IDE工程选项配置要点

很多新手在这个环节会卡住,所以我多写几句。EasyX对IDE的要求很宽松,VS全系都可以用,Dev-C++需要折腾一点,Code::Blocks需要做Makefile配置,我个人还是推荐用Visual Studio来学,集成度最好,调试也方便。

工程创建之后,有四个地方建议提前确认好:

配置项推荐设置说明
字符集使用多字节字符集(或Unicode均可)默认情况下VS新项目是Unicode,EasyX兼容,但用_T()宏包住字符串最保险
SDL检查关闭SDL检查不关的话,某些与main冲突的调用可能编译不过
附加依赖项winmm.lib(部分版本需要)有些动画和声音API需要这个库
平台工具集保持默认版本不需要为了EasyX降级工具集

注意:如果链接阶段报“无法解析的外部符号”,多半是附加依赖项没加全。在工程属性->链接器->输入->附加依赖项里手动加上winmm.lib试一下,这是最常见的一个坑。

3. 核心API拆解:我平时最常用的这批函数

3.1 绘图基础:坐标、颜色与画笔

EasyX的坐标系统很好理解:窗口左上角是原点(0,0),x轴向右为正,y轴向下为正,单位是像素。这和数学课上那种左下角原点、y向上为正的坐标系不一样,初学时常有人把图形画反了,其实只要记住屏幕坐标和数学坐标的差异,多画几个点就有了手感。

颜色上,EasyX用16进制COLORREF表示颜色,格式是0xBBGGRR,和常见的0xRRGGBB顺序反着来,知道有这回事就行,平时直接用宏RGB(r,g,b)来生成颜色最省心:RGB(255,0,0)是纯红色,RGB(0,255,0)是纯绿,RGB(0,0,255)是纯蓝。淡一点的颜色就是把三个分量调小,比如粉红色是RGB(255,200,200)。

绘图的核心理念是“先设置画笔/画刷,再绘制图形”。EasyX里对应的机制是:你可以设置当前画笔的颜色、线宽,然后调用circle、rectangle、line这些函数去画。这种“画笔模式”跟Windows GDI的思路一致,其实跟现实里拿笔在纸上作画也差不多——你先把笔换了,画出来的线条就是这种风格。

我最初写的图形程序经常有个问题:画完一个图形后接着画下一个,结果前一个的颜色“自动”变了。后来才明白,不是图形库有问题,是我在两次绘图之间改动了全局画刷设置。如果你想让每个图形颜色独立,记得在每次绘制前重置画笔和画刷。

3.2 常用绘图函数速查表

先给一张我平日用得最多的函数表,照着写不容易出错:

功能函数签名备注
画点putpixel(int x, int y, COLORREF color)注意它需要指定颜色,画大量点时性能一般
画线line(int x1, int y1, int x2, int y2)使用当前画笔颜色和线宽
画圆circle(int x, int y, int radius)只画轮廓,不带填充
填充圆solidcircle(int x, int y, int radius)画实心圆,覆盖性好
画矩形rectangle(int left, int top, int right, int bottom)对角线两个点确定矩形
填充矩形solidrectangle(int left, int top, int right, int bottom)常用于障碍物、背景块
画椭圆ellipse(int left, int top, int right, int bottom)以内切矩形的方式定义椭圆
圆角矩形roundrect(int left, int top, int right, int bottom, int ellipseWidth, int ellipseHeight)做对话框、按钮很有用
填充圆角矩形solidroundrect(...)与上面类似,参数一样
输出文字outtextxy(int x, int y, LPCTSTR str)需要配合settextstyle设置字体
设置背景色setbkcolor(COLORREF color)配合cleardevice()使用

这里有个小要点:circle的第三个参数是半径,不是直径。这个概念很基础,但真的有人会传直径进去,结果画出来的圆大了一圈。我还见过有人问“为什么circle画出来的圆自己会变形”,其实是窗口宽高比例和屏幕像素宽高比不一致导致的视觉畸变,不是函数的问题。

3.3 窗口控制、刷新与双缓冲

窗口相关API也是根基础,但有几个细节容易被忽略:

  • initgraph(int width, int height):初始化指定大小的绘图窗口。函数还可以接受第三个参数,用来设置窗口样式(比如无边框)。
  • closegraph():关闭图形窗口,释放绘图资源。注意,这个函数和_getch()配合使用能实现“按任意键退出”的效果。如果忘了调用closegraph(),有些环境会出现程序结束但窗口残留或黑屏的情况。
  • cleardevice():用当前背景色清空整个绘图区域。每个新帧开始前,你通常都要先调用它,不然上一帧的图像会残留。
  • BeginBatchDraw()/EndBatchDraw()/FlushBatchDraw():这是一组极重要的函数。简单来说,批量绘制可以让所有绘图操作先发生在内存中,调用FlushBatchDraw()时才一次性把画面呈现到屏幕上,这样可以有效避免画面闪烁。

双缓冲的原理,我用一个生活化的例子讲:手工黑板报如果边写边给同学看,你写一笔大家看到一笔,中间所有的“半成品状态”都暴露了,非常乱。而先在一张草稿纸上把所有字画好,最后直接把透明贴纸贴到黑板上,大家看到的就永远是完整、干净的成品。BeginBatchDraw()就是在草稿纸上开工,FlushBatchDraw()就是贴成品那一下。这也是游戏渲染不闪的基础逻辑。

如果你发现自己程序里的图形疯狂闪烁,先别怀疑电脑配置,先检查自己有没有用BeginBatchDraw()和FlushBatchDraw()。我见过有人把这两个函数放在每次绘图的循环体里,正确用法是循环外开启、循环里刷帧。

3.4 图像贴图与透明处理

EasyX的loadimage和putimage可以实现把图片文件(bmp/jpg/png等)加载并绘制到窗口。初学者做游戏,最容易在这里踩“黑底盖住精灵”的坑。

  • loadimage(IMAGE* dst, LPCTSTR file, int width = 0, int height = 0):从文件加载图片到IMAGE对象。
  • putimage(int x, int y, IMAGE* src, DWORD dwRop = SRCCOPY):把图片绘制到窗口指定位置。
  • putimage的透明玩法:当src画面里有某种 “关键色”(通常是洋红色RGB(255,0,255)或黑色)时,可以通过设置dwRop参数为SRCAND或配合掩码图来实现透明效果。

但是!这种透明其实是很复古的做法,现代做游戏建议直接用带Alpha通道的PNG图片,配合EasyX的putimage加上TRANSPARENT参数也能实现半透明效果。我个人的做法是:所有精灵图都统一导出成带透明通道的PNG,加载后直接用,省时省心;只有一些老素材才用“关键色抠图”的方式。

另外要注意,loadimage一次只能加载一个图片对象,如果游戏里有几十张图,你需要管理好你的IMAGE数组或对象池,反复load会拖慢速度,而且容易内存泄漏。我的经验是,把所有图片在initgraph后一次性load到一个数组里,之后再putimage就只复制不读盘了。

3.5 键盘鼠标输入处理

一个交互式程序,如果没有输入,就只是个画板。EasyX处理输入的方式很朴实,你可以在循环里去主动查“有没有按键”,或者用消息函数“等”事件。

  • _getch():等待一个键盘按键并返回其ASCII码,在控制台时代就能用,EasyX里依然有效。
  • kbhit():非阻塞式检测“是否有键按下”,有返回非0。
  • MOUSEMSG结构体:配合MouseHit()和GetMouseMsg()获取鼠标消息,结构体里有x、y、uMsg字段,uMsg是WM_LBUTTONDOWN这种消息标识。
  • ExMessage结构体:新版EasyX提供了更现代的消息机制,一个结构体包含了消息类型、按键虚拟码、鼠标坐标、滚轮数据等。用peekmessage()可以非阻塞读取,用getmessage()则会阻塞等待,适合做“等用户点击”的流程。

我在做小游戏的时候,输入处理的代码通常写成这种“事件泵”的形态:

while (true) { // 循环体开始 flushmessage(EM_KEY | EM_MOUSE); // 可选的:清空消息队列 ExMessage msg; while (peekmessage(&msg)) { if (msg.message == WM_KEYDOWN) { // 根据 msg.vkcode 处理按键 } else if (msg.message == WM_LBUTTONDOWN) { // 鼠标左键点击,msg.x 和 msg.y 是点击位置 } } // 游戏逻辑更新 // 绘制 FlushBatchDraw(); Sleep(10); // 简单控制帧率,实际项目里推荐用时间戳 }

这种消息泵的好处是不阻塞,游戏循环一直跑,不会因为“等用户按键”导致动画卡住。我见过很多新手写_getch()直接卡住整个游戏,等按完键画面才动,这就是阻塞式输入和游戏主循环冲突的典型例子。

4. 实战:做一个会动的俄罗斯方块引擎

学完API,最后一个实战环节我选了俄罗斯方块。为什么选它?因为俄罗斯方块的核心逻辑是“图形块 + 边界判断 + 碰撞检测 + 消行计分”,图形库用得最少,但C/C++语言的数组、循环、状态机用得特别扎实。做通这个项目,你对EasyX的窗口管理、绘图、输入响应、刷新控制就全通了。

4.1 游戏逻辑与数据建模

俄罗斯方块的本质,是维护一个“宽10格、高20格”的二维数组。每一格的值代表当前是否有方块占用(0是空,1或颜色值表示有方块)。方块本身也是一个二维数组,用0和1表示形状。

我在做这个项目时,最深的体会是:图形库只负责把数组里的数据画出来,真正的游戏性全在数组里。你写游戏的时候,脑子里要砍成两个世界——逻辑世界是数组,渲染世界是像素。这两个世界通过坐标映射关联起来。

坐标映射公式很简单:

  • 列的x像素 = 列号 * 格子大小 + 偏移
  • 行的y像素 = 行号 * 格子大小 + 偏移

以格子大小30px为例,第2列第3行的格子,绘制的左上角就是(2*30, 3*30)。这个映射关系写清楚后,后续所有碰撞检测和渲染都可以统一处理。

4.2 方块碰撞与旋转的核心实现

方块的碰撞检测,核心就是“把方块的相对坐标加上全局偏移后,逐格去检查目标数组区域能不能放进去”。伪代码如下:

bool canPlace(int blockType, int rotation, int gridX, int gridY) { for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) { // 只处理方块占用为1的格子 if (shape[blockType][rotation][r][c] == 0) continue; int boardRow = gridY + r; int boardCol = gridX + c; // 超出边界:不可放置 if (boardCol < 0 || boardCol >= BOARD_WIDTH) return false; if (boardRow >= BOARD_HEIGHT) return false; // 如果底部还没出界,但目标位置已经有方块 if (boardRow >= 0 && board[boardRow][boardCol] != 0) return false; } return true; }

旋转的实现有个常见的坑:方块旋转后长宽比例变了,碰撞框也要跟着变。我的做法是,把所有方块(含旋转形态)都预生成成4×4的二维数组,这样旋转逻辑就不再是复杂的坐标变换了,而是一个“查表”:rotation加1对4取余,就能得到下一形态。这个方法代码量最少,而且不容易算错。

4.3 渲染关键代码与界面布局

这一节是图形库的核心应用。窗口逻辑上分三块:游戏区、下一个方块预览区、分数显示区。我的布局是:游戏区左上角在(0,0),宽1030=300px,高2030=600px;预览区在游戏区右侧,差不多(320,100);分数显示在(320,250)。

渲染的代码思路是这样的:

void render() { cleardevice(); // 画背景格 setlinecolor(RGB(200, 200, 200)); for (int x = 0; x <= BOARD_WIDTH; x++) { line(x * GRID_SIZE, 0, x * GRID_SIZE, BOARD_HEIGHT * GRID_SIZE); } // 画已落地的方块 for (int row = 0; row < BOARD_HEIGHT; row++) { for (int col = 0; col < BOARD_WIDTH; col++) { if (board[row][col] != 0) { setfillcolor(board[row][col]); solidrectangle(col * GRID_SIZE, row * GRID_SIZE, (col + 1) * GRID_SIZE, (row + 1) * GRID_SIZE); } } } // 画当前下落的方块 for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { if (curShape[r][c] != 0) { int px = (curGridX + c) * GRID_SIZE; int py = (curGridY + r) * GRID_SIZE; setfillcolor(curColor); solidrectangle(px, py, px + GRID_SIZE, py + GRID_SIZE); } } } // 绘制预览与分数 // ... FlushBatchDraw(); }

这段渲染代码有几个心得:

  • 为了让每个方块看起来立体一点,我习惯用setlinecolor加上浅色外框缝线,具体是在每个solidrectangle外面套一个rectangle画边框。视觉效果好很多。
  • 在清屏之前记得setbkcolor,不然后台缓存可能会用上一次的背景色。
  • 整体绘制顺序一定要稳定,先画背景网格、再画已固定方块、再画移动中的方块。如果顺序乱了,移动方块会被背景网格盖住,出现“半透明”的错觉。

4.4 计时器与帧率控制的实践

很多初学者做俄罗斯方块,会直接用Sleep(500)来控制下落速度。这在小项目里可行,但有几个问题:

  • Sleep会阻塞整个线程,导致按键响应的灵敏度变差了。
  • 下落速度想随关卡加快时,调整Sleep的值不方便。
  • 不同机器上时间精度不一样,Sleep的实际阻塞时长有波动。

我的做法是用一个独立累加器,在主循环里累加GetTickCount()或clock()的差值,当累计时间超过“下落间隔”时,才执行一次下落。

DWORD lastTime = GetTickCount(); while (true) { DWORD now = GetTickCount(); DWORD elapsed = now - lastTime; if (elapsed >= dropInterval) { // 执行下落 MoveDown(); lastTime = now; } // 按键处理 // 渲染 Sleep(16); // 至少给系统一点喘息,控制在大约60FPS }

这种“时间戳驱动”的模式,以后你学任何游戏引擎都会遇到,先在这里养成习惯不亏。

5. 常见问题与排错实录

5.1 编译报错排雷

我整理了一张常见编译错误表,基本都是队友和群友问过我无数次的。

报错现象常见原因解决方案
fatal error C1083: Cannot open include file: 'graphics.h'没安装EasyX,或装的时候漏选了当前VS版本重装EasyX,勾选全部版本
无法解析的外部符号 _main,该符号在函数...中被引用工程类型选了Windows应用程序,但代码入口还是控制台的main把工程类型改成“控制台应用程序”
无法解析的外部符号 _WinMain@16工程类型是控制台,但代码里没有main,写了WinMain要么改工程类型,要么把入口统一成main
error C2664: “void std::...”: 不能将参数从“const char [..]”转换为“LPCTSTR”字符集不匹配或字符串没加_T()用_T("字符串")包住,或把字符集改成多字节
warning C4996: 'fopen' was deprecated使用了一些老C函数在代码头加#define _CRT_SECURE_NO_WARNINGS

这里特别说明一下_T()宏的意义。如果你工程是Unicode字符集,"abc"这种字面量是窄字符,而LPCTSTR在Unicode下是宽字符LPCWSTR,类型不匹配就会报上面那个C2664错误。用_T("abc")后,编译器会自动根据字符集把字符串替换成正确类型。这是从MFC时代传下来的老传统,EasyX保留了兼容,所以写代码时养成加_T()的习惯能省很多事。

5.2 运行时表现异常排查

图画出来了、但表现很怪,这类问题也特别多。我按严重程度排一下:

  • 窗口打开后黑屏或白屏:最常见的原因是initgraph之后直接输出了文字,但没有刷新画面。解决方法是输出文字之后加FlushBatchDraw(),或者用Sleep(100)等待一帧。
  • 画面闪烁严重:前面提到了,没有用BeginBatchDraw()/FlushBatchDraw()。或者你把这俩函数放进了循环里而不是循环外。另外如果每一帧都cleardevice(),再逐个重画,也会闪,改用批量绘制就解决了。
  • 方块掉落速度不规律,时快时慢:多半是因为你在下落逻辑里用了Sleep,但不同帧里按键处理、绘制耗时不同。换成时间戳累加器就好。
  • 方块越界或穿模:碰撞判定里的边界条件写错了,或者旋转形态和实际碰撞框不匹配。这种问题要善用调试输出:在逻辑循环里把当前方块的坐标、方向打印到控制台或窗口上,一眼就看到了。
  • 窗口关不掉或退出异常:closegraph()前有阻塞调用没返回,比如getmessage()在等一个永远不来的消息。检查你的消息循环有没有在游戏结束时正确退出。

5.3 关于EasyX的一些切身建议

最后说点不那么技术、但很有用的个人经验。

第一,EasyX画的窗口不是真正的独立窗口,它在底层还是依赖于控制台窗口,所以运行时你会看到一个命令行窗口和一个图形窗口并存。这个外观对“发布给别人用”来说不太好看,但作为学习和课设完全没问题。如果你要发布正式一点的程序,后面可以考虑换个图形框架,但学习期别折腾。

第二,EasyX的调试体验在VS里其实相当好。因为图形窗口和控制台可以并存,你可以用printf往控制台打日志,这对排查逻辑问题特别有帮助——图形程序最怕的就是看不出状态,有控制台输出就踏实多了。我做的项目里,控制台常年打印方块坐标、当前分数、消行数。

第三,用EasyX写游戏,很快就你会发现,游戏的灵魂不在“图形库好不好看”,而在“逻辑封装”和“状态管理”。方块落地后怎么合并进棋盘、消行怎么处理、游戏结束怎么判断,这些才是代码的精华。当你开始琢磨这些问题的时候,说明你已经不是在学图形库,而是在学做游戏了。

最后再分享一个小技巧:做任何图形项目,一开始别追求华丽特效,先用最简单的方形代表物体,把逻辑跑通,再把方块替换成图片。我在俄罗斯方块项目里,方块一开始是纯色矩形,颜色全靠RGB生成,后来才加格线和阴影。这样思路不被打断,调试也快。图形库终究是工具,真正的功底,永远是你的C/C++底子和数据结构能力。

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

全微分方程探秘:势函数、积分因子与Python实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:16:33

从零构建回归预测模型:基于交易数据的特征工程与LightGBM实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:16:02

PCA9422与STM32F101ZG的工业级电源管理协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:15:39

PCA9422+MCU协同:多路电源上电时序与I2C寄存器配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:15:01

STM32F767与PCA9422协同电源管理实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:14:35

UltraScale架构深度解析:从Spartan到Virtex的选型与设计要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华