C语言基本功:从指针数组、内存管理到字符串陷阱的避坑指南
很多初学者觉得 C 语言语法简单变量、循环、函数、指针几天就能把所有语法糖都过一遍。但真正上手写项目的时候被各种“琐碎”的细节反复折磨——比如为什么a[i]和i[a]等价int *p[3]和int (*p)[3]到底哪个是指针数组哪个是数组指针sizeof在指针和数组上为什么表现完全不一样这些问题的答案恰恰就是 C 语言的基本功底。这篇总结不是教科书式的语法罗列而是我从初学 C 到后来用 C 写解析器、写网络程序的过程中踩坑踩出来的琐碎经验合集。适合刚学完 C 语法、正在做课程设计或小工具的你也适合那些“学过但总感觉不扎实”的开发者。我会把每一个看似零散的细节都讲清楚原理并配上代码场景确保你能直接拿去用。1. 先聊聊为什么“琐碎”决定了你的 C 水平1.1 语法五分钟踩坑五小时C 语言的语法确实少关键字一共就三十来个循环就三种函数声明也就那么回事。但实际写起来你会发现真正的难点根本不在语法规则本身而在语法规则背后的机器行为。我给你举个最常见的场景写一个字符串拷贝函数。void copy_string(char *dest, char *src) { while (*src ! \0) { *dest *src; dest; src; } *dest \0; }这段代码看起来人畜无害但里面藏着几个非常关键的基本功问题第一src和dest如果指向同一块内存会怎样第二dest的空间不够怎么办第三调用者传入空指针怎么办第四拷贝过程中如果src本身就是个空字符串*dest \0这句会不会写入不该写的位置这些问题在语法书里没有标准答案但在实际工程里每一个都是事故隐患。所以我一直认为所谓“C 语言功底”本质上就是你面对这些琐碎细节时脑子里能瞬间浮现出“这里会出什么问题”的能力。1.2 基本功底的构成框架总结下来我认为初涉 C 语言阶段必须夯实的基本功就四大块类型系统、指针与数组、内存管理、字符串与输入输出。这四块是缠在一起的单独学任何一块都没法真正掌握。举个例子你学指针的时候如果不理解数组退化成指针的规则那后面看main函数的char *argv[]参数就会很吃力你学内存管理的时候如果不知道栈和堆的区别就搞不懂为什么局部变量不能返回你学字符串的时候如果不清楚scanf的行为就会一直被缓冲区残留问题折磨。这篇文章的章节就是按这个框架展开的。每一节我都会先抛出最常见的错误场景再解释底层的机器原理最后给出对应的正确写法和避坑建议。2. 类型与作用域一堆容易被忽略的“小规则”2.1 整型转来转去结果转出了 bug初学者很少意识到 C 语言存在整型提升和隐式转换的规则。看下面这段代码#include stdio.h int main() { char c 200; unsigned char u 200; printf(char 的值: %d\n, c); printf(unsigned char 的值: %d\n, u); return 0; }在大多数平台上char是有符号的所以c打印出来是 -56而u是 200。同一个二进制位0xC8存放在有符号和无符号类型里解释出来的数值完全不同。这还不算什么真正的坑在于混合运算时int a -1; unsigned int b 1; if (a b) { printf(a 小于 b\n); } else { printf(a 大于等于 b\n); }很多新手理所当然地认为 -1 小于 1但实际输出是“a 大于等于 b”。原因就是当有符号和无符号类型混合比较时C 语言会把有符号数隐式转换成无符号数来比较。-1 转换成无符号数后变成了 4294967295自然大于 1。我在实际排查中就遇到过这种问题某个判断条件len count里面len是intcount是size_t结果数组长度超过 2GB 时逻辑反了。之后我一直坚持一个习惯比较和运算时尽量让两侧类型一致要么统一用size_t要么统一用int不要指望隐式转换给你带来惊喜。2.2 static 的三个面孔static关键字可能是初学阶段最容易混淆的概念因为它有完全不同的三种用途。局部变量加static变量生命周期变成整个程序期间但作用域不变全局函数加static函数变得只能在本文件内访问相当于把函数“私有化”了全局变量加static同理只能在本文件访问。局部变量的例子最直观#include stdio.h void counter() { static int count 0; count; printf(第 %d 次调用\n, count); } int main() { counter(); counter(); counter(); return 0; }输出是第 1 次、第 2 次、第 3 次。这个场景很多教材讲递归的时候会提到但很少有人强调背后的原因是static int count 0的初始化只在程序启动时执行一次函数每次进入不会重新赋值为 0变量存放在静态存储区而不是栈上。我个人的使用经验是static修饰函数和全局变量这个特性非常有用写模块化代码时用它把内部实现和外部接口隔开防止符号冲突。但局部static要谨慎用因为它会让函数变成有状态的多线程环境下容易出问题。2.3 作用域遮蔽编译器的沉默让人防不胜防看这段代码#include stdio.h int a 10; int main() { int a 20; printf(%d\n, a); return 0; }输出是 20因为局部变量遮蔽了全局变量。这个规则本身很简单但如果项目中变量命名不规范就会出现离奇的 bug。我在一次帮同学排查问题时就遇到过一个全局变量名和局部变量名完全一致局部作用域里修改了值导致另一处函数拿到的全局变量数据异常排查了整整一个下午。建议从入门就养成两个习惯第一全局变量加前缀区分比如g_socket_fd第二尽量避免局部变量和全局变量同名。编译器一般对这种遮蔽只给 warning 或不提示别指望它帮你抓住。3. 指针与数组初学者的第一道分水岭3.1 数组名到底是不是指针几乎所有初学者都在这里被绕晕过。严格来说数组名不是指针它是一个指向数组首元素的常量地址值。你用sizeof就能验证对数组取sizeof得到的是整个数组占用的字节数对指向数组首元素的指针取sizeof在 64 位平台上得到的是 8。#include stdio.h int main() { int arr[10]; int *p arr; printf(sizeof(arr) %zu\n, sizeof(arr)); // 40 printf(sizeof(p) %zu\n, sizeof(p)); // 8 return 0; }数组名在绝大多数表达式中会“退化”成指向首元素的指针但有两个例外一是在sizeof运算符中二是在取地址运算符arr中。arr的类型是int (*)[10]也就是指向整个数组的指针和int *不是同一种类型。虽然它们指向的内存地址相同但指针加一时跳过的字节数完全不同。这个差异直接用代码验证更直观#include stdio.h int main() { int arr[10] {0}; int *p arr; int (*p2)[10] arr; printf(p 1 偏移: %ld 字节\n, (long)(p 1) - (long)p); // 4 printf(p2 1 偏移: %ld 字节\n, (long)(p2 1) - (long)p2); // 40 return 0; }这解释了为什么二维数组作为函数参数时会出现各种奇怪的编译错误本质问题就是数组指针和指针数组搞混了。3.2 指针数组与数组指针的区分方法int *p[3]和int (*p)[3]的读法顺序是很多人的噩梦。我提供一个拆分方法[]的优先级高于*。int *p[3]先看p[3]说明 p 是一个有 3 个元素的数组数组元素类型是int *所以它是指针数组——一个存了 3 个指针的数组。int (*p)[3]括号让*先和p结合说明 p 先是一个指针再去指向一个int[3]类型的数组所以它是数组指针——一个指向数组的指针。实际写代码时二者用途完全不同。指针数组常用于处理多个字符串比如存储命令行参数数组指针则常用来指向二维数组的行。我建议你在初学阶段就用typedef把复杂类型简化掉比如typedef int (*IntArrayPtr)[3];这样后面写代码时心智负担会小很多。这不算偷懒工程实践中同样会这么做因为人脑对复杂声明的解析能力是有限的。3.3 函数参数传数组你以为传了数组其实传了指针这是初学者最常犯的错误之一。看这个函数void modify(int arr[]) { arr[0] 999; }你以为函数通过“值传递”的方式拷贝了整个数组所以在函数里修改不会影响原数组。但实际上函数参数里的int arr[]会被编译器自动改写成int *arr所以函数里修改的就是原数组内存。我们写个完整例子验证#include stdio.h void modify(int arr[]) { printf(在函数里 sizeof(arr) %zu\n, sizeof(arr)); arr[0] 999; } int main() { int data[5] {1, 2, 3, 4, 5}; printf(在 main 里 sizeof(data) %zu\n, sizeof(data)); modify(data); printf(data[0] 被改成了 %d\n, data[0]); return 0; }函数里的sizeof(arr)输出的不是 20而是 864 位系统这就是函数参数退化成指针的铁证。所以当你需要在函数内部知道数组长度时不能依赖sizeof(arr) / sizeof(arr[0])这一招必须把数组长度一并传入。我见过不少初学阶段的同学在函数里计算数组长度得到错误结果后一脸迷茫。记住一句话C 语言函数参数传数组的本质是传地址长度要单独传。3.4 野指针、空指针与悬垂指针这三类指针问题在初学阶段出镜率极高。空指针比较好理解就是指向NULL的指针通常表示“不指向任何有效对象”使用前判空就能避免大部分问题。野指针更隐蔽声明后未初始化的局部指针就属于野指针它的值是随机的。悬垂指针则是指向已经释放内存的指针比如int *bad() { int x 10; return x; // x 是局部变量函数返回后栈内存被回收 }这个函数的返回值就是典型的悬垂指针。虽然编译器通常会给出 warning但新手往往忽略它。运行时不一定会立刻崩溃因为那块栈内存还没被覆盖但下一次任何函数调用都可能复用它程序行为变得完全不可预测。我的建议是遵循几条硬性纪律指针声明时就初始化free之后立刻把指针赋值为NULL函数不要返回局部变量的地址如果确实要返回指针要么用static局部变量要么用malloc分配堆内存。4. 内存管理栈也好堆也好都是基本功4.1 程序到底住在什么样的内存里C 程序运行时的内存布局初学阶段至少要清楚四个区域。栈区存放局部变量和函数调用的返回地址由编译器自动分配和释放空间有限且紧凑默认一般只有几兆。堆区存放动态分配的内存由程序员用malloc申请、用free释放空间较大但需要手动管理。全局区存放全局变量、static变量、字符串常量。代码区存放机器指令只读。区分栈和堆的意义在于你会明白为什么局部数组不能开太大为什么函数不能返回局部变量指针为什么频繁malloc会带来性能问题。一个新手常见问题是试图在函数里开一个超大数组void demo() { double data[1000000]; // 约 8MB可能直接爆栈 }这不是语法错误但运行时很可能直接段错误或者程序异常退出。数据量大时应该使用malloc分配堆内存或者用static把数据放到全局区。这个选择本质上是对内存布局有没有基本概念的问题。4.2 malloc 和 free 的配对纪律malloc用少了会内存泄漏用多了会重复释放导致崩溃用了没释放完项目运行时内存持续上涨。我给学生项目做代码审查时最常见的问题就是申请了内存但在某个分支里忘了释放。这里有三个实测有效的好习惯。第一写代码时立即配对申请内存的那行写完马上写对应的释放代码再往中间填逻辑。第二释放后立刻置 NULLfree(ptr); ptr NULL;这样即使后续不小心再次free(ptr)free(NULL)是安全的不会崩溃。第三malloc后一定要判断返回值是否为 NULL虽然多数情况下申请普通大小内存不会失败但这是好习惯至少在嵌入式或内存受限场景下能帮你抓住问题。4.3 一个内存泄漏的排查思路如果程序内存持续增长可以用一个简单的方法定位大方向在内存增长的区间前后打印已分配的内存总量。很多项目中泄漏往往集中在循环里。while (1) { char *buf (char *)malloc(1024); // 处理业务逻辑 // 忘了 free(buf) }只要循环不停内存就会被一点点耗尽。排查手段上推荐你尽早熟悉 Valgrind 之类的内存检测工具它能直接告诉你哪一行申请的哪一块内存没有被释放。这不是复杂高深的技术但动手实践过一遍之后你会对内存管理有远比看书深刻的理解。5. 字符串、输入输出琐碎事里全是坑5.1 字符串的本质是“字符数组 结束标志”hello这个字符串常量在内存里是h e l l o \0六个字节。所以当你声明char str[5] hello时编译会报错或者警告因为没地方放\0。而char str[6] hello才能正确存储。这个琐碎的点带来的连锁反应是几乎所有字符串函数都靠\0判断结束位置。strlen(hello)返回 5但实际占用了 6 字节。同理strcpy会一直拷贝到\0为止如果源字符串没有结束符它就会一路拷贝到内存野区去。写程序时我建议你总是给字符数组预留一个字节的富余也就是“实际内容长度 1”。5.2 scanf 的缓冲区残留问题scanf配合%d读数字时不会清掉输入流里的换行符。当同一程序后面再用getchar或者fgets时就会读到残留的换行符导致逻辑直接跳过。我有一个自己经常用的解法int num; scanf(%d, num); // 清掉缓冲区里的残留字符 int c; while ((c getchar()) ! \n c ! EOF) { // 丢弃 }或者直接用fgets读整行再用sscanf解析char line[128]; fgets(line, sizeof(line), stdin); sscanf(line, %d, num);这个做法是初学阶段最值得养成的习惯之一它能规避大部分输入相关的 bug。真实工程项目中命令行交互程序几乎不会直接用scanf(%s)去读字符串因为%s遇到空格就停根本没法处理带空格的输入。我实测过数次之后对输入处理总结了两个原则一是整行读取再解析二是每次解析都检查返回值判断是否真的读到了期望的数据。5.3 字符串函数的安全版本strcpy、strcat、sprintf这三兄弟是初学阶段最容易引发问题的函数。strcpy不检查目标空间是否足够strcat也没有长度限制sprintf同样没有边界保护。用它们时一旦源字符串长度超出了目标缓冲区的容量就会造成缓冲区溢出不仅可能崩溃还可能产生更严重的安全漏洞。虽然有些环境提供了strcpy_s、sprintf_s这类安全版本但跨平台兼容性不佳。更通用的做法是使用带长度参数的函数strncpy、strncat、snprintf。其中snprintf是我个人使用频率最高的一个因为它在格式化输出时能够明确限制写入的最大字节数char buf[64]; snprintf(buf, sizeof(buf), name: %s, age: %d, name, age);这样即使name超长也不会溢出buf。snprintf还会告诉我们理论上应该写入多少字节方便判断是否被截断。初学阶段把这几个函数的用法转换清楚后面写网络通信、日志系统时的安全性会好很多。6. 常见问题排查与经验快查表6.1 段错误到底是谁的锅段错误可能是 C 语言初学阶段遇到最多的运行时错误。有几个最经典的触发点访问了空指针数组越界尤其是写越界对只读字符串直接赋值函数返回了局部变量的地址重复释放同一块内存。排查的手段主要靠三招。第一招编译时加调试信息运行时用调试器段错误时调试器会直接告诉你崩在哪一行。第二招在怀疑的区间前后打印调试日志逐步缩小范围。第三招用内存检测工具定位非法内存访问的位置。我特别想强调的一点是很多段错误不是立刻崩溃的可能过了很久才在某次调用中爆发。这是因为你写进了别的变量所在的内存破坏了对面的数据但当前函数没有感知。这种 bug 最难排查唯一的出路是规范代码、尽量用安全函数、多做边界检查。6.2 编译警告不是用来无视的很多初学者看到 warning 就当没看见这是大忌。编译器给出 warning 往往意味着它在告诉你“按 C 语言标准我允许你这么做但它几乎肯定不是你想要的。”最典型的就是“未使用的变量”“有符号和无符号比较”“函数声明与定义不匹配”。我建议大家从第一天起就把编译警告视为错误来处理在编译命令里加上相应选项比如用 GCC 时开启gcc -Wall -Wextra -o demo demo.c这样之前说的很多琐碎问题编译器都会预先给你提示。用好编译器这个免费的老师远比自己闷头调试高效。6.3 混用 char 数组和指针导致修改只读内存最后一个常见问题也是很多同学栽过的地方char *str hello; str[0] H;字符串常量存放在只读的全局区通过char *指向它然后修改后果未定义大多数平台直接崩溃。而写成char str[] hello时字符串会拷贝到可修改的栈数组里随后修改就合法了。这个差异新手很容易忽略因为两行代码从“用”的角度看长得差不多。我的记忆口诀是指针指向常量数组拥有拷贝。只要是想修改字符串内容就用数组方式定义如果只是当作只读参数传给函数用字符串常量本身没问题因为很多标准函数参数都是const char *。7. 琐碎经验如何沉淀成真正的功底写了这么多最后分享一些我自己的学习体会。刚学 C 的时候我也觉得这些细节很烦为什么不能像 Python 那样直接做字符串拼接、自动管理内存。但后来我意识到C 的这些“琐碎”恰恰是它强大的来源。正因为你能控制内存、控制指针、控制字节你才可能写出接近机器效率的代码也才可能在阅读系统代码、编写嵌入式程序或者实现底层协议时游刃有余。我建议你把本文提到的几个典型代码片段自己动手敲一遍亲眼看看输出结果。改一改参数观察行为变化。比如把int a -1; unsigned int b 1;这个例子改成long试试把char *str hello改成char str[] hello再执行一次修改操作这些第一手的观察比背十遍规则都管用。最后再说一个我实测有效的小习惯手边常开一个编辑器文件把所有踩过的坑和运行输出的怪现象记下来每条附上代码。积累到几十条之后你会发现自己面对陌生代码时预测 bug 的直觉强了一大截。C 语言的基本功就是从这些琐碎的经验里一点点垒起来的。

