数据在内存中的存储:字节序、对齐与浮点精度的底层原理
“数据在内存中的存储”这个话题是每个写代码的人都绕不开的底层命题。我在项目现场排查过一个特别诡异的问题一段缓存数据在运行几个小时后出现乱码追了两天才发现是有人把 int 按字节数组读取而且完全没考虑字节顺序。后来我给这个同事补了三天基础课从 0 和 1 开始讲。写这篇文章就是那三天的浓缩版。适合谁看写 C/C 的、做嵌入式的、对程序运行机制充满好奇的开发者也包括调试问题时总感觉无从下手的同学。它解决的问题很简单让你拿到一个变量时能准确知道它在内存里长什么样、为什么长这样、以及哪些操作会偷偷改变它的样子。1. 一切存储的起点内存里只有0和1“类型”只是解释规则1.1 变量名、地址和字节内存的“门牌号”体系大多数编程课一上来就教变量但很少有人强调变量名只是给人用的编译期符号一旦程序运行起来它就被彻底遗忘。真正留在内存里的只有地址和字节。每个字节是内存的最小可寻址单位就像停车场里的一个车位地址就是车位编号。32 位程序里地址宽度是 4 字节64 位程序里是 8 字节能访问的“车位”总数天差地别。明白这一点很多事就能想通为什么一个 int 在 64 位机器上依然通常是 4 字节因为这是 C 语言标准规定的最小 int 大小具体取决于编译器和平台。为什么指针尽量别直接转成 int因为指针可能 8 字节int 只有 4 字节一截断地址就丢了。数据在内存中的存储首先就是“数据占几个字节、起始地址在哪”这两个问题的答案其余的一切都建立在这之上。1.2 类型到底是干什么的内存只存 0 和 1那类型信息存哪答案是不存。类型是编译时用来生成指令的规则读了 4 字节怎么解释、按无符号还是有符号加减、移位时补 0 还是补符号位这些逻辑都由编译期指令决定。运行时内存里没有任何标签告诉你“这块是 int 还是 float”。我做教学实验时常用一段代码说明int a 65; char c A;在内存里看a 和 c 所在的那个字节内容都是 0x41。如果你把 0x41 按 char 读它是 A按 int 读它是 65按 float 读它是个趋近于零的小数。同一个比特串解释方式不同结果天差地远。所以排查数据问题时第一件事永远是问我现在用哪种类型在解释这堆字节问错了方向后面全是瞎忙。2. 整数补码、符号位和溢出的边界感觉2.1 补码不是数学考试是CPU偷懒的产物整数在内存里怎么存正数好说直接二进制展开。负数呢用的补码。很多教材把补码讲得很玄乎其实就是个工程选择的问题。如果直接用原码0 就有正 0 和负 0 两种表示CPU 做加减法还得特判符号位。补码的好处是同一个加法电路既能算正数加法也能算负数加法符号位只是顺带产生的。用 8 位整数举个例子1 0000 0001-1 1111 1111为什么 -1 是 1111 1111因为 (-1) (1) 0而 1111 1111 0000 0001 1 0000 0000舍弃溢出位后正好是 0000 0000。这样设计后所有整数的加减法都可以用模运算统一完成。这也解释了为什么 int 能表示的最小值比最大值的绝对值大 1正数要留给 0 和正数负数把 0x80000000 这个“空位”占上了。8位二进制无符号解释有符号解释0000 0001110111 11111271271000 0000128-1281111 1111255-1同样的比特串按无符号和有符号去读数值完全不同。写代码时如果混用了这两种视角总有一天会被自己坑到。2.2 把整数比作里程表溢出不是bug的错写代码时如果不考虑边界溢出很容易出现。把 int 当计数器用跑几个月后突然变成负数这种问题我见过不止一次。有符号溢出在 C/C 里是未定义行为编译器甚至可以假设你不会溢出从而做一些看起来很神奇的优化无符号溢出则是定义良好的回绕行为。一次实际场景里某系统用 int 存设备累计消息数量大了以后直接翻负下游判断逻辑全部紊乱。排查方法打印计数变量发现是负数查看二进制发现实际是回绕了。解决办法改用 64 位整数或者至少用无符号并提前做阈值处理。教训是只要数字可能长期增长就别用它做“足够大”的假设。内存里存的是连续的二进制位类型决定了它回绕后如何被解释而解释错了比存错了更可怕。3. 浮点数不精确不是bug是表示法在妥协3.1 拆开一个float符号位、指数、尾数浮点数的存储很反直觉。float 32 位分为 1 位符号、8 位指数、23 位尾数。原理跟科学计数法类似1.xxx × 2^e。指数部分用偏移量表示尾数部分存小数。这意味着它能表示的数值不是连续均匀分布的而是在 0 附近密度大、远离 0 密度小。一个看得见的例子1.0f 在内存里是 0x3F800000。拆开看符号位 0指数部分 0x7F127减去偏移 127 后为 0尾数部分是 0所以就是 1.0×2^0。IEEE 754 这个格式几乎是所有现代处理器浮点运算的事实标准理解它你才能理解为什么浮点数会“不精确”。3.2 0.1为什么永远存不干净十进制里 1/3 是无限循环小数二进制里 0.1 也是。0.1 的二进制小数是 0.0001100110011...循环float 尾数只有 23 位double 只有 52 位存不下就只能截断。于是有了经典的代码printf(%.20f\n, 0.1f); // 0.10000000149011611938 printf(%.20f\n, 0.1); // 0.10000000000000000555这是存储格式决定的天生误差不是编译器在为难你。做金融计算、物理模拟、游戏分数累加时这种误差会在多次运算中不断积累。很多人第一次看到 float 打印出一串奇怪的尾巴时会以为是自己代码写错了其实这恰恰说明你真正看到了“数据在内存中的存储”——存储格式的取舍直接决定了计算结果的样子。3.3 浮点数比较的实操替代方案因为浮点有误差所以判断两个浮点数是否相等用基本是找坑。等金额、游戏分数、物理引擎里全是这种问题。有效做法之一是用绝对误差或相对误差更好的做法是按 ULP最小精度单位比较两个数相差不超过几个 ULP 就认为相等。int nearly_equal(float a, float b, float tolerance) { float diff a - b; if (diff 0) diff -diff; if (diff 1e-6f) return 1; // 绝对误差兜底 float scale a b ? a : b; // 相对误差按更大的数作为基准 return diff scale * tolerance; }这是一个实用简化版真实业务里还要根据数值范围调整容差。记住原则先想清楚你需要多精确再决定比较的方式而不是反过来指望浮点天生精确。你能在内存里放下多少个有效数字是早在存储那一刻就注定的事。4. 大端还是小端字节顺序只在跨界时咬人4.1 内存里的数到底从哪头开始数字 0x12345678在小端机上是78 56 34 12在大端机上是12 34 56 78。小端是低字节放在低地址大端是高字节放在低地址。查到本机字节序最简单的办法uint32_t x 0x12345678; unsigned char *p (unsigned char *)x; for (int i 0; i 4; i) printf(%02x , p[i]);小端机输出 78 56 34 12。我见过一个解析图片头的代码本地测试好好的换到另一台设备上图片尺寸变成天文数字原因就是直接把内存里的 uint32_t 指针当字节流去解析文件没做大小端转换。大部分常见处理器都是小端所以多数人一辈子没感觉但只要一碰网络协议、二进制文件、跨平台数据包字节序就会突然跳出来咬人。内存地址偏移小端模式大端模式00x780x1210x560x3420x340x5630x120x784.2 网络协议的约定一般都用大端网络字节序是大端。这是早期协议设计者希望数据不管在什么机器上解释都是一致的所以约定成俗。写网络程序要习惯用 htonl、ntohl、htons、ntohs 做转换。本质是内存里的排布和传输时的排布是两回事。你在小端机上把 uint32_t 转成网络序实际上就是把 0x78 0x56 0x34 0x12 调成 0x12 0x34 0x56 0x78 再发送。一个比较容易理解的类比日期格式。有人写 2024-05-11有人写 11/05/2024。同样的信息写法不同。跨文化交流时你问“今天是几号”对方报“11”你得知道他说的是 11 号还是 11 月。字节序就是机器之间约定“先说是月还是日”的规则。沟通前不谈好数据就错位。4.3 什么时候该关心字节序什么时候别关心协议解析、文件格式解析、网络收发、驱动开发这些一定要关心。自己机器内部、同一平台的两进程共享内存、或者明确只在本机运行的代码可以不管。判断标准很简单数据会不会离开当前进程的内存空间会就要问一声两边采用的字节序是否一致不一致转换在哪一层做最合理我的建议是所有二进制协议都在收发边界统一转成网络序内部逻辑一律用本机序别把转换拆散到业务代码里。这样才能把字节序问题限制在一个薄薄的边界层而不是让它在整个代码库里到处传染。5. 结构体的隐形开销对齐、填充与sizeof里的猫腻5.1 CPU为什么要求对齐一次读四个字节的故事结构体内部存储没有直接“结构体”这个类型它只是编译器把多个字段拼在一起。但拼的时候不能简单首尾相连——CPU 读取内存通常按字长对齐访问比如 4 字节宽的机器读一个 4 字节 int最理想情况是它正好落在 4 字节对齐的地址上如果跨越两个 4 字节块就得做两次读取加拼接某些平台甚至直接异常。这就是结构体填充的根本原因。编译器在字段之间插入无名字节让每个字段都落在一个对齐的地址上。这类填充字节约占内存但你平时看不出。拿 sizeof 去看才会发现明明字段加起来没那么大结构体却大了一截。很多人在项目里优化内存第一反应是换容器、换算法却忽略了一个最简单的事实结构体本身的布局可能就浪费了三分之一的空间。5.2 sizeof为什么不是字段之和一个6字节的结构体却占了12举一个最常见例子struct S { char a; int b; char c; };每个成员a 占 1 字节b 占 4 字节c 占 1 字节加起来 6 字节。但sizeof(S)通常是 12。为什么因为 b 要求 4 字节对齐a 之后必须先填充 3 个字节才能放 bc 放在 b 之后结构体总大小还得是最大对齐数 4 的倍数于是尾部再填充 3 字节。最终布局是a 3 填充 b(4) c 3 填充 12。同样的字段换个顺序struct S2 { char a; char c; int b; };a 占用偏移 0c 占用偏移 1b 从偏移 4 开始。总大小 8 字节。就一个字段重排内存从 12 变 8省了三分之一。要是几十万个对象的数组这个差距可能是几十 MB。很多大型系统做内存优化时第一件事就是重排结构体字段而不是急着改缓存或者分批处理。5.3 对齐之外的坑序列化与pragma pack结构体填充在进程内部没毛病但一旦你要写文件、走网络整个结构体字节流里就带了大量填充字节。不同编译器、不同平台对齐规则还不一样所以直接把结构体指针转成 char* 来收发基本等于定时炸弹。更稳妥的做法是显式序列化逐字段写入固定字节序的缓冲区。如果非要紧凑排布可以用#pragma pack(1)或者类似的“紧凑模式”请求编译器取消填充。这样可以省内存、方便字节流解析但代价是可能访问未对齐字段牺牲性能甚至在某些硬件上触发异常。我的看法是除非你在写非常底层的通信协议且明确知道目标平台支持否则别滥用。把“字段重排”和“显式序列化”用熟比祈求编译器特殊照顾更靠谱。6. 栈、堆、静态区三种内存区域的生存法则6.1 栈自动分配但在脚下画了个圈先厘清“数据在内存中的存储”不仅指字节排布还包括数据放在哪块区域。程序的地址空间大致分几块代码段、数据段、堆、栈。栈用来放函数调用相关的局部变量、参数、返回值地址它的特点是后进先出函数进入时分配返回时自动释放所以快、无需手动管理。缺点是容量固定通常也就几 MB 到十几 MB递归调几万层就可能溢出程序直接崩。栈上一个经典的坑是返回局部变量地址。函数返回后那块栈内存就可能被其他调用改掉返回的指针指向的内容不可预测。用“悬垂指针”形容非常准确指针还在脚下地皮已经属于别人了。我见过有人在回调函数里攒了一批指针等回调结束再去用结果数据全变了最后追查才发现所有指针都指向同一个被反复覆盖的栈地址。6.2 堆灵活但有代价不释放就漏堆是程序员自己控制的区域malloc/new 从系统申请大块内存然后按需分配。它不像栈那样容量受限但每次分配有开销频繁申请释放会造成碎片更麻烦的是忘记释放——内存泄漏。长期运行的服务里泄漏几 KB 看起来不多日积月累就会把内存吃光。排查泄漏的经典办法是用工具统计分配与释放是否配对。我处理过一个案例某功能模块每请求都 new 一个临时对象但只在特定错误分支忘记 delete平时测试流量小看不出压测一跑内存曲线直线上升。最后靠对比分配记录才定位到那一行缺失的释放。堆上的内存没有“自动收尸”的魔法分配和释放必须像借条一样对得上。6.3 静态区程序启动就位全程只有一份静态区和全局变量在程序启动阶段完成初始化存放在数据段。static 修饰的局部变量虽然写在函数内部但存储位置和全局变量一样生命周期是整个进程而不是函数。这带来两个特性一是未显式初始化时被系统清零二是只有一份多线程并发访问时必须加锁或原子操作否则一个线程改另一个线程读到中间态问题非常隐蔽。存储区域分配方式生命周期典型风险栈函数进入自动分配返回自动释放函数调用期间栈溢出、返回悬垂指针堆手动申请、手动释放从分配到释放泄漏、碎片、重复释放静态区/全局区程序启动分配整个进程多线程竞争、全局状态污染6.4 地址空间的大致画卷如果按地址从低到高粗略看进程地址空间一般是代码段在最底部接着是已初始化数据段和未初始化数据段然后堆从低地址向上生长栈从高地址向下生长中间是自由区。这意味着栈和堆是相向而行的极端情况下可能撞到一起。了解这个布局你再看“栈溢出”和“堆溢出”两个词就知道它们攻击的其实是相邻的地盘。变量在内存中不是孤岛所有字节都在一个大坐标系里互相挨着越界的动作往往不只是破坏自己还会波及邻居。7. 眼见为实用调试器和内存工具看字节排布7.1 别再脑补了直接在调试器里看从上面的原理推导到实战真正常用的手段是调试器内存视图。比如你想确认一个 float 的内存布局可以先赋值 1.0f然后在内存窗口里看那 4 个字节在小端机上你会看到 00 00 80 3f对应的十六进制整数值是 0x3f800000。这和前面讲的 IEEE 754 完全对得上。类似地你可以看 int、double、指针、结构体数组所有的理论都能在这一步得到验证。给一个不用图形界面的办法float f 1.0f; unsigned char *byte (unsigned char *)f; for (int i 0; i 4; i) printf(%02x , byte[i]);输出 00 00 80 3f。看到这串字节你就彻底明白内存里没有“float”只有四个字节是我们用 float 这个模板去解释它。调试器和这行代码做的事是一样的只是更快更直观。遇到任何关于“数据在内存中的存储”的疑问先动手打出一段字节看比反复查文档更管用。7.2 一个真实的排查链路数组越界改掉了邻居变量栈上的变量是紧挨着的越界写极可能擦到邻居。实战中我遇到过这样的代码int arr[4] {1, 2, 3, 4}; int flag 0; for (int i 0; i 4; i) { // 注意4越界一次 arr[i] i * 10; } printf(%d\n, flag);arr[4] 并没有发生在数组的空间里而是写到了相邻内存。如果编译器恰好把 flag 放在那flag 就会被改掉。像这种事读代码看不出只有把变量地址打出来看打印 arr[0]、flag发现两个地址只差几个字节在调试器里对 flag 加内存断点让程序在 flag 被修改的瞬间停下调用栈指向 arr[4] 这行赋值问题定位两分钟搞定。这个排查链路的价值在于遇到“某个变量莫名其妙变了”的诡异 bug先不要怀疑魔法先怀疑相邻内存被越界写。数组越界、memcpy 拷贝长度算错、结构体长度不齐导致 memcpy 覆盖都是同类问题的高发原因。内存断点这招在排查这类问题时比翻日志管用十倍。7.3 我的习惯先打印排布再追业务最后分享一个个人习惯。无论是网络协议、文件解析还是数据结构调试动手前我都先写两三行代码把关键结构体的起始地址、每个字段的偏移、字节序内容打印一遍。别嫌麻烦这能让脑子里的“存储模型”与真实内存对一次表。很多问题在表对完之后就已经暴露了偏移错了、字节序反了、填充字节占位了一目了然。我处理过最快的一次排查是在三分钟内靠一份十六进制转储确定了问题——字段根本没拼错是读取端用了错误的偏移量去解包多读了一个字节。所有关于“数据在内存中的存储”的原理最终都要落到字节上能看见字节就没什么神秘的了。下次你在代码里看到一个变量瘦成一道闪电却总也不对别急着怀疑逻辑先问它一句你现在在内存里到底长的什么样

相关新闻

实时数仓DWS层设计:FlinkSQL构建地区主题表实战

实时数仓DWS层设计:FlinkSQL构建地区主题表实战

做实时数仓的同学应该都有印象,离线T1报表在业务眼里永远是“差一天”的。之前我们接过一个需求:运营要各省的实时GMV,拿来盯当日大促进度。我第一反应是让他们直接查Kafka里的DWD订单明细,结果被怼回来了——半小时内的数据想拉一…

2026/10/18 21:56:18 阅读更多 →
2026年电脑装机选购指南:核心硬件避坑与配置思路

2026年电脑装机选购指南:核心硬件避坑与配置思路

每年开春都会有一波装机高峰,2026年这阵仗明显比往年更大。原因不难理解:新一代CPU和GPU基本完成了换代,旧款价格开始松动,内存和SSD的容量价格也卷到了从未见过的位置。很多人后台问我,“2026年了装机到底怎么选&…

2026/10/18 21:56:18 阅读更多 →
基于SSM的物流管理系统:Java课程设计完整源码与万字文档

基于SSM的物流管理系统:Java课程设计完整源码与万字文档

简介:这份资源面向计算机相关专业的本科生与课程设计学习者,提供一套基于SSM框架的物流管理系统完整实现,可用于毕业设计、期末大作业或课程设计场景。系统采用B/S三层结构,基于JDK1.8与MySQL5.7开发,兼容Eclipse和IDE…

2026/10/18 21:56:18 阅读更多 →

最新新闻

大麦网自动抢票脚本:从配置到下单的完整使用教程

大麦网自动抢票脚本:从配置到下单的完整使用教程

大麦网自动抢票脚本:从配置到下单的完整使用教程 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 开票倒计时归零那一秒,页面已弹出"缺货登记&quo…

2026/10/18 23:40:36 阅读更多 →
Vosk C API 编译避坑指南:Linux/ARM/Windows 三平台源码构建实战

Vosk C API 编译避坑指南:Linux/ARM/Windows 三平台源码构建实战

简介:本资源为已编译完成的Vosk语音识别C语言调用库,专为嵌入式开发、边缘计算及低功耗平台(如树莓派、Windows桌面应用)开发者设计,解决C项目中集成离线语音识别时需自行编译Vosk源码的复杂问题。压缩包共6个文件&…

2026/10/18 23:40:36 阅读更多 →
如何把加密视频真正存到本地:res-downloader 抓包下载与解密实战

如何把加密视频真正存到本地:res-downloader 抓包下载与解密实战

如何把加密视频真正存到本地:res-downloader 抓包下载与解密实战 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader …

2026/10/18 23:40:36 阅读更多 →
Java医药进销存系统:Spring Boot+MyBatis-Plus实战全链路

Java医药进销存系统:Spring Boot+MyBatis-Plus实战全链路

简介:这是一套面向计算机专业本科生毕业设计与Java Web开发初学者的医药行业进销存管理系统源码,聚焦药品采购、销售、库存三大核心业务场景,助力药店、医院药房及小型批发商实现业务流程数字化。资源共944个文件,涵盖105个Java后…

2026/10/18 23:40:36 阅读更多 →
皮尔逊III型曲线拟合实战:从实测流量到设计洪水频率分析

皮尔逊III型曲线拟合实战:从实测流量到设计洪水频率分析

简介:一份用于皮尔逊III型曲线拟合与绘图的MATLAB代码包,面向水文学、统计学及工程防灾领域的研究人员、学生和工程师,解决流量、降雨等自然数据的频率分析问题。压缩包共三个文件,均为MATLAB脚本,整体大小仅3KB&#…

2026/10/18 23:40:36 阅读更多 →
三则AI新闻揭示应用临界点:轻量模型、免费旗舰与团队回迁

三则AI新闻揭示应用临界点:轻量模型、免费旗舰与团队回迁

10月8日早上,我的朋友圈和几个技术群几乎同时被三则AI新闻刷屏:一家主流模型厂商发布了新的轻量快速模型H 5.5,另一家巨头把第六代旗舰模型向普通用户免费开放,还有一个此前把部分团队搬到外地的智能体产品团队,宣布要…

2026/10/18 23:39:35 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →