<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>模块化 on Yun 的个人博客</title><link>https://yun7211.github.io/tags/%E6%A8%A1%E5%9D%97%E5%8C%96/</link><description>Recent content in 模块化 on Yun 的个人博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 18 May 2026 10:40:00 +0800</lastBuildDate><atom:link href="https://yun7211.github.io/tags/%E6%A8%A1%E5%9D%97%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><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></channel></rss>