相关新闻

AnyPS5:用闲置设备打造PS5串流终端,实现远程游戏自由

AnyPS5:用闲置设备打造PS5串流终端,实现远程游戏自由

1. 项目缘起与核心定位AnyPS5 这个名字第一次出现在我视野里的时候,我正蹲在一堆旧设备中间翻找能用的零件。当时手头有一台闲置的迷你主机,性能不算强,但胜在功耗低、接口全,扔了可惜,留着又不知道干什么。后来跟几个…

2026/10/14 6:52:31 阅读更多 →
AIGC创作训练营:从提示词工程到AI内容变现

AIGC创作训练营:从提示词工程到AI内容变现

1. 从“看见别人AI出图”到“自己动手变现”,训练营到底解决什么问题这两年AIGC的热度一直没降过,但真正让人焦虑的不是技术本身,而是“别人已经用AI批量产出内容了,我还在当观众”。朋友圈里有人用AI画头像接单,短视频…

2026/10/14 6:52:31 阅读更多 →
PS5远程串流实战指南:从NAT配置到低延迟优化

PS5远程串流实战指南:从NAT配置到低延迟优化

要是有人在你买PS5的时候告诉你,这台主机不必每次都坐在客厅电视前玩,你多半会觉得那是云游戏广告。但当我抱着笔记本电脑躺到床上,远程串流进游戏,顺手把主机丢在三楼房间继续跑的时候,我才认真把这件事当成一个项目来…

2026/10/14 6:52:31 阅读更多 →

最新新闻

多模态AI搜索重构内容生态:企业信源适配的落地路径

多模态AI搜索重构内容生态:企业信源适配的落地路径

一、多模态AI搜索的四个常见问题多模态AI搜索正在改变信息分发的基本逻辑。用户不再只输入关键词,而是用自然语言提问,甚至上传图片、语音,期待AI直接给出整合后的答案。这带来四个行业普遍困惑。第一,企业内容发了不少&#xff0…

2026/10/14 14:23:36 阅读更多 →
Python scipy.stats.geom 几何分布实战:从原理到避坑指南

Python scipy.stats.geom 几何分布实战:从原理到避坑指南

1. 从一个真实场景说起:为什么几何分布值得单独拎出来讲做数据分析和概率建模的朋友,大概率都遇到过这类问题:用户第一次点击广告之前要刷多少次页面?一台设备从启动到首次故障能撑多少个运行周期?某个抽卡游戏里&…

2026/10/14 14:23:36 阅读更多 →
ARM64服务器安装MySQL 5.7.32二进制包与调优指南

ARM64服务器安装MySQL 5.7.32二进制包与调优指南

简介:面向需要在ARM64(aarch64)Linux环境中部署MySQL的开发者与运维人员,这是MySQL 5.7.32预编译二进制包,依赖glibc 2.28,适配树莓派4及各类ARM服务器,免去源码编译的繁琐流程。包内共14882个文…

2026/10/14 14:23:36 阅读更多 →
SSM框架构建微博系统:Java Web开发实战解析

SSM框架构建微博系统:Java Web开发实战解析

1. 项目概述这个基于SSM框架的微博系统是一个典型的Java Web应用开发案例,我最近刚用它作为教学项目带学生完成了一轮实战开发。作为从2010年就开始接触SSM框架的老开发,我发现这个技术组合至今仍是企业级Java开发的中坚力量,特别适合快速构建…

2026/10/14 14:23:36 阅读更多 →
寄生虫虫卵检测数据集:基于YOLOv8的目标检测训练全攻略

寄生虫虫卵检测数据集:基于YOLOv8的目标检测训练全攻略

做了好几年医学影像相关的目标检测项目,一直有个绕不开的痛点:寄生虫虫卵检测。这东西在临床检验里需求量很大,特别是在基层医疗场景,检验科医师每天要对着显微镜看几百张涂片,全靠肉眼找特定虫卵,费眼又费…

2026/10/14 14:23:36 阅读更多 →
手游脚本开发实战:图像识别与状态机架构设计

手游脚本开发实战:图像识别与状态机架构设计

1. 从零到一:手游脚本开发的核心逻辑与整体设计1.1 为什么选择脚本而不是直接改内存很多刚接触这个领域的朋友,第一反应往往是去逆向游戏客户端、直接修改内存数据。我早期也走过这条路,踩过的坑足够写一本小册子。直接改内存确实见效快&…

2026/10/14 14:22:35 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →