<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>C 语言 on Yun 的个人博客</title><link>https://yun7211.github.io/categories/c-%E8%AF%AD%E8%A8%80/</link><description>Recent content in C 语言 on Yun 的个人博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 18 May 2026 11:40:00 +0800</lastBuildDate><atom:link href="https://yun7211.github.io/categories/c-%E8%AF%AD%E8%A8%80/index.xml" rel="self" type="application/rss+xml"/><item><title>C 代码审查清单</title><link>https://yun7211.github.io/c-language/debug-quality/code-review-checklist/</link><pubDate>Mon, 18 May 2026 11:40:00 +0800</pubDate><guid>https://yun7211.github.io/c-language/debug-quality/code-review-checklist/</guid><description>&lt;h2 id="审查目标"&gt;审查目标
&lt;/h2&gt;&lt;p&gt;C 代码审查不是挑风格问题，而是提前发现运行时成本最高的问题：内存错误、资源泄漏、接口歧义、错误路径遗漏和不可测试的全局状态。&lt;/p&gt;
&lt;h2 id="接口语义"&gt;接口语义
&lt;/h2&gt;&lt;p&gt;先看函数接口是否清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参数能否为 &lt;code&gt;NULL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;返回值如何表达错误&lt;/li&gt;
&lt;li&gt;调用方是否负责释放资源&lt;/li&gt;
&lt;li&gt;输出参数失败时是否有效&lt;/li&gt;
&lt;li&gt;函数是否修改传入缓冲区&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接口语义不清楚时，后续实现再漂亮也容易出问题。&lt;/p&gt;
&lt;h2 id="内存和边界"&gt;内存和边界
&lt;/h2&gt;&lt;p&gt;重点检查长度、容量和生命周期：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串空间是否包含 &lt;code&gt;\0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memcpy&lt;/code&gt;、&lt;code&gt;snprintf&lt;/code&gt;、数组下标是否有边界&lt;/li&gt;
&lt;li&gt;释放路径是否覆盖所有失败分支&lt;/li&gt;
&lt;li&gt;指针释放后是否还会被访问&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="错误路径"&gt;错误路径
&lt;/h2&gt;&lt;p&gt;C 项目里很多 bug 只在异常路径出现。审查时要刻意走失败分支：申请失败、文件打开失败、设备不存在、协议字段不合法、部分初始化后返回错误。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结
&lt;/h2&gt;&lt;p&gt;好的代码审查清单能把经验固化下来。每次发现新问题，都应该把它变成下一次能提前检查的条目。&lt;/p&gt;</description></item><item><title>C 内存问题排查清单</title><link>https://yun7211.github.io/c-language/debug-quality/debugging-memory-bugs/</link><pubDate>Mon, 18 May 2026 10:50:00 +0800</pubDate><guid>https://yun7211.github.io/c-language/debug-quality/debugging-memory-bugs/</guid><description>&lt;h2 id="常见现象"&gt;常见现象
&lt;/h2&gt;&lt;p&gt;C 内存问题经常表现得很绕：崩溃点不一定是出错点，今天能复现的问题明天可能消失，打开日志后问题也可能变化。&lt;/p&gt;
&lt;p&gt;常见类型包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数组或缓冲区越界&lt;/li&gt;
&lt;li&gt;使用已经释放的指针&lt;/li&gt;
&lt;li&gt;重复释放&lt;/li&gt;
&lt;li&gt;未初始化读取&lt;/li&gt;
&lt;li&gt;资源泄漏&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="先缩小范围"&gt;先缩小范围
&lt;/h2&gt;&lt;p&gt;不要一开始就盯着崩溃现场。先确认最近改动、输入数据规模、线程数量、编译优化级别和是否只在特定平台出现。&lt;/p&gt;
&lt;p&gt;如果问题能稳定复现，优先构造最小复现用例。&lt;/p&gt;
&lt;h2 id="检查边界"&gt;检查边界
&lt;/h2&gt;&lt;p&gt;内存越界通常和长度计算有关。重点检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓冲区大小是否包含结尾 &lt;code&gt;\0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memcpy&lt;/code&gt; 长度是否来自可信来源&lt;/li&gt;
&lt;li&gt;循环边界是否使用 &lt;code&gt;&amp;lt;&lt;/code&gt; 而不是误用 &lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;结构体长度和协议字段是否一致&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="检查生命周期"&gt;检查生命周期
&lt;/h2&gt;&lt;p&gt;悬空指针来自对象生命周期结束后继续访问。释放后可以把指针置为 &lt;code&gt;NULL&lt;/code&gt;，但这只能减少部分误用，不能替代清晰的所有权设计。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结
&lt;/h2&gt;&lt;p&gt;排查内存问题要有耐心，也要有方法。先稳定复现，再缩小范围，最后回到边界、生命周期和错误路径。&lt;/p&gt;</description></item><item><title>C 模块接口设计的几个原则</title><link>https://yun7211.github.io/c-language/module-interface/module-interface-design/</link><pubDate>Mon, 18 May 2026 10:40:00 +0800</pubDate><guid>https://yun7211.github.io/c-language/module-interface/module-interface-design/</guid><description>&lt;h2 id="模块边界"&gt;模块边界
&lt;/h2&gt;&lt;p&gt;C 项目变大以后，真正难维护的往往不是语法，而是模块边界。好的模块接口应该让调用方知道能做什么，不需要知道内部怎么做。&lt;/p&gt;
&lt;p&gt;常见做法是把结构体定义隐藏在 &lt;code&gt;.c&lt;/code&gt; 文件里，只在头文件暴露不透明类型。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;logger_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;logger_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;logger_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;调用方不能直接访问内部字段，模块就有空间调整实现。&lt;/p&gt;
&lt;h2 id="资源成对管理"&gt;资源成对管理
&lt;/h2&gt;&lt;p&gt;创建和销毁函数要成对出现，命名保持一致。这样调用方一眼能看出资源生命周期。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;init/deinit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;create/destroy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;open/close
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;alloc/free
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不要让调用方猜一个对象应该用 &lt;code&gt;free&lt;/code&gt;、&lt;code&gt;close&lt;/code&gt; 还是自定义函数释放。&lt;/p&gt;
&lt;h2 id="错误处理"&gt;错误处理
&lt;/h2&gt;&lt;p&gt;C 语言没有异常，接口必须明确错误返回。常见方式是返回 &lt;code&gt;0&lt;/code&gt; 表示成功，负数或非零表示错误。&lt;/p&gt;
&lt;p&gt;更重要的是：失败时资源状态要可预测。函数要么完全成功，要么保持原状态，不能留下半初始化对象。&lt;/p&gt;
&lt;h2 id="减少全局状态"&gt;减少全局状态
&lt;/h2&gt;&lt;p&gt;全局变量会让测试、并发和复用都变困难。能通过上下文对象传递的状态，就尽量放进上下文对象。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结
&lt;/h2&gt;&lt;p&gt;C 模块接口的目标是降低调用方心智负担。接口越稳定、语义越明确，后续维护成本越低。&lt;/p&gt;</description></item><item><title>C 语言指针与所有权笔记</title><link>https://yun7211.github.io/c-language/pointer-memory/pointer-and-ownership/</link><pubDate>Mon, 18 May 2026 10:30:00 +0800</pubDate><guid>https://yun7211.github.io/c-language/pointer-memory/pointer-and-ownership/</guid><description>&lt;h2 id="指针问题的本质"&gt;指针问题的本质
&lt;/h2&gt;&lt;p&gt;C 语言不会替我们记录对象归谁所有、什么时候释放、还能不能访问。很多指针 bug 表面是野指针、重复释放或内存泄漏，背后都是所有权边界不清楚。&lt;/p&gt;
&lt;p&gt;写 C 代码时，可以主动给指针加上三类语义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;拥有：负责释放资源&lt;/li&gt;
&lt;li&gt;借用：只临时使用，不释放&lt;/li&gt;
&lt;li&gt;输出：函数负责写入结果&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="拥有型指针"&gt;拥有型指针
&lt;/h2&gt;&lt;p&gt;拥有型指针通常来自 &lt;code&gt;malloc&lt;/code&gt;、资源打开函数或创建函数。谁拥有它，谁就负责释放。&lt;/p&gt;
&lt;p&gt;命名上可以用 &lt;code&gt;create/free&lt;/code&gt;、&lt;code&gt;open/close&lt;/code&gt; 成对出现：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;buffer_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;buffer_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种接口比让调用者猜释放方式更稳。&lt;/p&gt;
&lt;h2 id="借用型指针"&gt;借用型指针
&lt;/h2&gt;&lt;p&gt;借用型指针不应该被释放，也不应该长期保存到超过对象生命周期的地方。&lt;/p&gt;
&lt;p&gt;函数参数里常见的 &lt;code&gt;const char *name&lt;/code&gt; 多数是借用。加上 &lt;code&gt;const&lt;/code&gt; 能明确函数不会修改传入内容。&lt;/p&gt;
&lt;h2 id="输出参数"&gt;输出参数
&lt;/h2&gt;&lt;p&gt;C 语言里常用输出参数返回多个结果。输出参数要说明是否允许为 &lt;code&gt;NULL&lt;/code&gt;，以及失败时是否会被修改。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sensor_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;sensor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个接口至少需要约定：&lt;code&gt;dev&lt;/code&gt; 和 &lt;code&gt;value&lt;/code&gt; 是否能为空，返回非零时 &lt;code&gt;value&lt;/code&gt; 是否有效。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结
&lt;/h2&gt;&lt;p&gt;指针本身只是地址。真正决定代码可靠性的，是所有权、生命周期和错误路径是否写清楚。&lt;/p&gt;</description></item></channel></rss>