C语言循环控制结构:从语法到实战避坑指南
写代码这行当干久了你会慢慢发现真正决定程序“智商”的往往不是那些复杂的算法而是最基础的三件事顺序、选择、循环。前两件事好理解一行一行执行遇到岔路判断一下方向。但一旦遇上“重复做某件事”比如打印1到100、统计一个班四十个人的成绩、在十万个数字里找一个目标值要是没有循环代码能把人写到崩溃。我刚入门的时候就干过把同一个输出语句复制粘贴一百遍的蠢事后来被带我的同事狠狠教育了一顿才老老实实把C语言里的循环控制结构啃扎实。说白了循环控制结构就是让计算机替你干那些“重复但有规律”的活儿的钥匙这也正是计算机最擅长的事。这篇内容不扯虚的直接从实际使用的角度把C语言里的三种循环——while、do-while、for连同break、continue这两个辅助控制语句一个一个拆开讲清楚。每一个知识点都会配上能直接运行的代码片段并且把我这些年用循环时踩过的坑、总结出来的调试技巧一并写出来。不管你是刚学编程的在校生还是自学C语言想补基础的转行者这篇文章都适合你。读完不需要你记住多少语法细节只需要你明白下次遇到“重复处理”的题目时大脑里能第一时间浮现出循环的框架并且知道该选哪一种循环来写。1. 整体设计与思路拆解为什么要“绕圈子”执行代码很多初学者对循环的第一反应是“麻烦”觉得代码一行一行写完不就行了吗为什么要来回跳转这里我先讲一个最朴素的应用场景。假如老师布置了一个作业让你在屏幕上打印十次“Hello”你不写循环就得写十个printf语句。那打印一百次呢一千次呢就算你真有耐心复制一千行万一哪一次需要改输出内容你得手动改一千处。循环解决的就是这个“批量处理”的痛点用一段简短的代码描述“重复执行的规律”再由计算机自动去执行这既是循环的魅力也是程序效率的来源。1.1 循环三要素初始化、条件、更新用我自己的理解方式可以把任何循环都拆成三个部分。第一是初始化给循环变量一个起点就像跑步比赛前站到起跑线上第二是条件判断每次循环开始前确认“还跑不跑”相当于比赛的终点线第三是更新操作跑完一圈之后把变量往终点推进一步相当于每跑一圈数一次数。这三个要素配合好循环才会按预期停止否则就是死循环。用代码来对照就是int i 0; // 初始化起点 while (i 10) // 条件终点线 { printf(%d , i); i i 1; // 更新每跑一圈加一次数 }这段代码会打印0到9。我见过不少初学者把更新操作漏掉结果程序进入了死循环控制台窗口疯狂刷屏最后只能强制关闭。这是学习循环的第一个要命的地方也恰恰说明三要素缺一不可。1.2 三种循环的基本语法与执行顺序C语言提供三种循环语法很多人把它们割裂来记其实没必要。从执行逻辑上看它们之间可以互相转换区别主要在“判断时机”和“书写习惯”上。while和for都是先判断条件、后执行循环体条件不满足时循环体一次都不执行do-while则是先执行一次循环体、再判断条件所以循环体至少会执行一次。// while循环先判断再执行 int a 0; while (a 3) { printf(a%d\n, a); a; } // do-while循环先执行再判断 int b 0; do { printf(b%d\n, b); b; } while (b 3); // for循环把三要素集中在头部 for (int c 0; c 3; c) { printf(c%d\n, c); }三种写法最终打印的效果一样但do-while有一个特殊场景是其他两种比不了的比如判断用户输入是否合法总得先让用户输入一次再判断要不要重新输入。这种场景用do-while写起来非常顺而while的写法反而要在循环前额外写一遍输入代码。我平时写交互式菜单程序时就特别偏爱do-while。1.3 选型依据什么时候用哪种循环直接给你一个可以“抄作业”的判断标准是我在带新人时反反复复讲的。如果循环次数是事先确定或者能用计数器控制的无脑用for比如遍历数组、打印固定行数如果循环次数不确定只能靠某个条件是否成立来判断的用while比如读取文件直到文件末尾如果无论如何都要先执行一次循环体再根据结果判断是否继续的用do-while比如菜单选择输入。循环类型判断时机最少执行次数典型场景while先判断后执行0次条件等待、文件读取do-while先执行后判断1次菜单输入、用户输入校验for先判断后执行0次计数遍历、固定次数循环记住这张表就够了。我见过不少工作了几年的人写代码也经常while和for混用不是说不行但代码读起来总是不够直白。遵循这个选型标准你的代码会清晰很多别人接手也省心。2. 核心细节解析与实操要点循环边界的判断与流程跳转语法谁都能背下来真正拉开差距的是细节。循环控制结构里最容易出错的地方几乎都集中在边界条件和流程控制语句的使用上。我写代码十二年回头看看早年犯的错一半以上都出在这些看似不起眼的小地方。2.1 循环变量的边界小于还是小于等于这是初学者问得最多的问题。打印十次内容到底写i 10还是i 10我的习惯是优先使用左闭右开区间也就是i 10。为什么因为这种写法有一个明显的好处循环次数刚好等于条件里的那个数。i 10执行10次i n执行n次一目了然。而且很多底层API和数组下标的取值范围也是左闭右开的保持同样的习惯可以降低认知负担。再比如从5数到10你会看到有人写for (int i 5; i 10; i)也有人写for (int i 5; i 11; i)。两种都对但意思的清晰程度不一样。i 11看起来会有“为什么是11”的疑惑而i 10直白地告诉你“包含10”。所以在边界是孤立的数值时用包含式更合适在表达次数时用左闭右开更合适。我建议你把这两种思维都记下来根据场景选择比死记一个规则要靠谱。2.2 循环条件中的浮点数比较陷阱用一个真实踩过的坑说明这个问题。当时我需要把0.1累加十次判断结果是否为1.0我下意识写了这样的代码double sum 0.0; for (int i 0; i 10; i) { sum 0.1; } if (sum 1.0) { printf(相等\n); } else { printf(不相等sum%.20f\n, sum); }运行结果是“不相等”打印出来的sum是0.99999999999999988898。原因并不神秘0.1在计算机的二进制浮点数里并不是精确的0.1累加十次之后误差就累积出来了。从那以后我给自己定了一条规则循环条件里不要用浮点数比较尤其是相等判断。必要的浮点累加场景要么用整数计数循环要么判断差值小于一个很小的误差值比如fabs(sum - 1.0) 1e-6。// 推荐做法用整数作为循环变量 double sum 0.0; for (int i 0; i 10; i) { sum 0.1; } if (fabs(sum - 1.0) 1e-6) { printf(近似相等\n); }写循环条件的本质是在定义一个“边界”而浮点数的边界是非常不可靠的。能避开就避开不能避开就用误差区间判断。2.3 break和continue循环里的急刹车与跳过牌break用于立即终止当前所在的循环程序跳到循环外继续执行continue则跳过本次循环的剩余语句直接进入下一次循环的条件判断。初学者最容易混淆这两个词。我用一个生活中的例子说明假设你在操场跑10圈break的意思是“我不跑了直接离场”continue的意思是“这一圈我状态不好不跑了但下一圈我还接着来”。很明显前者彻底结束后者只是跳过一轮。为了看清它们的区别可以写一小段代码for (int i 1; i 10; i) { if (i 5) { break; // 运行到这里直接终止循环 } printf(%d , i); } // 输出1 2 3 4 for (int i 1; i 10; i) { if (i 5) { continue; // 跳过5这一轮继续6到10 } printf(%d , i); } // 输出1 2 3 4 6 7 8 9 10这段代码非常经典我建议你亲手敲一遍。还有一个容易被忽略的细节break在switch语句里同样起到跳出作用而且如果break用在嵌套循环里它只跳出最内层的那一层循环而不是一次性跳出所有循环。这个特性后面实操部分会专门讲。3. 实操过程与核心环节实现三个经典案例手把手拆解掌握了语法和边界判断现在进入动手环节。C语言循环控制结构的技能树我认为用三个经典案例就可以点满百分之八十累加器模式、双重循环嵌套、枚举算法。这三个案例覆盖了循环的绝大多数应用场景代码量不大但逻辑密度很高。3.1 案例一累加求和与连乘中的初始化陷阱累加器是最基础的循环模式比如计算1加到100的和。很多教材会直接给代码但没有告诉你为什么累加变量sum必须初始化为0。我换个角度讲初始化为0是因为任何数加0都不会改变它本身这叫“零元”而连乘时mul必须初始化为1是因为任何数乘以1都不会改变本身这叫“幺元”。这两个词听着唬人道理其实很简单。如果连乘时错误地初始化为0结果永远只能是0。long sum 0; for (int i 1; i 100; i) { sum i; } printf(1到100的和%ld\n, sum); long mul 1; for (int i 1; i 10; i) { mul * i; } printf(1到10的乘积%ld\n, mul);溢出问题也得提醒一句。1到20的乘积用int存储就会溢出结果变成一个莫名其妙的负数。计算规模较大时把累加变量声明为long再大时用long long。这类问题不会直接报错只会逻辑上出错排查起来很隐蔽。3.2 案例二嵌套循环输出九九乘法表嵌套循环是双重循环的典型应用外循环控制行内循环控制列。九九乘法表是理解嵌套循环的最佳练习因为行和列之间存在明确的依赖关系第i行有i列正好用内层循环的边界去依赖外层循环变量。for (int i 1; i 9; i) { for (int j 1; j i; j) { printf(%d*%d%-2d , j, i, j * i); } printf(\n); }输出效果是左下三角的乘法表。这里有一个细节%-2d表示左对齐、占2个字符宽度用它可以让输出的表格对齐整齐不至于数字一长一短看着乱。这个格式化输出的习惯我在实际开发里经常用推荐你留意一下。写这个案例时我建议你人为地出几个错把内层循环的j i误写成j 9观察输出从三角变成矩形把外层从i 1改成i 0观察第一行变成零乘法。这种“故意改错”的训练方法比单纯照着敲代码有效得多。3.3 案例三质数判断与sqrt优化判断一个整数是否为质数思路是枚举所有可能的因子。一个数如果在2到它自身减1之间没有任何因子它就是质数。这里我不想写那种从头枚举到尾的笨代码因为完全没有必要。假设要判断一个数n一旦发现了大于sqrt(n)的因子必然会有一个**小于sqrt(n)**的因子与它对应。所以枚举范围只需要到sqrt(n)计算量从n次缩减到大约根号n次效率提升非常明显。#include stdio.h #include math.h int is_prime(int n) { if (n 2) { return 0; } for (int i 2; i sqrt(n); i) { if (n % i 0) { return 0; } } return 1; } int main(void) { int num; printf(请输入一个正整数); scanf(%d, num); if (is_prime(num)) { printf(%d是质数\n, num); } else { printf(%d不是质数\n, num); } return 0; }注意return 0写在循环体内一旦发现一个因子函数立刻结束并返回“不是质数”这其实就是一种提前退出的思想。用return来中断函数比用break标志位要干净得多。这段代码我能背下来每次讲goto和break的区别时也拿它举例因为在质数判断里中途退出完全合法而且让代码变得非常短小清晰。3.4 从代码数量级看循环性能差异嵌套循环带来的性能问题是面试里高频出现的点实际开发中也不能忽略。假设外层n次、内层m次简单的双重循环复杂度就是O(n*m)。如果你能把内层循环做到提前退出最坏情况和平均情况会差很多。拿质数判断加上素数计数来说遍历1到100000之间所有数逐个用慢办法枚举和用sqrt办法枚举时间差距肉眼可见。我在机器上实测过从大约5秒多降到不到0.1秒。这不是玄学就是循环次数实打实减少了。写循环代码时脑子里要有一根弦循环层数越深总执行次数增长越快。平时能单层解决就不要叠两层能提前退出就写提前退出。这不是让你过度优化而是在一开始就避免写出明显会被数据规模压垮的代码。4. 常见问题与排查技巧实录多轮调试换来的避坑经验代码写多了出错是常态关键是要有快速的排查思路。下面这些问题每一个都是我在教学和实际项目里真实遇到过的记录下来希望能让你少走弯路。4.1 死循环忘记更新循环变量的经典现场死循环可能是循环学习里最让人崩溃的问题表现形式有两种一是程序完全卡死二是控制台窗口疯狂输出。前者一般是循环体内没有任何输出语句CPU全在空转后者则是一看输出就猜到了原因。最经典的死循环写法是int i 0; while (i 10) { printf(i%d\n, i); // 忘写 i; }变量i永远是0条件永远成立循环永远出不来。排查这类问题的顺序我建议是这样先看初始化再看条件表达式有没有可能永远为真最后看循环体最后一行有没有更新语句。如果代码量很大就在循环体里临时加一个printf打印循环变量看它是否按预期变化这叫打点调试法。用完记得删掉这些临时输出不然代码会显得很邋遢。4.2 边界多跑一轮循环方向反了或者边界开错另一种常见错误是循环多执行一次或者少执行一次。最典型的是数组遍历C语言数组索引从0开始遍历长度为n的数组条件应该是i n但新手很容易写成i n结果越界访问轻则读到脏数据重则程序崩溃。遍历数组的正规写法如下int arr[5] {10, 20, 30, 40, 50}; for (int i 0; i 5; i) { printf(arr[%d]%d\n, i, arr[i]); }要格外注意C语言并不检查数组越界因此这类错误完全靠程序员自己把关。我现在的习惯是在每次写循环前都先默念一遍“次数是多少下标范围是多少”两个数字对上号了再往下写。4.3 嵌套循环中的break和continue到底跳出的是哪一层前面提到break只跳出最内层的循环这是嵌套循环里特别容易出理解偏差的地方。看下面这段代码for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (j 1) { break; } printf(i%d j%d\n, i, j); } printf(内层循环结束i%d\n, i); }输出结果里内层的break只会让j从0跳到2但外层循环还是正常会执行3次。如果你想在满足某个条件时彻底退出所有嵌套循环只靠break是不够的需要引入一个标志位或者让函数直接return。我的建议是在多层嵌套里需要彻底退出时把逻辑封装成一个函数用return退出这样代码比用标志位更直观也更好维护。4.4 continue在while循环里的特殊性continue对for循环很好用它会跳到for语句的更新表达式那一行然后判断条件。但放在while循环里如果循环变量的更新写在continue之后continue会直接跳过更新语句从而引发死循环int i 0; while (i 10) { if (i 5) { continue; // 跳过了下面的 ii永远是5 } printf(%d , i); i; }这个例子运行起来会卡死在i等于5那里。很多人直到程序刷屏都没反应过来是continue的问题。经验是如果while循环里用到continue就把循环变量的更新放在循环体的第一行或者干脆用for循环代替这样能省掉很多不必要的排查时间。这不是什么高深的技巧纯粹是踩坑之后的习惯养成。4.5 循环相关问题排查速查表把上面几个问题整理成速查表方便你写代码卡壳时对照。问题表现可能原因解决办法程序卡死、CPU占用高循环变量未更新或更新被跳过检查循环体末端是否有icontinue是否跳过了更新循环次数比预期多一次边界用了i n而不是i n明确循环次数数组遍历统一用i 长度循环一次都没执行while/for条件初始就不成立检查初始化值与条件的关系确认是否应为do-while输出杂乱无章嵌套循环中break跳错层记住break只结束最内层必要时用return提前返回浮点累加结果不对浮点数二进制精度误差累积用整数做循环变量比较时用误差范围判断循环内声明的变量外面用不了变量作用域仅限于循环体需要在循环外使用的数据在循环外先声明变量这一套表是我整理给自己团队新人看的每年都有人来问同样的问题。大部分循环问题翻来覆去都是这些原因定位到具体类型修起来就快多了。我个人在实际调试中的体会是循环语句写错了并不可怕可怕的是对着屏幕干瞪眼毫无章法地乱试。先把三要素列出来初始化、条件、更新逐一核对再确认break和continue的作用范围最后再检查边界是否包含端点。按这个顺序走一遍九成以上的循环问题都能在五分钟内找到根源。写循环多一点耐心把每个案例亲手敲出来执行一遍比看十篇文章都管用。

相关新闻

HashMap扩容机制深度解析:从位运算到工程实践

HashMap扩容机制深度解析:从位运算到工程实践

HashMap的扩容机制,是Java面试里出现频率几乎最高的题,也是线上定位Map相关性能瓶颈时绕不开的底层逻辑。大多数人都能背出"当元素数量超过阈值就扩容到两倍",但问到"为什么是2倍而不是1.5倍"、"扩容时节点到底怎么…

2026/10/16 17:16:21 阅读更多 →
卷积网络中Padding全解析:Valid与Same的数学原理、工程选型与调试实战

卷积网络中Padding全解析:Valid与Same的数学原理、工程选型与调试实战

聊到 CNN 卷积网络,很多人会把注意力放在卷积核大小、步长、激活函数这些参数上,真正把 padding 的两种方式彻底吃透的人并不多。我第一次意识到 padding 是个大问题,是在搭建一个图像分割网络时:模型结构看起来完全正常&#xff…

2026/10/16 17:15:20 阅读更多 →
TR 38.901信道建模原理:从坐标系到空间一致性

TR 38.901信道建模原理:从坐标系到空间一致性

简介:本资源为3GPP官方技术报告TR 38.901第14版(v14.3.0,2017年12月发布)的完整中文可读文档,面向5G无线通信系统研发工程师、信道建模研究人员及高校通信专业高年级学生。该报告系统定义了0.5–100 GHz频段&#xff0…

2026/10/16 17:15:20 阅读更多 →

最新新闻

TDH Workflow 工作流调度实战:从设计原理到生产落地

TDH Workflow 工作流调度实战:从设计原理到生产落地

1. 从一个调度需求说起:为什么需要 Workflow第一次接触 TDH 里的 Workflow,是因为一个很具体的需求:每天凌晨要跑一批数据处理任务,任务之间有明确的先后依赖,前一个跑完才能跑下一个,中间任何一个环节失败…

2026/10/16 17:53:21 阅读更多 →
交换机能带多少主机?接口不够怎么办?从端口规划到扩展方案全解析

交换机能带多少主机?接口不够怎么办?从端口规划到扩展方案全解析

机房网络改造这件事,我前前后后摸爬滚打了好些年。今天聊的这个话题,几乎每个搞过机房或办公网络的人都会遇到: 一台交换机到底能接多少台主机,口不够了又该怎么办 。这个问题看起来平平无奇,但真到了现场&#xff0…

2026/10/16 17:53:21 阅读更多 →
.NET 8.0下FastReport报表打印:许可证注册与水印清除指南

.NET 8.0下FastReport报表打印:许可证注册与水印清除指南

简介:.NET 8.0 环境下基于 FastReport 实现无水印报表打印与设计的完整示例包,主要面向使用 WinForms 开发桌面报表功能的程序员,解决在高版本 .NET 中集成 FastReport 时遇到的授权限制与方案搭建问题。包内含一个可直接运行的 WinForms 项目…

2026/10/16 17:53:21 阅读更多 →
SSM企业官网源码包实战:从环境搭建到二次开发避坑指南

SSM企业官网源码包实战:从环境搭建到二次开发避坑指南

简介:这份资源是一套基于SSM(Spring、SpringMVC、Mybatis)框架开发的Java企业官网源代码,面向具备Java Web基础、希望学习或二次开发企业级门户网站的开发者与在校学生。项目整合MySQL数据库与JSP视图技术,包含前台门户…

2026/10/16 17:53:21 阅读更多 →
DeepSeek本地部署全指南:工具选型、硬件配置与避坑实践

DeepSeek本地部署全指南:工具选型、硬件配置与避坑实践

简介:这是一份面向具备一定AI技术基础开发者的DeepSeek本地部署实操指南,适用于希望将先进开源模型应用于个人项目、工作场景或进行深度研究的技术人员。文档以Ollama、LM Studio、Jan三种主流部署工具为主线,从硬件最低配置到推荐配置、操作…

2026/10/16 17:53:21 阅读更多 →
STM32+MPU6050姿态解算源码详解:I2C通信与DMP固件实战

STM32+MPU6050姿态解算源码详解:I2C通信与DMP固件实战

简介:面向嵌入式入门与进阶开发者,一套基于STM32F103C8T6与MPU6050的Keil完整工程可直接编译下载使用。工程自带标准外设库源码与头文件,并配套完整示例与说明文档,覆盖I2C读取MPU6050原始数据、姿态解算、串口输出及定时器、ADC、…

2026/10/16 17:52:20 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →