Keil MDK 5 专用 ARM Compiler 6.21 独立安装版:Windows 64 位下多版本编译器共存与工程迁移避坑指南

简介:这份资源是面向嵌入式开发者的ARM Compiler for Embedded 6.21离线安装包,专为Keil MDK-ARM 5环境适配,解决在线安装受限或需要独立部署AC6编译器的问题。包内共10个文件,以msi主安装程序为核心,辅以txt法律与授权说明、html版本更新日志及开源组件授权目录等,压缩包约322.7MB,覆盖Windows x86_64平台,安装后可在Keil uVision 5中配置为默认ARM C/C++编译器。其兼容AC6语法标准,支持ARMv6-M至ARMv8-A架构,适用于STM32、NXP LPC、Renesas RA等主流Cortex-M/R/A系列MCU的嵌入式代码编译。目前已有140人学习下载,适合需要稳定构建环境、规避在线激活依赖的中高级嵌入式开发者参考使用。

1. 编译器版本卡死项目进度:为什么有人专门找 ARM Compiler 6.21 独立安装版

如果你在 Windows 64 位环境下用 Keil MDK 5 编译一个已经维护了三五年的 ARM 工程,大概率遇到过这种局面:换台新电脑、重装系统、或者同事接手,装完最新的 MDK,一编译满屏报错,不是unknown attribute就是内联汇编语法不认,链接阶段还冒出L6235E。代码一行没改,问题出在编译器版本上。ARM Compiler 6 系列基于 LLVM/Clang 架构,和早期的 ARM Compiler 5(armcc)是两套完全不同的前端,6.21 属于 AC6 里被大量存量工程验证过的一个稳定节点,很多芯片厂商的启动文件、DSP 库、RTOS 移植层都是按这个版本的语法和优化行为调通的。所以“Keil MDK 5 专用 ARM Compiler 6.21 独立安装版(Windows 64 位)”这件事,本质是给 MDK 5 单独挂一个指定版本的编译器,绕开随 IDE 捆绑升级带来的版本漂移。适合谁?适合手里有量产工程、不想因为工具链升级去改底层汇编和链接脚本的嵌入式工程师,也适合需要在一台机器上并存多个编译器版本、按工程切换的团队。

2. 先搞清楚 AC6 和 MDK 5 的挂接关系:编译器不是 IDE 的一部分

2.1 MDK 5 的编译器目录结构与版本发现机制

很多人以为编译器是装在 MDK 安装目录里的,其实从 MDK 5 开始,ARM 把编译器做成了可独立安装的组件,IDE 通过一个固定的目录约定去“发现”它。默认路径是Keil_v5\ARM\ARM_Compiler_6_21这类带版本号的文件夹,MDK 启动时会扫描ARM目录下的子文件夹,读取每个编译器根目录里的版本描述文件,然后把它列进Project → Manage → Project Items → Folders/Extensions或者 Options for Target 的编译器下拉框里。理解这一点很关键:所谓“独立安装版”,就是把这个编译器包解压/安装到ARM目录下,让 MDK 能扫到,而不是去覆盖 IDE 自带的那个。目录结构大致是这样:

Keil_v5/ ARM/ ARM_Compiler_6_21/ bin/ armclang.exe armlink.exe armasm.exe fromelf.exe include/ lib/ ARMCC/ <- AC5 的老目录 ...

bin下面是真正干活的工具链,armclang是编译前端,armlink负责链接,fromelf用来生成 bin/反汇编。版本号写在目录名和内部的版本文件里,MDK 靠这个区分 6.21 和 6.19、6.16。你如果手动改目录名,MDK 可能就认不出来,这是后面避坑章要展开的点。

2.2 为什么是 6.21 而不是最新版:语法、优化与库的兼容边界

AC6 每个大版本之间,Clang 前端基线会跳,优化器的行为也会变。6.21 对应的 Clang 版本相对成熟,对__attribute__((section))、__asm内联汇编、#pragma clang diagnostic这些常用扩展的处理比较稳。更新的版本可能收紧了某些隐式转换的警告,或者改了 LTO 的默认策略,导致老工程里那些“能跑但不太规范”的代码直接编译失败。反过来,太老的 6.9、6.12 又可能缺少某些 Cortex-M55/M85 的支持或者 DSP 指令的优化。6.21 处在一个甜点区:既覆盖了主流 M 核,又没激进到把存量代码判死刑。选型逻辑就是——跟着你用的芯片包和中间件走,芯片厂商的 pack 里如果标注了推荐编译器版本,优先对齐那个,而不是盲目追新。

2.3 独立安装包和 IDE 捆绑版的区别:别把两者混着装

IDE 捆绑版是随 MDK 安装程序一起落地的,版本由你装的 MDK 安装包决定,通常只有一个。独立安装版是一个单独的安装程序或压缩包,可以装多个,互不干扰。区别在于:捆绑版升级 MDK 时可能被替换,独立版装完就固定在那儿,除非你手动删。另一个区别是注册表和环境变量,独立安装版一般不去动系统级 PATH,只往ARM目录写文件,所以对系统影响小。实操上我建议:保留 IDE 自带的那个别动,把 6.21 独立装到ARM\ARM_Compiler_6_21,然后在工程里显式指定用哪个。这样即使 MDK 升级,你的工程编译环境也不漂。

3. 在 Windows 64 位下装好并让 MDK 5 认到 6.21

3.1 安装路径选择与目录命名规范

安装时第一件事是选路径。如果你装的是独立安装程序,它会问你要装到哪,这时候直接指到 MDK 的ARM目录下,文件夹名保持默认的ARM_Compiler_6_21,别自己改成AC6或者compiler621。原因是 MDK 的扫描逻辑对目录名有一定依赖,带ARM_Compiler_前缀的更容易被正确识别为编译器根目录。如果你拿到的是压缩包形式,解压后手动放进去,也要保证最终路径是...\Keil_v5\ARM\ARM_Compiler_6_21\bin\armclang.exe这一层。放错一层,比如多套了一个文件夹,MDK 就扫不到bin,下拉框里不会出现这个版本。

# 假设 MDK 装在 D 盘,检查目录是否放对 ls "D:/Keil_v5/ARM/ARM_Compiler_6_21/bin/armclang.exe" # 能列出文件说明层级正确;报 No such file 就是路径多套或少套了一层

这段命令是给你在 Git Bash 或 WSL 里快速验证路径用的,Windows 原生 CMD 用dir也行。关键是确认armclang.exe直接位于bin下,而不是bin/bin或者ARM_Compiler_6_21/ARM_Compiler_6_21/bin。参数上没什么可调的,就是路径核对。

3.2 在 Manage Project Items 里注册编译器并切换

装好文件只是第一步,还要让 MDK 知道它。打开 MDK,进Project → Manage → Project Items,切到Folders/Extensions标签页,这里能看到ARM Compiler的列表。正常情况下 MDK 会自动扫描到新装的 6.21,如果没有,点Add手动指定编译器根目录,也就是ARM_Compiler_6_21那一层,不是bin。注册完之后,去Options for Target → Target标签页,在ARM Compiler下拉框里选Use default compiler version 6或者直接选6.21。有些 MDK 版本下拉框只显示大版本,具体小版本在Manage里看。

// 验证当前工程用的是哪个编译器:在 main.c 里加一行 #include <stdio.h> int main(void) { // __ARMCOMPILER_VERSION 是 AC6 预定义宏,打印出来看具体版本 printf("Compiler version: %d\n", __ARMCOMPILER_VERSION); return 0; }

__ARMCOMPILER_VERSION这个宏在 AC6 里是整型,6210000 这种格式对应 6.21。编译烧录后从串口看输出,就能确认工程实际调用的是不是你装的那个版本。这一步比看 IDE 界面靠谱,因为界面显示和实际调用偶尔会因为工程配置缓存不一致。参数说明:这个宏不需要额外定义,AC6 自带;如果你用的是 AC5,这个宏不存在,会编译报错,那说明你切错了编译器。

3.3 用命令行验证 armclang 版本与目标架构

图形界面之外,直接跑命令行是最快的验证方式。进到ARM_Compiler_6_21\bin目录,执行版本查询:

# 查看 armclang 版本信息 ./armclang --version # 输出里会有 "Arm Compiler 6.21" 和基于的 Clang 版本号 # 查看默认目标架构 ./armclang -mcpu=list # 列出该版本支持的所有 -mcpu 值,确认你的芯片核在列表里

--version输出里如果只有 Clang 版本没有 ARM 标识,说明你跑到别的 clang 去了,检查 PATH 或者当前目录。-mcpu=list用来确认 6.21 是否支持你用的核,比如cortex-m33、cortex-m55。如果列表里没有你要的核,那这个版本对你没用,得换。参数上,-mcpu在真正编译时配合-mthumb使用,比如-mcpu=cortex-m4 -mthumb,这是 AC6 编译 M 核的标准组合。

4. 工程迁移到 6.21 时最容易翻车的几个地方

4.1 现象:内联汇编报错expected ')'——原因:AC5 的__asm语法 AC6 不认

这是迁移里最高频的翻车点。AC5 时代写内联汇编常用__asm { ... }这种块状语法,AC6 基于 Clang,只认__asm volatile("...")这种 GNU 风格。现象就是编译到那段汇编直接报语法错误,指向{或者}。解决办法不是把整个汇编重写,而是把块状语法改成字符串形式,寄存器约束用: "=r"(var)这种。如果汇编量大,考虑单独放一个.s文件用armasm汇编,而不是内联。注意 AC6 里armasm默认走的是 GNU 汇编器还是 legacy 汇编器,取决于文件扩展名和编译选项,.s通常走 GNU as,.asm走 legacy,混用时要在工程里分别配置。

4.2 现象:链接报L6235E: More than one section matches selector——原因:分散加载文件里用了通配符

AC6 的 armlink 对分散加载(scatter file)里*通配符的匹配规则比 AC5 严格。老工程里常见*(.text.*)这种写法,如果多个目标文件都有匹配的段,AC6 会报 L6235E,说选择器匹配到多个段。解决是把通配符收窄,明确指定哪个目标文件或哪个段名,比如main.o(.text.*)。或者用-Wl,--diag_suppress=L6235E临时压掉,但这只是掩盖,不推荐。根因是链接器版本行为差异,不是代码错。

4.3 现象:浮点运算结果和 AC5 不一致——原因:-ffp-contract默认值不同

AC6 默认开启浮点收缩(fused multiply-add),AC5 默认关闭。同一个表达式a*b+c,AC6 可能编译成一条 FMA 指令,精度和 AC5 的分步计算有细微差别。对大多数控制算法无所谓,但对做高精度传感器融合或者需要和上位机对账的场景,这个差异会导致结果对不上。解决是在编译选项里加-ffp-contract=off,强制和 AC5 行为对齐。这个参数在Options for Target → C/C++ → Misc Controls里填。

4.4 现象:MDK 下拉框里看不到 6.21——原因:目录层级或版本描述文件缺失

装完文件但 MDK 不认,先查目录层级,确认armclang.exe在bin下。再查编译器根目录里有没有版本描述文件,通常是一个.txt或者.xml,MDK 靠它读版本号。如果是从别处拷贝的绿色包,可能缺这个文件,MDK 就跳过不显示。解决是从完整安装包里把描述文件补回来,或者用 MDK 的Add手动指定根目录,强制注册。手动注册后如果还是不行,重启 MDK,它只在启动时扫描一次。

4.5 现象:编译通过但下载后跑飞——原因:库文件用了 AC5 的.lib没换

工程里如果引用了预编译的静态库,比如某些 DSP 库或者第三方算法库,这些.lib是用 AC5 编的,ABI 和 AC6 不完全兼容。编译链接可能不报错,但运行时函数调用约定对不上,直接跑飞。现象是下载后进不了 main,或者进 main 后一调用库函数就 HardFault。解决是找库的 AC6 版本,或者拿到源码用 6.21 重新编。没有源码的库,只能退回 AC5 编译整个工程,别混用。

5. 让 6.21 在团队和多工程里稳定复用的几个技巧

5.1 用工程级编译器锁定替代全局默认

团队协作里最怕的是每个人机器上默认编译器版本不一样。MDK 工程文件(.uvprojx)里其实记录了编译器版本选择,但如果你用的是“Use default compiler version”,它就跟 IDE 全局设置走,换台机器就变。正确做法是在每个工程的 Options 里显式选6.21,而不是 default。这样工程文件一提交,别人拉下来只要装了 6.21,编译环境就一致。如果没装,MDK 会提示找不到指定版本,而不是悄悄用别的版本编,这反而是好事,能提前暴露环境差异。

5.2 多版本并存时的切换检查清单

一台机器上同时有 6.16、6.21、6.24 是常态。切换时按这个清单走:第一,确认 Options 里选的是目标版本;第二,清理工程(Rebuild All),别用增量编译,因为不同版本的目标文件混在一起链接会出玄学问题;第三,检查Misc Controls里有没有版本相关的宏或参数残留;第四,编译后看 map 文件里的编译器版本标识,确认链接器也是同版本。这四步做完,基本不会出现“界面显示 6.21 实际用 6.16”的黑匣子情况。

5.3 用 map 文件和 fromelf 反查实际工具链版本

编译完的.map文件头部会记录链接器版本,fromelf生成的反汇编里也能看到编译器标识。养成习惯:出问题时先看 map 文件确认工具链版本,而不是猜。命令是fromelf --text -c -o output.txt your.axf,输出的头部有ARM Compiler字样和版本号。这个习惯能帮你省下大量“以为是代码问题其实是版本问题”的排查时间。我自己的做法是每次发布固件前,把 map 文件头几行截出来存档,出问题直接对版本,后悔药就省了。

6. 一个具体技巧:用批处理脚本一键校验编译器版本与工程配置

最后落一个能直接用的技巧。团队里新人装环境,或者 CI 上跑构建,最怕编译器版本不对还编过了。写一个批处理,在编译前强制校验armclang版本,不对就退出。

@echo off REM check_ac6.bat - 校验 ARM Compiler 版本是否为 6.21 set AC6_PATH=D:\Keil_v5\ARM\ARM_Compiler_6_21\bin\armclang.exe if not exist "%AC6_PATH%" ( echo [ERROR] ARM Compiler 6.21 not found at %AC6_PATH% exit /b 1 ) for /f "tokens=*" %%i in ('"%AC6_PATH%" --version ^| findstr /i "Arm Compiler"') do set VER_LINE=%%i echo %VER_LINE% | findstr /i "6.21" >nul if errorlevel 1 ( echo [ERROR] Expected Arm Compiler 6.21, got: %VER_LINE% exit /b 1 ) echo [OK] Arm Compiler 6.21 verified. exit /b 0

逻辑说明:先检查armclang.exe是否存在,不存在直接失败;存在则跑--version,用findstr抓包含Arm Compiler的那行,再判断这行里有没有6.21。参数上,AC6_PATH按你实际安装路径改,findstr /i忽略大小写。这个脚本可以挂在 Keil 的Before Build用户命令里,也可以放进 CI 的构建前步骤。注意for /f那行的^|是转义管道符,批处理里管道要转义,这是血泪经验,不转义直接语法错误。把这个脚本跑通,团队里就不会再出现“我这儿能编你那儿不能编”的扯皮,版本这件事从玄学变成可验证的检查项。希望帮到你。

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

相关新闻

AI学术速递:信息筛选与高效阅读的实操指南

AI学术速递:信息筛选与高效阅读的实操指南

1. 从“学术速递”这个栏目名说起&#xff1a;为什么信息筛选比信息获取更难做AI方向的研究或者工程落地&#xff0c;每天最不缺的就是新论文、新模型、新工具。arXiv上一天挂出来的预印本少说几百篇&#xff0c;顶会开完又是一波集中放送&#xff0c;再加上各种实验室博客、技…

2026/10/21 12:02:55 阅读更多 →
Selenium底层原理与WebDriver通信机制全解析

Selenium底层原理与WebDriver通信机制全解析

面试里聊Selenium&#xff0c;十个人有九个都会背“WebDriver是浏览器自动化标准”“通过HTTP协议通信”这两句话&#xff0c;但一旦追问“你执行一次click&#xff0c;底层到底发了什么”“为什么元素明明存在却点击不到”“隐式等待和显式等待的实现区别是什么”&#xff0c;…

2026/10/21 12:09:52 阅读更多 →
AI面试官如何评估求职者:评分机制、应对策略与能力边界

AI面试官如何评估求职者:评分机制、应对策略与能力边界

1. 当面试官变成一段代码&#xff1a;AI面试的临界点已经到来如果你最近半年投过简历&#xff0c;大概率经历过这样的场景&#xff1a;点开一个链接&#xff0c;屏幕上跳出一个虚拟形象&#xff0c;用平稳到近乎冷漠的语气说“你好&#xff0c;欢迎参加本次面试&#xff0c;请先…

2026/10/21 12:10:25 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/21 9:32:40 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/21 9:32:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/21 9:32:40 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/21 9:32:40 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/21 9:32:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/21 9:31:32 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →