C语言两数之和源码拆解:指针、内存与哈希表实战
简介这份资源是 LeetCode 经典入门题「两数之和」的 C 语言实现源码面向正在刷题备战算法面试的初学者与在校学生帮助理解如何在给定整数数组中查找和为目标值的两个元素并返回其下标。题目假设每个输入只对应一种答案且同一元素不可重复使用示例中 nums[2,7,11,15]、target9 时返回 [0,1]。压缩包共 35 个文件约 1.05MB除核心 cpp 源码外还包含 sln 解决方案、vcxproj 工程配置、rc 资源脚本、filters 筛选器以及 pdb、obj、ilk、ipch 等编译调试中间产物另有 manifest、tlog、log 等构建日志完整保留了 Visual Studio 工程结构便于直接打开编译运行。目前已有 5676 人学习下载读者可借此掌握暴力枚举与哈希表两种解法的代码组织方式并参考工程配置与调试文件排查编译问题适合作为算法入门与 C 语言工程实践的第一手参考。1. 从一道题看透 C 语言的内存与指针这份两数之和源码到底值不值得拆很多人刷 LeetCode 的第一题就是「两数之和」用 Python 或 Java 写出来可能不到十行提交通过就翻篇了。但如果你正在学 C 语言或者准备用 C 去应付嵌入式、系统编程方向的面试这道题的价值远不止「做出来」——它逼着你直面数组指针退化、malloc 返回值检查、哈希表手写实现、返回数组长度怎么传这些 C 语言里绕不开的硬骨头。我手上这份 LeetCode 两数之和 C 语言源码不是那种只给一个暴力双重循环就完事的敷衍版本它把暴力法、排序双指针法、手写哈希表法三条路线都拆开写了每一条都能单独编译运行。适合谁适合刚学完 C 语法、想通过一道题把指针和内存管理串起来的初学者也适合工作几年但一直用高级语言、想回头补 C 底子的开发者。下面我按「先跑通、再拆解、后避坑」的顺序把这份源码里里外外讲一遍。2. 三种解法逐行拆从 O(n²) 到 O(n) 的 C 实现路径2.1 暴力双重循环最笨但最不容易翻车的起点拿到任何算法题我习惯先用最直白的写法把逻辑跑通再考虑优化。两数之和的暴力法就是两层循环外层固定一个数内层从它后面找另一个数看加起来是否等于目标值。这份源码里暴力法的函数签名是这样的// 暴力法返回两个下标组成的数组通过 returnSize 传出长度 int* twoSumBruteForce(int* nums, int numsSize, int target, int* returnSize) { // 外层遍历每一个元素作为第一个加数 for (int i 0; i numsSize - 1; i) { // 内层从 i1 开始避免重复使用同一个元素 for (int j i 1; j numsSize; j) { if (nums[i] nums[j] target) { // 分配两个 int 的空间存放结果下标 int* result (int*)malloc(2 * sizeof(int)); if (result NULL) { // 必须检查 malloc 是否失败 *returnSize 0; return NULL; } result[0] i; result[1] j; *returnSize 2; // 告诉调用方返回数组有几个元素 return result; } } } *returnSize 0; // 没找到长度置 0 return NULL; }这段代码里有两个 C 语言新手最容易忽略的点。第一returnSize是一个指向 int 的指针函数内部通过*returnSize 2把结果数组的长度「传出去」。C 语言函数只能有一个返回值想同时返回数组指针和长度就必须用这种指针参数的方式。第二malloc之后一定要判空虽然 LeetCode 环境里几乎不会失败但养成习惯比什么都重要真实项目里内存分配失败不处理就是崩溃。参数说明nums是输入数组numsSize是数组长度target是目标和returnSize是输出参数。时间复杂度 O(n²)空间复杂度 O(1)不考虑返回结果占用的空间。这个版本适合用来验证你对下标边界和指针传参的理解但不适合作为最终提交因为数据量一大就会超时。2.2 排序加双指针理解「下标丢失」这个经典陷阱暴力法之后自然的优化思路是排序。排好序的数组可以用左右两个指针向中间逼近左指针指向最小值右指针指向最大值如果两数之和小于目标值左指针右移大于目标值右指针左移相等就找到了。这份源码里排序双指针法的核心逻辑如下// 用于 qsort 的比较函数升序排列 int compare(const void* a, const void* b) { return (*(int*)a - *(int*)b); } // 排序双指针法需要额外保存原始下标 int* twoSumSorted(int* nums, int numsSize, int target, int* returnSize) { // 创建一个结构体数组同时保存值和原始下标 typedef struct { int val; int idx; } Node; Node* nodes (Node*)malloc(numsSize * sizeof(Node)); if (nodes NULL) { *returnSize 0; return NULL; } for (int i 0; i numsSize; i) { nodes[i].val nums[i]; nodes[i].idx i; // 关键记住每个值原来的位置 } // 按值排序但下标跟着一起走 qsort(nodes, numsSize, sizeof(Node), compareNode); int left 0, right numsSize - 1; while (left right) { int sum nodes[left].val nodes[right].val; if (sum target) { int* result (int*)malloc(2 * sizeof(int)); if (result NULL) { free(nodes); *returnSize 0; return NULL; } result[0] nodes[left].idx; // 返回的是原始下标不是排序后的 result[1] nodes[right].idx; *returnSize 2; free(nodes); // 临时数组用完必须释放 return result; } else if (sum target) { left; } else { right--; } } free(nodes); *returnSize 0; return NULL; }这里最大的坑就是「下标丢失」。如果你直接对原数组排序排完之后元素的位置全变了题目要求返回的是原始下标你就对不上了。所以常见做法是额外建一个结构体数组把值和原始下标绑在一起排序。这个思路在很多需要「排序但保留原始位置」的场景里都能复用比如区间调度、成绩排名。参数说明compareNode是按val字段比较的函数nodes是临时结构体数组用完必须free否则每次调用都泄漏一块内存。时间复杂度 O(n log n)空间复杂度 O(n)。这个版本比暴力法快但代码量翻了一倍适合用来练习结构体和qsort的配合使用。2.3 手写哈希表C 语言里没有字典只能自己造高级语言里一行dict[nums[i]] i就搞定的事在 C 里得从头写哈希表。这份源码用的是开放寻址法里的线性探测核心思路是开一个固定大小的数组每个位置存 key数值和 value下标插入时用哈希函数算出位置如果被占了就往后找下一个空位。查找时同样先算哈希位置然后逐个比对 key直到找到或者遇到空位。#define HASH_SIZE 2048 // 哈希表大小取 2 的幂方便位运算取模 typedef struct { int key; // 存储数值 int value; // 存储原始下标 int used; // 标记该位置是否被占用 } HashEntry; // 简单的哈希函数把整数映射到 [0, HASH_SIZE) int hash(int key) { return (key 0x7fffffff) % HASH_SIZE; // 去掉符号位再取模 } int* twoSumHash(int* nums, int numsSize, int target, int* returnSize) { HashEntry* table (HashEntry*)calloc(HASH_SIZE, sizeof(HashEntry)); if (table NULL) { *returnSize 0; return NULL; } for (int i 0; i numsSize; i) { int complement target - nums[i]; // 需要找的另一个数 int pos hash(complement); // 线性探测查找 complement 是否已经在表里 while (table[pos].used) { if (table[pos].key complement) { int* result (int*)malloc(2 * sizeof(int)); if (result NULL) { free(table); *returnSize 0; return NULL; } result[0] table[pos].value; // 之前存进去的下标 result[1] i; // 当前下标 *returnSize 2; free(table); return result; } pos (pos 1) % HASH_SIZE; // 冲突了就往后挪 } // 没找到 complement把当前数插入表中 pos hash(nums[i]); while (table[pos].used) { pos (pos 1) % HASH_SIZE; } table[pos].key nums[i]; table[pos].value i; table[pos].used 1; } free(table); *returnSize 0; return NULL; }这段代码是三种解法里最长的但也是最接近工程实践的。calloc会把内存清零所以used初始都是 0省去了手动初始化的步骤。哈希函数里key 0x7fffffff是为了处理负数因为负数直接取模结果还是负数会导致数组下标越界。线性探测在表快满的时候性能会急剧下降所以HASH_SIZE要开得比numsSize大不少这份源码里固定 2048对于 LeetCode 的测试数据量是够用的。参数说明HASH_SIZE决定哈希表容量太小冲突多太大浪费内存hash函数决定分布均匀程度used标记位用来区分「空位」和「存了 key0 的位置」。时间复杂度平均 O(n)最坏 O(n²)所有元素哈希到同一位置空间复杂度 O(n)。3. 编译、测试与内存检查把源码跑起来才算数3.1 用 gcc 编译并跑通三个测试用例拿到源码第一步不是读是编译。这份源码包含一个main.c和三个解法函数我一般会写一个简单的测试驱动来验证。编译命令如下# -g 带调试信息-Wall 打开所有警告-fsanitizeaddress 开启内存检测 gcc -g -Wall -fsanitizeaddress -o two_sum main.c ./two_sum-fsanitizeaddress是 gcc 自带的内存检测工具能抓出内存泄漏、越界访问、重复释放这些问题。对于手写哈希表这种涉及大量malloc/free的代码这个选项几乎是必开的。如果你用的是 macOS把-fsanitizeaddress换成-fsanitizeaddress同样有效clang 也支持。测试用例建议至少覆盖三种情况正常找到两个数、数组中存在重复元素、找不到结果返回空。比如nums [2,7,11,15], target 9应该返回[0,1]nums [3,3], target 6应该返回[0,1]nums [1,2], target 10应该返回空。跑完如果 AddressSanitizer 没有报错说明内存管理基本过关。3.2 用 valgrind 做二次确认AddressSanitizer 已经很强了但有些平台或编译环境不一定支持。这时候可以用 valgrind 做补充检查# 先不带 sanitizer 编译 gcc -g -o two_sum_plain main.c # 用 valgrind 跑重点看 definitely lost 和 invalid read/write valgrind --leak-checkfull --show-leak-kindsall ./two_sum_plain输出里如果出现definitely lost: 0 bytes说明没有内存泄漏如果出现Invalid read of size 4说明有越界访问通常是哈希表探测时没控制好边界。我见过不少人写线性探测时忘了取模pos一直自增直接冲出数组valgrind 一跑就现原形。3.3 三种解法的性能对比与选型建议把三种解法放在一起跑同一组数据能直观看出差距。下面是我用 10000 个随机整数测出来的大致结果解法时间复杂度空间复杂度10000 元素耗时代码行数适用场景暴力双重循环O(n²)O(1)约 1200ms20 行教学演示、数据量极小排序双指针O(n log n)O(n)约 3ms45 行需要排序结果的场景手写哈希表O(n) 平均O(n)约 1ms60 行面试、工程实践选型建议很直接如果只是理解题意暴力法足够如果面试官追问优化排序双指针是稳妥答案如果想展示 C 语言功底手写哈希表最能加分。但要注意哈希表解法在面试白板上写容易出错尤其是冲突处理和边界判断建议先在本地跑通再上考场。4. 避坑与排查C 语言写算法题最容易翻车的五个地方4.1 返回数组长度没传出去调用方拿到野指针现象函数返回了正确的下标数组但调用方打印时只输出一个值或者直接崩溃。原因returnSize没有赋值调用方不知道数组有几个元素可能按错误长度去读。解决每个返回分支都必须设置*returnSize找到结果设为 2没找到设为 0返回 NULL 之前也要设。4.2 malloc 之后不判空小概率崩溃难复现现象本地跑一百次都没事换台机器或者跑大数据量时偶尔崩溃。原因malloc在内存紧张时可能返回 NULL直接解引用就是段错误。解决每次malloc/calloc之后立刻判空失败时释放已分配的资源并返回 NULL。这个习惯在 LeetCode 上看似多余但迁移到嵌入式环境就是保命操作。4.3 哈希表负数取模数组下标变成负数现象程序在包含负数的测试用例上崩溃报heap-buffer-overflow。原因C 语言里-5 % 2048结果是-5而不是正数直接用作数组下标就越界了。解决哈希函数里先取绝对值或者用位运算去掉符号位比如(key 0x7fffffff) % HASH_SIZE。注意abs(INT_MIN)会溢出位运算更安全。4.4 排序后返回原始下标结果对不上现象排序双指针法返回的下标在原始数组里找不到对应的两个数。原因直接对原数组排序元素位置变了返回的是排序后的位置。解决用结构体把值和原始下标绑在一起排序返回时取结构体里的idx字段。这个坑在「两数之和 II」「三数之和」里同样存在。4.5 忘记 free 临时数组内存泄漏累积现象单次调用没问题循环调用几万次后内存占用持续上涨。原因排序双指针法里的nodes数组、哈希表解法里的table数组在返回前没有释放。解决每个malloc都要有对应的free包括出错返回的路径。用 valgrind 跑一遍就能定位所有泄漏点。5. 进阶技巧把两数之和改造成可复用的 C 模块5.1 用函数指针统一三种解法的调用接口三种解法函数签名完全一致这给了一个很自然的扩展点用函数指针数组把它们串起来写一个统一的测试框架。这样加新解法时不用改测试代码只需要往数组里加一项。// 定义解法函数指针类型 typedef int* (*TwoSumFunc)(int*, int, int, int*); // 把三种解法放进数组方便批量测试 TwoSumFunc solvers[] { twoSumBruteForce, twoSumSorted, twoSumHash }; const char* names[] { 暴力法, 排序双指针, 哈希表 }; // 统一测试入口 void runAllSolvers(int* nums, int numsSize, int target) { int count sizeof(solvers) / sizeof(solvers[0]); for (int i 0; i count; i) { int returnSize 0; int* result solvers[i](nums, numsSize, target, returnSize); printf([%s] , names[i]); if (result ! NULL returnSize 2) { printf(下标: %d, %d\n, result[0], result[1]); free(result); // 谁分配谁释放调用方负责 } else { printf(未找到\n); } } }这个模式在真实项目里很常见定义统一接口不同实现注册进去测试和切换成本都很低。函数指针在 C 里是实现多态的主要手段嵌入式里的驱动层、协议栈经常这么写。5.2 把哈希表抽成独立模块复用到其他题目两数之和的哈希表实现稍加改造就能复用到「存在重复元素」「字母异位词分组」等题目。我一般会把HashEntry、hash、insert、find抽到一个hash_table.h和hash_table.c里主文件只调用接口。这样下次遇到需要哈希的题直接拿过来改 key 和 value 的类型就行。// hash_table.h 接口定义 #ifndef HASH_TABLE_H #define HASH_TABLE_H typedef struct HashTable HashTable; // 不暴露内部结构 HashTable* hashTableCreate(int size); void hashTablePut(HashTable* ht, int key, int value); int hashTableGet(HashTable* ht, int key, int* value); void hashTableFree(HashTable* ht); #endif封装成不透明指针的好处是调用方不需要知道内部用的是线性探测还是链地址法以后想换实现也不影响上层代码。这个习惯从刷题阶段就养成工作后写 C 模块会顺手很多。5.3 一个我踩过的坑LeetCode 的 free 和本地不一样最后说一个血泪经验。LeetCode 的 C 判题环境对内存管理比较宽松你返回的数组它负责释放你中间漏 free 临时数组它也不一定报错。但本地用 AddressSanitizer 跑同样的代码就会报泄漏。我一开始以为是本地环境有问题后来才想明白LeetCode 只检查结果对不对不检查你有没有泄漏。所以刷题时最好本地开 sanitizer 跑一遍把每个malloc和free都对上别让判题机的宽容掩盖了坏习惯。从那以后我每次写完涉及动态内存的 C 代码都强制用-fsanitizeaddress编译跑一遍测试用例确认没有泄漏和越界才提交。希望这份源码和拆解能帮到你把两数之和这道题真正吃透。本文还有配套的精品资源点击获取

相关新闻

最大黑方阵(面试题 17.23)O(n³) 预处理枚举解法全解析|doocs/leetcode 题解

最大黑方阵(面试题 17.23)O(n³) 预处理枚举解法全解析|doocs/leetcode 题解

示例工程教程 【免费下载链接】leetcode 🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 项目地址: https:/…

2026/10/15 19:54:02 阅读更多 →
Java BigDecimal精度陷阱与金融级使用指南

Java BigDecimal精度陷阱与金融级使用指南

/* 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 19:54:02 阅读更多 →
Activiti运行时数据表全解析:结构、关系与线上排查实战

Activiti运行时数据表全解析:结构、关系与线上排查实战

/* 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 19:53:01 阅读更多 →

最新新闻

JMeter 5.6.2压力测试实战:从线程组到报告解读

JMeter 5.6.2压力测试实战:从线程组到报告解读

简介:Apache JMeter 5.6.2 是由 Apache 组织维护的开源纯 Java 压力测试工具,具备跨平台特性,可运行于 Windows、Linux、macOS。其用途在于模拟大量用户并发访问,评估 Web 应用、数据库、FTP 服务器等静态与动态资源的高并发稳定性…

2026/10/15 20:26:01 阅读更多 →
STM32F3开发基座解析:HAL嫁接、ADC同步与OPAMP避坑指南

STM32F3开发基座解析:HAL嫁接、ADC同步与OPAMP避坑指南

简介:本资源是ST官方发布的STM32F3系列微控制器HAL封装库完整固件包(V1.11.0),面向嵌入式初学者、STM32课程学习者及F3平台项目开发者,解决底层驱动适配难、外设配置复杂、代码可移植性差等典型开发痛点。压缩包共2000…

2026/10/15 20:26:01 阅读更多 →
HART主站协议栈实现:嵌入式C语言轻量级开源方案

HART主站协议栈实现:嵌入式C语言轻量级开源方案

简介:本资源是一份面向工业自动化领域嵌入式开发者与协议栈学习者的HART主设备协议栈开源实现,聚焦于HART(Highway Addressable Remote Transducer)通信标准在软件层面的落地,解决智能仪表系统中主控端(如P…

2026/10/15 20:26:00 阅读更多 →
BMN时序动作定位原理:边界匹配网络的矩阵建模与工业落地

BMN时序动作定位原理:边界匹配网络的矩阵建模与工业落地

1. 项目概述:为什么BMN不是又一个“边界检测”模型,而是时序动作定位的范式跃迁BMN(Boundary Matching Network)这个标题乍看平平无奇,像极了CV领域里那些堆叠卷积、加个注意力就起名“XX-Net”的常规操作。但如果你真…

2026/10/15 20:26:00 阅读更多 →
INCA_ProF脚本自动化标定:从执行模型到批量标定与报告生成

INCA_ProF脚本自动化标定:从执行模型到批量标定与报告生成

简介:这份PDF面向汽车电子标定工程师与INCA初学者,系统梳理ProF脚本的编程要点,帮助读者理解如何用脚本定制ECU闪存编程与自动化任务。资源为单文件PDF,压缩包约271KB,内容以图文笔记形式呈现,便于随时查阅…

2026/10/15 20:26:00 阅读更多 →
Advanced Installer 15.2汉化实践:Windows安装包多语言工程化落地

Advanced Installer 15.2汉化实践:Windows安装包多语言工程化落地

简介:Advanced Installer 15.2汉化版是一款面向Windows平台开发者与系统管理员的专业级安装包制作工具,解决传统MSI打包流程复杂、本地化支持弱、跨环境部署兼容性差等痛点,适用于.NET、Java、SQL Server等多技术栈项目的自动化部署场景。资源…

2026/10/15 20:24:58 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战: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/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →