news 2026/10/10 19:41:46

用Cocos Creator开发斗地主微信小游戏Demo:从状态管理到真机适配全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Cocos Creator开发斗地主微信小游戏Demo:从状态管理到真机适配全流程

简介:基于 Cocos Creator 开发的斗地主微信小游戏 Demo,面向希望学习微信小游戏开发的 Cocos 开发者,尤其适合想从零搭建完整工程、理解项目结构的入门与中级使用者。压缩包共 470 个文件,包含 TS 逻辑脚本、Prefab 预制体、场景与动画资源、PNG/JPG 图片、MP3 音效及相关配置文件,整体约 18.43MB;目录覆盖代码、美术、音频与场景配置,可直接导入 Cocos Creator 工程进行对照分析。项目实现了洗牌发牌、出牌规则、牌型判断等斗地主核心机制,并处理了触摸选牌、出牌交互、与后端数据交换等流程;同时针对微信小游戏平台,给出资源压缩、动画优化、场景管理等适配思路,并接入排行榜、邀请好友等社交能力。已有 245 人学习下载。对于想快速掌握 Cocos Creator 微信小游戏开发流程、梳理脚本组织与平台适配方法的开发者,这份 Demo 是值得参考的完整样例。

1. 用 Cocos Creator 开发斗地主微信小游戏 Demo:先想清楚这四件事再动手

做斗地主微信小游戏 Demo,看起来是“发牌、出牌、比大小”三件事,真正动手才发现,它把 Cocos Creator 的 UI 系统、事件机制、状态管理、资源加载和微信小游戏适配全串在了一起。很多新手卡住,不是卡在牌型算法——那个网上有大把现成代码——而是卡在“牌桌状态怎么管理”“网络同步什么时候介入”“微信小游戏的内存和分包限制怎么绕”。这篇文章就以一个可运行的 Demo 为线索,讲清楚从建项目到真机运行的全过程,适合刚学完 Cocos Creator 基础、想用一个小而完整的项目验证自己能力的开发者,也适合想评估“斗地主类小游戏开发成本”的技术负责人。

先给一个反直觉的结论:斗地主 Demo 的代码量,一半以上花在“出牌合法性校验”和“状态流转”上,真正画牌桌、搓动画的部分反而简单。如果你的目标是先跑通,那就把 AI 托管、联网对战全部砍掉,先做单机三人(两个 AI)版本,这能把工作量缩小一大半。下文的所有方案都是按“本地三人桌 + 后续可扩展网络对战”的思路来搭的,好处是 Demo 阶段不需要买服务器,后续接协议层也不至于推翻重来。

2. 技术选型与项目结构:为什么用 Cocos Creator 3.x,场景怎么划分

2.1 版本选型:3.8 LTS 还是 2.4.x,这是个先手问题

微信小游戏 + Cocos Creator 的组合,目前社区里存的案例大多是 2.x 时代的,包括很多所谓的“斗地主源码”。但如果你现在才起步,我建议直接用 3.8 LTS 版本,理由有三个。第一,3.x 的构建发布对微信小游戏的适配已经相当成熟,原生渲染器在 iOS 低端机上的表现比 2.x 的 WebView 方案稳定得多。第二,3.x 的资源管线和微信小游戏的分包机制配合更顺畅,2.4 时代常见的“首包超过 4MB 就得手动拆”的问题,在 3.8 里有更明确的配置入口。第三,你现在学 2.x,半年后还是得切过来,不如一步到位。

用 3.8 的代价也有,最典型的就是 2.x 的老代码、老教程大量失效——尤其是cc.Node、cc.loader这种 API 全换了写法。网上能找到的斗地主思路可以参考,代码基本要自己重写。如果你只求最快看到效果、不想折腾新 API,那 2.4.10 也不拦你,但要清楚这是“短期效率换长期维护成本”的决定。我的选择是 3.8,下面的所有代码都基于 3.x 的模块化写法。

2.2 场景划分:把 Lobby、Table 和 Game 拆开,别塞进一个场景

斗地主 Demo 最容易做坏的一件事,就是把大厅、牌桌、结算界面全部堆在一个场景里,用node.active来回切换。Demo 阶段可能感觉还行,一旦要加动画、加音效、加网络状态回调,一个场景里的节点树会膨胀到没法维护。

我的做法是拆成三个场景:Lobby、Table、GameResult。如果后续要加网络,再拆一个 RoomList 出来。场景之间用director.loadScene切换,参数用全局单例传——注意不是挂在场景节点上的,而是挂在persistRootNode上,否则场景切换时数据就丢了。

// 全局数据管理器,挂到 persistent 节点上 export class GameData extends Component { private static _inst: GameData = null; public static get inst(): GameData { if (!this._inst) { const node = new Node('GameData'); director.getScene()?.addChild(node); director.addPersistRootNode(node); this._inst = node.addComponent(GameData); } return this._inst; } public roomType: number = 0; // 低倍场/高倍场 public playerName: string = ''; public playerAvatar: number = 0; }

这段代码的核心在addPersistRootNode——它把节点标记为“常驻”,场景切换不销毁。参数说明:roomType走 Lobby 时写入,进 Table 场景后读取;playerName和playerAvatar是给牌桌顶部信息条用的。注意director.getScene()在场景未加载完时可能拿到 null,所以这个管理器最好由 Lobby 场景里的某个节点在onLoad时调用一次并addPersistRootNode,而不是等 Table 场景再去创建。

2.3 目录组织:资源按“功能模块”放,别按“资源类型”放

一个常见的目录组织方式是:assets/textures、assets/audio、assets/scripts这样按类型分。斗地主这种项目里,牌桌相关资源至少有几十张牌面、十几种按钮、弹窗、动画序列帧,全混在一个 textures 目录里,后期找图能把人找疯掉。

我推荐的按功能模块组织:assets/modules/lobby、assets/modules/table、assets/modules/common,每个模块内部再分 prefab、texture、audio、script。这样做最直接的好处是——后续做微信小游戏分包的时候,可以直接把assets/modules/table单独打成 Table 分包,Lobby 作为主包,首包体积立刻降下来。如果你一开始就按类型分,到分包的阶段就得重新整理资源依赖,那才是真正的“返工式”工作。

3. 核心玩法落地:发牌、叫地主、出牌校验的完整实现

3.1 牌的数据结构:用一维数组表示 54 张牌,别用字符串

斗地主的牌数据,最稳的结构是用数字 0-53 表示 54 张牌,而不是用字符串(比如'3D'表示方块3)或者两个字段(花色+点数)。原因很简单:排序、比较大小、判断牌型时,数字的大小直接对应牌面大小和花色优先级,一个sort()就完事。

具体编码规则:0-3对应方块3、梅花3、红桃3、黑桃3,4-7对应方块4、梅花4、红桃4、黑桃4……以此类推。52对应小王,53对应大王。这样设计的技巧是:Math.floor(cardId / 4)就是牌面点数(0-12,对应3到2),cardId % 4就是花色(0 方块,1 梅花,2 红桃,3 黑桃)。大小王单独处理,不参与花色计算。

// 洗牌:生成54张牌并打乱 function createDeck(): number[] { const deck = []; for (let i = 0; i < 54; i++) deck.push(i); // Fisher-Yates 洗牌,保证均匀 for (let i = deck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [deck[i], deck[j]] = [deck[j], deck[i]]; } return deck; }

为什么不用deck.sort(() => Math.random() - 0.5)?因为 V8 引擎的sort不保证对“不稳定比较器”的随机性,实际洗牌结果会有明显偏差,某些牌出现的概率偏高,玩家玩几局就能感觉到“牌太假”。Fisher-Yates 是教科书的做法,写起来也就三行,这点复杂度值得付。注意这里的随机数源是Math.random(),Demo 足够,但如果做真钱或金币场,洗牌必须由服务器生成,并且在网络同步前不能下发到客户端。

3.2 手牌排序:从大到小排列,让玩家一眼看清

发牌后玩家手里的 17 张牌要按牌面从大到小排列,王炸在左上角,3 在右下角。这里有个细节:牌面大小和编码大小不完全一致——编码里 0-3 是 3,4-7 是 4,所以牌面越大编码越大,直接降序排列就是对的。

// 手牌排序:降序,同点数按花色排序(黑桃>红桃>梅花>方块) function sortHandCards(hand: number[]): number[] { return hand.slice().sort((a, b) => { const aRank = getRank(a); // 0-12 对应 3-K-A-2 const bRank = getRank(b); if (aRank !== bRank) return bRank - aRank; return b % 4 - a % 4; // 花色优先级:方块0 梅花1 红桃2 黑桃3 }); } // 取牌面点数:3~2 映射到 0~12,小王13 大王14 function getRank(cardId: number): number { if (cardId === 52) return 13; if (cardId === 53) return 14; return Math.floor(cardId / 4); }

这里的要点是大小王单独返回 13 和 14,而不是Math.floor(52/4) = 13,否则你还要额外判断 52/53 谁是王。排序结果直接决定 UI 层牌的摆放顺序,所以这个函数是后续所有手牌展示的基础。如果你希望“相同的牌”在界面上紧凑排列而不是全展开,排序后还需要按组计数——那就是后面“手牌分组”函数的活,这里先不展开。

3.3 出牌合法性校验:最核心的算法模块,先把牌型定义清楚

这是整个斗地主 Demo 里最容易写乱的部分。不要一上来就写“判断数组长度是1就是单牌”,而是先定一个牌型枚举,然后写一个统一的解析函数,把“一组手牌”解析成“牌型 + 关键参数”,再写判定函数去检验。

export enum CardType { None, Single, Pair, Three, ThreeWithOne, ThreeWithPair, Straight, PairStraight, Plane, PlaneWithOne, PlaneWithPair, FourWithTwo, FourWithFour, Bomb, Rocket } // 解析一手牌,返回 { type, rank, length },type为None表示非法牌型 export function parseCards(cards: number[]): { type: CardType, rank: number, length: number } { if (!cards || cards.length === 0) return { type: CardType.None, rank: 0, length: 0 }; cards = cards.slice().sort((a, b) => getRank(a) - getRank(b)); // 按点数分组计数 const counts: [number, number][] = []; // [rank, count] for (const c of cards) { const r = getRank(c); const last = counts[counts.length - 1]; if (last && last[0] === r) last[1]++; else counts.push([r, 1]); } const ranks = counts.map(x => x[0]); const nums = counts.map(x => x[1]).sort((a, b) => a - b); const len = cards.length; // 单牌 if (len === 1) return { type: CardType.Single, rank: ranks[0], length: 1 }; // 对子 if (len === 2 && nums[0] === 2) return { type: CardType.Pair, rank: ranks[0], length: 2 }; // 王炸 if (len === 2 && ranks[0] === 13 && ranks[1] === 14) return { type: CardType.Rocket, rank: 99, length: 2 }; // 三张 / 三带一 / 三带二 ... if (len === 3 && nums[0] === 3) return { type: CardType.Three, rank: ranks[0], length: 3 }; if (len === 4 && nums[0] === 3 && nums[1] === 1) return { type: CardType.ThreeWithOne, rank: ranks[0], length: 4 }; // ... 省略顺子、连对、飞机等判断 return { type: CardType.None, rank: 0, length: 0 }; }

这个解析函数的关键设计是:先按点数分组,再基于“有几组、每组几张”来判断牌型。这样做比“按长度 switch”健壮得多——比如长度 6 可能是顺子、连对、三带二、三对,你得先分组统计才能区分。牌型判断完还要判断牌型大小:同类型比rank,不同类型中只有炸弹和王炸能压其他类型,王炸压炸弹。这个比较逻辑一般在出牌管理器里写,不要在 UI 层里耦合。

3.4 出牌状态机:轮转、合法回击、过牌,用状态而不是散乱布尔值

斗地主牌桌的核心状态机只有四个:Idle(等待开始)、Bidding(叫地主)、Playing(出牌中)、GameOver。出牌中又分WaitPlayer、WaitAI1、WaitAI2三个子状态,注意“等待谁出牌”不要用布尔值isMyTurn来维护——一旦同步延迟或者 AI 动画播放出错,这个值就不可信了。

export enum TableState { Idle, Bidding, Playing, GameEnd } export enum TurnPhase { None, Player, AI1, AI2 } // 出牌管理器:负责轮转、验证、切换 export class TurnManager { private state: TableState = TableState.Idle; private phase: TurnPhase = TurnPhase.None; private lastPlay: { cards: number[], player: number, type: CardType, rank: number } | null = null; private passCount = 0; // 连续过牌计数,谁出的牌没人管 }

轮转规则中最容易写错的是“过牌后谁先出”:如果上家出牌后你选择过牌,那么由下家继续;如果三家中两家都过牌,则由刚才出牌那家重新出牌。这个逻辑用passCount计数:lastPlay的持有者出牌后passCount = 0,每过一次 +1,passCount === 2时说明其他两家都不要,lastPlay持有者可以重新出任意合法牌型(不再是“压过上家”)。重新出牌时,记得把lastPlay清空,否则你第一手就要求“必须大于上家”,直接导致死循环。

4. 微信小游戏适配与构建:从浏览器能跑,到真机能玩

4.1 构建发布:Cocos Creator 3.8 的微信小游戏输出配置

在 Cocos Creator 里构建微信小游戏,路径是“项目 -> 构建发布 -> 微信小游戏平台”。你需要重点确认的参数有四个:appid(测试阶段可以用测试号)、生成分包(勾选后需要配置分包目录)、初始场景(必须是 Lobby)、资源服务器地址(Demo 阶段留空,走本地包)。

有一个非常容易踩的坑是“首包体积超限”。微信小游戏主包限制 4MB,Cocos 默认会把所有资源和代码打进 br 包,如果你的牌面图片一张 300KB、音效加了一堆,首包瞬间超了。构建面板的“构建进度”日志里会有体积统计,注意看 output 目录下的cocos-js和assets文件夹大小。首包超限的两个快速解法:一是把图片压缩到PNG8 / RGB565格式,二是做分包——把 Table 场景的资源独立成一个分包,主包只留 Lobby 和公共资源。

4.2 屏幕适配:斗地主界面最怕刘海屏和长宽比差异

微信小游戏是在手机上跑的,手机屏幕的宽高比从 iPhone SE 的 16:9 到全面屏的 19.5:9 再到折叠屏,差距很大。斗地主的牌桌是横向布局:顶部是对手信息,中间是出牌区,底部是你的手牌和操作按钮。如果 Canvas 的Fit Width和Fit Height设置不对,会出现“手牌跑出屏幕”或者“两侧大片空白”的问题。

我一般用“固定设计分辨率 + 安全区适配”的方案。设计分辨率设成1280 x 720(横向游戏),Canvas 的适配策略选择Fit Height+SHOW_ALL,这样在不同宽高比下,场景都会完整显示但两侧会出现黑边。黑边的处理方式是在 Lobby 的背景图上留出足够的可裁剪区域,而不是拉伸背景图。在代码层面,UI 节点的定位不要全部依赖Widget对齐,底部的手牌区域要动态算安全区。

// 微信小游戏安全区适配 import { view, screen, Canvas } from 'cc'; // 获取安全区(单位:设计分辨率下的坐标) const safeArea = screen.safeArea; const visibleSize = view.getVisibleSize(); const designSize = view.getDesignResolutionSize(); // 计算底部偏移量(像素),再转成 UI 坐标 const bottomOffset = safeArea.y; // 拿到这个值后,就可以把它应用到牌桌底部容器节点的 y 坐标上

这段代码的关键变量是safeArea.y——在 iPhone X 及以后的机型上,这个值不为 0,表示底部有手势条区域。你把牌桌底部容器节点的y设为-visibleSize.height/2 + bottomOffset,手牌和按钮就永远不会被手势条挡住。注意safeArea的单位是像素,在view.getVisibleSize()对应的 UI 坐标系里用的话,可能需要除以view.getScaleX()做一次换算,否则不同分辨率下偏移量会不对。

4.3 资源加载与内存:图片别在场景里直接引用,用远程加载或分包

微信小游戏的运行环境是浏览器内核,本地包体积和内存都有严格的限制。斗地主全套 54 张牌面 + 背景 + 特效 + 音效,如果全在主包里预加载,内存很可能在低端 Android 机上崩溃。我的做法是:牌面图片不直接挂在 prefab 上,而是做一张“牌面图集”并在用到的时候再加载。

常见的内存优化方案是“图集拆分 + 按需加载”:牌面图集拆成cards-1(3到10)和cards-2(J到2、大小王)两个文件。游戏刚开始只加载cards-1,玩家手中的牌大概率是小牌为主(底牌和炸弹到后面才用上大牌),当出现第一张 J 及以上的牌时再异步加载cards-2。这样做的好处是进入牌桌的首屏时间变短,低端机的初始内存压力也小。代价是代码里要处理一次“图片未加载完时牌的显示占位”逻辑,用一个空牌盒节点兜底即可。

5. 避坑:斗地主 Demo 从构建到真机的 6 个常见问题

避坑这一章,写我在这类项目上踩过的真实问题,每条都按“现象、原因、解决”的结构来,方便你对照排查。

坑一:构建到微信小游戏后发现音效播放不了,控制台报webaudio相关错误。现象是模拟器里一切正常,真机没有声音。原因很直接:微信小游戏的 Audio 在用户触摸屏幕之前是被禁用的,而 Cocos 引擎默认在场景加载时就尝试播放背景音乐。解决方法是把背景音乐播放的调用放到玩家点击“开始游戏”按钮的回调里,并且在wx.onShow事件里做一次恢复播放的处理。

坑二:手牌区域的牌重叠严重,尤其是牌一多(17张)的时候。现象是手牌数量超过 10 张,后半部分几乎看不出牌面。原因是 UI 布局用的固定间距,没有根据手牌数量动态计算。解决方法是写一个“手牌重排”函数:牌的显示宽度是固定的,容器可用宽度也是固定的,间距 = (容器宽度 - 单牌宽度) / (牌数 - 1),牌多时间距变小,牌少时间距变大,保证每张牌露出约 20% 的宽度。注意重排要加过渡动画,否则点一张牌后整体闪跳。

坑三:出牌校验在“三带一”时漏判。现象是玩家打出“三张J带一张4”,服务器端校验不通过——当然 Demo 没有服务器,这是 UI 提示非法出牌时发现的。原因是 parseCards 里对“三带一”的判断条件写成了nums[0] === 3 && nums[1] === 1,但nums是按数量排序的数组,nums是[1, 3]时会误判。解决方法是判断分组数量而不是排序位置:先确认总共只有两组,然后找哪组是 3 张,哪组是 1 张。类似的逻辑还影响“三带两”“四带两张单牌”的判断,核心是:不要用排序后数组的下标去猜牌型,要考虑具体是哪一组。

坑四:AI 托管时表现得很“傻”,不管牌多大都直接出。现象是演示 Demo 时把游戏交给 AI 托管,AI 手上有王炸却出单张。原因是 AI 最简单的策略是“从最小牌开始出”,但没做两个基本优化:有炸弹时要不要拆、对手出大牌时要不要“忍一手”。Demo 阶段的 AI 可以不做完整决策树,但至少加一个“最大牌优先级”的规则:手中牌大于当前桌上牌时,选择最小可压过的牌型;如果手中有炸但对手只剩一手牌,可以考虑直接炸。这块不追求强,但“AI 完全不过大脑”会让你的 Demo 看起来很廉价。

坑五:微信小游戏分包后,Lobby 点击“进入牌桌”时白屏。现象是构建时勾选了分包,Lobby 正常,切 Table 场景时加载卡住。原因是分包里的场景脚本没有被主包引用,导致代码没有被打进主包。解决方法是:在game.ts或其他入口脚本里显式引用分包里所有需要用的脚本类(哪怕只是import不调用),或者在分包配置里把Table场景和它的脚本、资源全部归进同一个分包,并确保所有改用resources.load加载的资源路径正确。这个问题排查起来比较隐蔽,建议一开始就把分包配置好再往上加资源,而不是后补分包。

坑六:真机上牌桌背景图变形拉伸。现象是模拟器正常,真机变糊和变形。原因是背景图用的 JPEG 尺寸不够,被强行拉伸到 1280x720 以上;另外部分 Android 机的 GPU 对非2的幂尺寸的纹理处理效率低。解决方法:背景图做成 2048x1024 或 2560x1440 的JPEG(质量 80),UI 元素用 PNG 带透明通道;所有纹理导入设置里的Filter设为Bilinear,Premultiply Alpha设为不勾选,避免黑色描边。如果你用的是大图拼小图的方式,还要注意 SpriteFrame 的Trim设置,否则会出现裁切偏移。

6. 进阶技巧:让 Demo 具备“演示价值”的两个关键——AI 手感和断线恢复

AI 手感是斗地主 Demo 观感的分水岭,断线恢复则是微信小游戏微信环境的刚需,这两点做好,项目就具备“拿出来演示”的完成度了。

AI 的牌风控制,在 Demo 阶段不需要上模型,用“规则 + 随机”就能达到能玩的水平。策略分三层。第一层是“要不要出牌”:如果是地主上家、且地主只剩一两手牌,优先出最大牌尝试走完;如果是地主下家,跟随队友出牌时尽量不出大牌拦队友。第二层是“出什么牌型”:从最小单牌开始试探,但如果手上有对子或顺子,优先选择“能走完”或“能接近走完”的牌型。第三层是“炸弹时机”:对手只剩一手牌且大于你所有单牌时才炸,否则留着。AI 决策函数的参数里加一个riskLevel(0-100),值越高 AI 越激进,这个值每次开局随机化,就能避免 AI 打法千篇一律。这样不会让 Demo 呈现“AI 水平太高不可信”或者“太傻没法看”的两极状态。

断线恢复在 Demo 阶段要做的不是真正的重连服务器,而是做本地状态恢复——玩家滑动切走微信再回来,牌桌不能重置。做法是在游戏进入后台时(监听wx.onHide)把TableState、手牌数组、当前轮到谁、上一次出牌的牌型全部序列化到wx.setStorageSync,回到前台(wx.onShow)时反序列化恢复。注意只保存必要数据,不要把整个场景树存进去,否则存储和恢复的开销都会变大。这一步的目的有两个:一是让队友在演示时敢于切出去回微信消息再回来,不至于重新开局;二是为后续接真正的网络同步做一个本地快照的对照基准——网络重连时以服务器推送的牌面为准,用本地快照做比对,不一致时强制刷新。

最后一个习惯问题:真机调试时不要只盯着功能,每改一个 UI 参数、每加一张图片,都构建一次真机预览看内存占用。微信开发者工具的“性能面板”里能看到 CPU、内存、帧率三条曲线,帧率掉到 40 以下基本就是有资源反复加载或节点泄漏。这个习惯帮我提前发现了很多看不到的坑,希望也能帮到你。

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

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

用a2d-diary把杂乱日记变结构化数据:Python日记管理自动化实践

最近在整理自己的日记和项目记录时&#xff0c;我一直被一个问题困扰&#xff1a;手头的笔记散落在txt、Markdown、手机备忘录里&#xff0c;格式五花八门&#xff0c;想统计一下某个时间段内自己写了多少东西、状态如何&#xff0c;几乎得靠肉眼数。直到我翻到a2d-diary这个Py…

作者头像 李华
网站建设 2026/10/10 19:34:19

基于Spring Boot的飘香水果购物网站毕业设计全解析

做过多年Java开发&#xff0c;也带过不少毕业设计的学生&#xff0c;每年到了三四月份总会收到一堆求助&#xff1a;老师&#xff0c;Spring Boot项目怎么跑起来&#xff1f;数据库连不上怎么办&#xff1f;功能做完了答辩怎么讲&#xff1f;说实话&#xff0c;很多同学的毕设选…

作者头像 李华
网站建设 2026/10/10 19:30:47

能ping通却下载失败?远程维护中MTU黑洞的排查与解决

1. 问题现象与排查思路总览1.1 一个让人抓狂的现场做工业自动化远程维护的同行&#xff0c;大概率都遇到过这种场景&#xff1a;现场一台 PLC 控制着整条产线&#xff0c;工程师在办公室通过远程通道连过去&#xff0c;ping命令一发&#xff0c;延迟稳定、丢包为零&#xff0c;…

作者头像 李华
网站建设 2026/10/10 19:30:09

PHP程序员学习困局:从“学而思”到“思而学”的进阶之路

1. 从“学而思”到“思而学”&#xff1a;PHP程序员的学习困局1.1 为什么大多数PHP程序员卡在了“学而思”这一步“PHP程序员学而思 思而学&#xff1f;”这个标题我第一眼看到的时候&#xff0c;脑子里蹦出来的不是那个教育品牌&#xff0c;而是一句话&#xff1a;我们天天都…

作者头像 李华
网站建设 2026/10/10 19:27:52

枚举:从enum类型到暴力枚举与硬件设备枚举

我们技术圈子里&#xff0c;枚举可能是最被低估的关键词。写业务代码时&#xff0c;它是不起眼的enum类型&#xff1b;刷算法题时&#xff0c;它又是“暴力枚举”的代名词&#xff1b;到了底层硬件领域&#xff0c;PCIe 枚举、Linux SRIO 枚举又是完全另一套运行机制。同一个词…

作者头像 李华