<?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 映屿</title>
    <link>https://blog.verdant.ee/tags/c%E8%AF%AD%E8%A8%80/</link>
    <description>Recent content in C语言 on 映屿</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    
      <managingEditor>i@glowisle.me (五葉地錦)</managingEditor>
    
    
      <webMaster>i@glowisle.me (五葉地錦)</webMaster>
    
    
    
    <lastBuildDate>Sun, 08 Feb 2026 11:33:19 +0800</lastBuildDate>
    
    
    <atom:link href="http://blog.verdant.ee/tags/c%E8%AF%AD%E8%A8%80/atom.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>回声周刊 Vol.8：欧盟初步认定TikTok成瘾性违法</title>
      <link>https://blog.verdant.ee/posts/weekly/8/</link>
      <pubDate>Sun, 08 Feb 2026 11:33:19 +0800</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/weekly/8/</guid>
      <description>&lt;p&gt;这里是回声周刊，分享我这周读过的文章书籍、近期发生的大事小事。每周日更新。如果你对这个周刊感兴趣，可以 &lt;a href=&#34;https://blog.verdant.ee/categories/%E5%9B%9E%E5%A3%B0%E5%91%A8%E5%88%8A/atom.xml&#34;&gt;订阅周刊&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;-声波&#34;&gt;〰︎ 声波&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;music-card&#34;&gt;&#xA;    &#xA;    &lt;div class=&#34;music-info&#34;&gt;&#xA;        &lt;img class=&#34;music-cover&#34;&#xA;            src=&#34;https://images.glowisle.me/ab67616d0000b27302bbe49ea09e5b6c02ee34c0.jpeg&#34; loading=&#34;lazy&#34; alt=&#34;Cover&#34; /&gt;&#xA;        &lt;div class=&#34;music-text&#34;&gt;&#xA;            &lt;h4 class=&#34;music-title&#34;&gt;とかげ3号&lt;/h4&gt;&#xA;            &lt;p class=&#34;music-artist&#34;&gt;GO!GO!7188&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;music-links&#34;&gt;&#xA;        &#xA;&#xA;        &#xA;&#xA;        &#xA;        &lt;a href=&#34;https://open.spotify.com/album/5vOopPWCJOE6tmHYolsFkb&#34; class=&#34;music-btn&#34; target=&#34;_blank&#34; title=&#34;在 Spotify 中打开&#34; data-tooltip&gt;&#xA;            &lt;img src=&#34;https://blog.verdant.ee/images/spotify.png&#34; loading=&#34;lazy&#34; class=&#34;music-icon&#34; alt=&#34;Spotify&#34; /&gt;&#xA;            &lt;span class=&#34;music-btn-text&#34;&gt;Spotify&lt;/span&gt;&#xA;        &lt;/a&gt;&#xA;        &#xA;    &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;とかげ3号~ とかげ3号~&lt;/p&gt;&#xA;&lt;p&gt;しっぽの切れた 八虫類&lt;/p&gt;&#xA;&lt;p&gt;とかげ3号 とかげ3号&lt;/p&gt;&#xA;&lt;p&gt;お腹青光り 八虫類&lt;/p&gt;&#xA;&lt;p&gt;キラキラと光る あの物体は何だ?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;已经成为这段时间的必需品，停止播放大脑就会&lt;em&gt;とかげ3号~ とかげ3号&lt;/em&gt;个不停。&lt;/p&gt;&#xA;&lt;h2 id=&#34;-声源&#34;&gt;🕮 声源&lt;/h2&gt;&#xA;&lt;h3 id=&#34;欧盟初步认定tiktok成瘾性违法英文&#34;&gt;欧盟初步认定TikTok成瘾性违法（英文）&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://ec.europa.eu/commission/presscorner/detail/en/ip_26_312&#34;&gt;文章链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;欧盟委员会初步认定TikTok因其令人上瘾的设计违反了《数字服务法》。这包括无限滚动、自动播放、推送通知和高度个性化的推荐系统等功能。&lt;/p&gt;&#xA;&lt;p&gt;Twitter同理，他们对青少年的访问进行了限制。但我请问，现在广为人知的娱乐平台哪一个不是这样？&lt;/p&gt;&#xA;&lt;h3 id=&#34;检查伴侣的手机中国亲密关系中的一个恶习&#34;&gt;检查伴侣的手机：中国亲密关系中的一个恶习&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://stephenleng.com/cn/jiancha-shouji-qinglv/&#34;&gt;文章链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;文章探讨了&lt;strong&gt;中国人&lt;/strong&gt;在亲密关系中对隐私权的让渡问题，如查看手机，并列出了&lt;strong&gt;美国人&lt;/strong&gt;的数据作为对比，深入到信任问题，并对查看手机这一行为持明显批判态度。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在中国，有些人常常逼迫自己伴侣交出手机，假若不从，就以分手为威胁。这种让人惊掉下巴的寡廉鲜耻已经到了人神共愤的程度，因为这表明这些人完全不尊重对方的隐私权，丝毫没有权利意识。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;虽然文章的内容确实有道理，但作者几乎是先假设了一个「中国人在亲密关系中隐私关系欠佳、存在信任问题」的结论，寻找论据进行论证的。以及，「中国人」在文章中被描述为一个静态的整体。&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么我用c语言制作游戏英文&#34;&gt;为什么我用C语言制作游戏（英文）&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://jonathanwhiting.com/writing/blog/games_in_c/&#34;&gt;文章链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;作者将不同编程语言对比，结合自己的需求和经验，最终认为C语言是最适合自己的语言。&lt;/p&gt;&#xA;&lt;p&gt;文中提到了作者想要的编程语言特性：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I want to produce less bugs, so I want strict typing, strong warning messages and static code analysis.&lt;/p&gt;&#xA;&lt;p&gt;我希望Bug更少，所以我想要&lt;strong&gt;强类型&lt;/strong&gt;、&lt;strong&gt;强大的警告信息&lt;/strong&gt;、&lt;strong&gt;静态代码分析&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;del&gt;乍一听，如果不单纯考虑游戏开发的话，他应该会喜欢Rust的。&lt;/del&gt;&#xA;文章中的不少观点都能引发我的共鸣，比如对C++的厌恶和对Golang的喜爱，但显然Go因为游戏相关的库太少，不适用于游戏开发。&lt;/p&gt;&#xA;&lt;p&gt;我相信在Linux环境下工作的程序员很难讨厌C。我个人对于C的喜爱，大部分来自GNU/Linux, Emacs和Vim等等一大堆使用C开发的日常使用的工具，除此之外，还有这个语言本身的简洁性：没有过多的语法糖和抽象，完全清楚代码在做什么，几乎没有隐藏的开销。如此简单却接近底层的机制带来了巨大的灵活性。&lt;/p&gt;&#xA;&lt;h3 id=&#34;互联网档案馆发布插件解决40的互联网死链问题&#34;&gt;互联网档案馆发布插件，解决40%的互联网死链问题&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://github.com/a8cteam51/internet-archive-wayback-machine-link-fixer&#34;&gt;项目链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://techcrunch.com/2026/02/04/the-wayback-machine-debuts-a-new-plugin-designed-to-fix-the-internets-broken-links-problem/&#34;&gt;来源&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;wemd&#34;&gt;WeMD&lt;/h3&gt;&#xA;&lt;p&gt;更优雅的Markdown公众号排版工具。告别复杂工具。Markdown写作，一键复制到公众号。专为公众号创作者设计的本地优先编辑器。支持桌面客户端和网页版在线使用。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://github.com/tenngoxars/WeMD&#34;&gt;项目链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;微信公众号的编辑器如果敢说自己是世界上第二难用的编辑器，没人敢说自己是第一。&lt;/p&gt;&#xA;&lt;h3 id=&#34;superdoc&#34;&gt;SuperDoc&lt;/h3&gt;&#xA;&lt;p&gt;SuperDoc&lt;a href=&#34;https://www.superdoc.dev&#34;&gt;（在线演示）&lt;/a&gt;是一个开源文档编辑器，将Microsoft Word功能带到Web上，具有实时协作，广泛的格式选项和轻松的集成。可自托管Vanilla JS，React，Vue等。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://github.com/superdoc-dev/superdoc&#34;&gt;项目链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;tmux-plugin-manager&#34;&gt;Tmux Plugin Manager&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔗 &lt;a href=&#34;https://github.com/tmux-plugins/tpm&#34;&gt;项目链接&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;安装和加载tmux插件。&lt;/p&gt;&#xA;&lt;h2 id=&#34;-所在&#34;&gt;♇ 所在&lt;/h2&gt;&#xA;&lt;h3 id=&#34;自建fedi实例&#34;&gt;自建Fedi实例&lt;/h3&gt;&#xA;&lt;p&gt;昨天和群友讨论泰拉瑞亚联机方案的时候提到服务器，我随便看了看，发现有一台挺适合的。虽然联机方案的讨论结果是不需要单独的服务器，我也不用干活，但还是拿下了这台机器，搭建了一个akkoma实例。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⁂ &lt;a href=&#34;https://akk.glowisle.me/&#34;&gt;实例地址&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;选择akkoma就是因为它相对轻量，可以减少服务器成本。减少成本是对于我个人来说，让服务活下去并且活得好的方法之一，~因为这个东西就像Spotify一样会每月吃掉我的钱啊！~目前是不开放注册的单人实例，当然这个配置应该不会开放注册的。&lt;/p&gt;&#xA;&lt;p&gt;这个过程踩了不少坑，从服务端到nginx到cloudflare缓存，前端后端几乎全踩了一遍，不过也好，也算是为对服务器维护一窍不通的我积累经验了。其中卡了我最长时间的问题是头像的显示问题。这个问题有好几个阶段。&lt;/p&gt;&#xA;&lt;p&gt;阶段一：上传图片会裂开。是nginx配置问题导致，程序不知道该把图片上传到什么位置，在调整nginx配置文件后解决了。&lt;/p&gt;&#xA;&lt;p&gt;阶段二：可以上传图片，服务器物理上存储了图片，但还是裂，实际url被设置为：&lt;code&gt;https:akk.glowisle.me/media.glowisle.me/xxxx.png/&lt;/code&gt;，这是baseurl没有加&lt;code&gt;https://&lt;/code&gt;导致的。&lt;/p&gt;&#xA;&lt;p&gt;阶段三：上传头像，png格式的图片会变成一张透明图，其他格式全部变成纯黑图。&#xA;这里是浪费时间最长的，一开始我以为图片损坏或者媒体类型不正确，用curl各种测都正常，之后怀疑是图片裁剪后端的问题，因为我的服务器发行版是Debian，我担心软件版本太低，然而禁用了裁剪器后问题依然存在，我开始抓瞎排查调试……&lt;/p&gt;&#xA;&lt;p&gt;我用手机客户端登录账号上传头像，就不会有这个问题。在那个客户端里没有裁剪图片这个步骤，我就更加笃定是裁剪后端的问题了。直到在&lt;a href=&#34;https://akk.glowisle.me/notice/B35Dd1zdF1yNJurewS&#34;&gt;这条帖子&lt;/a&gt;下有网友回复可能是浏览器问题，我才换了一个浏览器来测试，终于能正常上传图片了。没想到问题竟然出在这里！&lt;/p&gt;&#xA;&lt;h3 id=&#34;有点折磨自己的改变&#34;&gt;有点折磨自己的改变！&lt;/h3&gt;&#xA;&lt;p&gt;我最近打破了很多原有的习惯。比如，我重新配置了Emacs，而不是继续使用Doom Emacs。&lt;a href=&#34;https://codeberg.org/verdant/emacs.d&#34;&gt;这里是我的配置文件&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;p&gt;我还把&lt;code&gt;&amp;lt;Escape&amp;gt;&lt;/code&gt;重新映射到了&lt;code&gt;CapsLock&lt;/code&gt;，所以我现在会使用&lt;code&gt;CapsLock&lt;/code&gt;来切换evil的insert和normal模式，确实更近了一点，但小指有些无力，按起来有点难受。&lt;/p&gt;&#xA;&lt;p&gt;最近也在强制自己使用标准指法打字，因为我之前的打字指法错误率太高了。什么？你问我是什么指法，我可以告诉你，那是一坨不可名状的，正在蠕动生长的东西。那是我刚刚接触电脑，二指禅时代的遗留，和大量编程敲字的vim习惯的结合。一般大家打字，右手都是把手指以此放在&lt;code&gt;J&lt;/code&gt; &lt;code&gt;K&lt;/code&gt; &lt;code&gt;L&lt;/code&gt; 和分号键，是的，想也知道，我放在&lt;code&gt;H&lt;/code&gt; &lt;code&gt;J&lt;/code&gt; &lt;code&gt;K&lt;/code&gt; &lt;code&gt;L&lt;/code&gt;……&lt;/p&gt;&#xA;&lt;p&gt;虽然现在打字速度变得更慢，而且错误率也增加了，并且碰到左手小指无名指和中指压力大的单词时十分不舒服，但我相信熟练后收益不会低的，就像学习双拼和Vim一样，总要经过一个难受的磨合期。&lt;/p&gt;&#xA;&lt;p&gt;以上就是本期回声周刊的全部内容，感谢你的阅读，祝你有充满ECHO的一周！👋&lt;/p&gt;</description>
    </item>
    <item>
      <title>剖析千行C语言文本编辑器Kilo的技术细节</title>
      <link>https://blog.verdant.ee/posts/kilo-analysis/</link>
      <pubDate>Sat, 06 Dec 2025 13:26:47 +0800</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/kilo-analysis/</guid>
      <description>&lt;p&gt;今天上午学习了一下&lt;a href=&#34;https://github.com/antirez/kilo&#34;&gt;Kilo&lt;/a&gt;的源代码。我很早以前就对文本编辑器的实现方法感兴趣了。&lt;/p&gt;&#xA;&lt;p&gt;Kilo是一个很简易却不简陋的项目，清晰地展示了如何构建一个终端下的文本编辑器，它的目的不是真正让你学会去开发一个高标准高质量，能投入使用的文本编辑器，而是理解文本编辑器的核心骨架、理解一个看似庞大一团糟的问题的拆解思路。这是一个很好的起点。也过了一把爽玩C语言的瘾（虽然我并没有写几行代码）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;程序分析&#34;&gt;程序分析&lt;/h2&gt;&#xA;&lt;p&gt;整个项目只有一个文件，一千三百行代码。我用了大概一个半小时梳理了程序的执行流程，手画了一个流程图。为了美观，我又用&lt;a href=&#34;https://www.glowisle.me/posts/%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8graphviz%E7%BB%98%E5%88%B6%E7%A8%8B%E5%BA%8F%E6%B5%81%E7%A8%8B%E5%9B%BE/&#34;&gt;Graphviz&lt;/a&gt;绘制了一个电子版：&lt;/p&gt;&#xA;&lt;p&gt;&lt;figure class=&#34;image-caption&#34;&gt;&#xA;    &lt;img src=&#34;https://images.glowisle.me/kilo-graph.png&#34; alt=&#34;程序流程图（大意）&#34;&gt;&#xA;    &lt;figcaption&gt;程序流程图（大意）&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&lt;/p&gt;&#xA;&lt;p&gt;这张图我省略了一些深的函数调用，但也能帮助我大体上掌握这个程序的执行流程。结合这张图与源码，我发现文本编辑器的核心功能——打开、编辑、保存，实现难度并不大，在C语言中容易踩坑的是缓冲区处理、文件读写这种老生常谈的内容。在这个程序中，调用最多、最重磅的部分是&lt;code&gt;initEditor&lt;/code&gt;这个函数，以及后续的高亮处理，尤其是前者在窗口尺寸计算、修改后的做法上花费了大功夫。其实和终端环境的交互才是最麻烦的点，它提供的封装和抽象并不多，有很多需要自己手动调试的地方，繁琐是显著特征。&lt;/p&gt;&#xA;&lt;h3 id=&#34;终端信号处理&#34;&gt;终端信号处理&lt;/h3&gt;&#xA;&lt;p&gt;我发现在终端程序里，需要快捷键的部分都是使用Raw mode和signal相关的函数组合实现的，在理解&lt;code&gt;signal&lt;/code&gt;这个函数和它的有关宏的概念时，耗费了比较长的时间。&lt;/p&gt;&#xA;&lt;p&gt;简单来说，signal用于处理用户在终端发出的信号，比如&lt;code&gt;SIGINT&lt;/code&gt;代表由&lt;code&gt;C-c&lt;/code&gt;产生的中断信号，&lt;code&gt;SIGIGN&lt;/code&gt;代表忽略信号，即接受到这个信号以后什么都不做，关于如何接受信号，就要说起&lt;code&gt;signal()&lt;/code&gt;这个函数。定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; sig, &lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;func)(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;)))(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来非常复杂，说人话就是接受两个参数，第一个参数是int类型的&lt;code&gt;sig&lt;/code&gt;，是信号编号，比如&lt;code&gt;SIGINT&lt;/code&gt;，这是要接收的信号。第二个参数是一个函数指针，接受一个返回void，参数是int类型的信号处理函数，使用第二个函数中的函数对接受到的信号做处理。函数返回原来的信号处理函数（函数指针）。&lt;/p&gt;&#xA;&lt;p&gt;可以用typedef简化理解：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 定义信号处理函数的类型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt;)(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 用 typedef 重写 signal 声明&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; sig, &lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt; func);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Kilo中，&lt;code&gt;C-c&lt;/code&gt;是被忽略的，因为它非常容易导致丢失修改，可以这样实现：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(SIGINT, SIGIGN);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不过，在Kilo的实现，是通过调用&lt;code&gt;editorReadKey()&lt;/code&gt;，从Raw Mode 中读取一个按键存入数组，用switch匹配按键对应的值再返回给调用方，调用方也通过switch，匹配对应的操作函数。而在&lt;code&gt;C-c&lt;/code&gt;的部分，则是直接break掉了。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e67e80&#34;&gt;CTRL_C&lt;/span&gt;: &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Ctrl-c */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* We ignore ctrl-c, it can&amp;#39;t be so simple to lose the changes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;         * to the edited file. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种实现方法也有一定局限性，不同的终端模拟器可能发送不同的转义字符，硬编码转义字符会出现不适配的情况。并且使用&lt;code&gt;read()&lt;/code&gt;阻塞读取输入有性能瓶颈。&lt;/p&gt;&#xA;&lt;h3 id=&#34;精妙的数据结构与算法&#34;&gt;精妙的数据结构与算法&lt;/h3&gt;&#xA;&lt;p&gt;这个程序最有趣的地方在于清晰、通用的数据结构的设计，以&lt;code&gt;editorConfig&lt;/code&gt;为例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; editorConfig {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; cx, cy;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Cursor x and y position in characters */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rowoff;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Offset of row displayed. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; coloff;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Offset of column displayed. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; screenrows; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of rows that we can show */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; screencols; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of cols that we can show */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; numrows;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of rows */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rawmode;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Is terminal raw mode enabled? */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    erow &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;row;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Rows */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; dirty;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* File modified but not saved. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;filename; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Currently open filename */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; statusmsg[&lt;span style=&#34;color:#d699b6&#34;&gt;80&lt;/span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;time_t&lt;/span&gt; statusmsg_time;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; editorSyntax &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;syntax; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Current syntax highlight, or NULL. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们定义了一个&lt;code&gt;editorConfig&lt;/code&gt;类型的变量，它全局唯一，维护了程序的基本状态，包括行、列、滚动偏移、终端尺寸。让程序状态的流转非常清楚。这些内容都是一个文本编辑器需要关心的最核心内容：光标位置、视图偏移、数据和文件的状态等信息。&lt;/p&gt;&#xA;&lt;p&gt;通过这个结构体，能简单地获取程序当前的状态，或者为某项功能对状态作出修改，对一个新手来说还是挺拓宽思路的，至少我想不到怎么设计这些数据结构。&lt;/p&gt;&#xA;&lt;h3 id=&#34;数据与显示的分离&#34;&gt;数据与显示的分离&lt;/h3&gt;&#xA;&lt;p&gt;在&lt;code&gt;editorConfig&lt;/code&gt;中嵌套了一个&lt;code&gt;erow&lt;/code&gt;类型的变量，里面的东西也可以展开说说，定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; erow {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; idx;           &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row index in the file, zero-based. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; size;          &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Size of the row, excluding the null term. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rsize;         &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Size of the rendered row. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;chars;       &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row content. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;render;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row content &amp;#34;rendered&amp;#34; for screen (for TABs). */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;hl; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Syntax highlight type for each character in render.*/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; hl_oc;         &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row had open comment at end in last syntax highlight&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;                          check. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} erow;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里面有一个&lt;code&gt;render&lt;/code&gt;字段，在&lt;code&gt;editorUpdateRow()&lt;/code&gt;中，有这样的代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;, nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; j, idx;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Create a version of the row we can directly print on the screen,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;     * respecting tabs, substituting non printable characters with &amp;#39;?&amp;#39;. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;free&lt;/span&gt;(row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt; (j &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size; j&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j] &lt;span style=&#34;color:#7a8478&#34;&gt;==&lt;/span&gt; TAB)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            tabs&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; allocsize &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt;)row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (allocsize &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;gt;&lt;/span&gt; UINT32_MAX) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;Some line of the edited file is too long for kilo&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;循环的if中使用的 &lt;code&gt;TAB&lt;/code&gt; 定义在KEY_ACTION枚举，值为9，在ASCII码中是&lt;code&gt;\t&lt;/code&gt;也就是水平制表符。代码在统计tab的数量。&lt;/p&gt;&#xA;&lt;p&gt;问题在于，一个&lt;code&gt;\t&lt;/code&gt;在内存中占1字节，但在屏幕显示的时候会占据八个字符的宽度，这里就体现出&lt;code&gt;render&lt;/code&gt;的作用了，如果一行有两个&lt;code&gt;\t&lt;/code&gt;，每个最多展开为八个空格，那么所需要计算的大小就是&lt;code&gt;2 * 8 + chars的大小&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt;)row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那个恒为0的变量&lt;code&gt;nonprint&lt;/code&gt;可能是为将来打印不可见字符设计的。结尾的&lt;code&gt;+1&lt;/code&gt;为&lt;code&gt;&#39;\0&#39;&lt;/code&gt;预留。&lt;/p&gt;&#xA;&lt;p&gt;按照这个公式，给&lt;code&gt;render&lt;/code&gt;分配内存：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;malloc&lt;/span&gt;(row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;随后，这些代码在非制表位填充空格：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;idx &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt; (j &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size; j&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j] &lt;span style=&#34;color:#7a8478&#34;&gt;==&lt;/span&gt; TAB) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e67e80&#34;&gt;while&lt;/span&gt; ((idx &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#7a8478&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 在非制表位填充空格&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#e67e80&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 正常字符直接赋值&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;rsize &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; idx; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 在循环结束的时候，idx等于写入字符总数&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&lt;/span&gt;render[idx] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;//在字符末尾添加结束符&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然有点绕，但设计还是非常巧妙的！&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h3 id=&#34;代码高亮&#34;&gt;代码高亮&lt;/h3&gt;&#xA;&lt;p&gt;源码中使用大量篇幅实现了代码高亮，定义了一些关键字：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;C_HL_extensions[] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.c&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.h&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.cpp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.hpp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.cc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;NULL&lt;/span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;C_HL_keywords[] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C Keywords */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;break&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;case&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;continue&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;do&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;else&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;enum&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;extern&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;for&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;goto&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;if&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;register&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;return&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;sizeof&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;struct&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;switch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typedef&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;union&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;volatile&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;while&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;NULL&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C++ Keywords */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;alignas&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;alignof&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;and&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;and_eq&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;asm&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bitand&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bitor&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;class&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;compl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;constexpr&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;const_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;deltype&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;dynamic_cast&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;explicit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;export&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;friend&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;inline&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;mutable&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;new&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;noexcept&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;not&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;not_eq&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;nullptr&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;operator&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;or&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;or_eq&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;private&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;protected&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;public&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;reinterpret_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static_assert&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;template&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;this&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;thread_local&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;throw&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;try&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typeid&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typename&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;virtual&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;xor&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;xor_eq&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C types */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;int|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;long|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;double|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;float|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;char|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;unsigned|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;signed|&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;void|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;short|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;auto|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;const|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bool|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;NULL&lt;/span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在具体实现&lt;code&gt;editorUpdateSyntax()&lt;/code&gt;中，简单粗暴地遍历字符匹配这些关键字。在一般的教学例子中这样实现是可以的，我认为在具体的工程中应当用词法分析、语法分析和字典树去匹配。更易于维护和拓展，也能适配复杂的嵌套。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;上述分析提到的缺点都可以作为优化方向，比如提供更简单操作接口，用词法分析技术或接入LSP服务器，为程序提供Lua接口来扩展插件……不过我相信在古老的纯C应用中，添加这些功能的繁琐程度和开发周期简直是灾难级别的。但是在处理快捷键上，使用&lt;code&gt;termcap&lt;/code&gt;库的难度应该小于修改代码高亮部分的难度。&lt;/p&gt;&#xA;&lt;p&gt;这个项目最值得学习的点是如何将抽象的功能和终端联系起来、如何设计合理的数据结构以及标准库的使用。是阐释「程序 = 数据结构 + 算法」的很好例子。不过我自己是想不到那些函数该什么时候用，没准还会手动实现标准库造好的轮子呢。&lt;/p&gt;&#xA;&lt;p&gt;学习的过程很好玩，从主函数开始探索整个程序，一段一段地跳转调用，A调用B，B调用C，C调用D，理解了逻辑后再把它们画成图，对感兴趣的部分深入研究，有一种前人用他的智慧抚平我大脑褶皱的感觉……读懂它，几乎就等于一只脚趾踩上了理解Vim / Nano等项目的大门吧。&lt;/p&gt;&#xA;&lt;p&gt;想自己重新实现一次，然后加入自己的优化，比如联动Lua / Zig甚至是Go来实现上层的功能，好玩好玩真好玩。&lt;/p&gt;&#xA;&lt;p&gt;头皮好痒，要长脑子了！&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在查资料的过程中又发现了 &lt;a href=&#34;https://github.com/martanne/vis&#34;&gt;vis&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/zyedidia/micro&#34;&gt;micro&lt;/a&gt;（它甚至是用Go写的），又有新玩具了！&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>C语言的可变参数</title>
      <link>https://blog.verdant.ee/posts/c%E8%AF%AD%E8%A8%80%E7%9A%84%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0/</link>
      <pubDate>Sat, 05 Jul 2025 18:32:52 +0000</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/c%E8%AF%AD%E8%A8%80%E7%9A%84%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0/</guid>
      <description>&lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;&#xA;&lt;p&gt;C语言中，&lt;code&gt;printf()&lt;/code&gt;和&lt;code&gt;scanf()&lt;/code&gt;函数就是典型的变参函数，其优点是灵活处理参数。&lt;/p&gt;&#xA;&lt;p&gt;想要创建变参函数需引入头文件&lt;code&gt;stdarg.h&lt;/code&gt;，它有一些宏：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;va_list 指向整个可变参数列表的指针&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;原型：typedef char* va_list;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;va_start 指向可变参数列表前的参数（...前的参数）&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;原型：void va_start(va_list ap, paramN);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;va_arg 可变参数列表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;原型：typedef va_arg(va_list ap, type)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;va_end 结束对可变参数列表的访问，并释放资源&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;原型：void va_end(va_list ap);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;使用例&#34;&gt;使用例&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#e67e80;font-style:italic&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#e67e80;font-style:italic&#34;&gt;&amp;lt;stdarg.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 定义一个使用省略号的函数原型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; argument, ...)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 声明一个va_list类型的变量ap，这是可变参数列表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        va_list ap;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 使用va_start把变量ap初始化为参数列表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;va_start&lt;/span&gt;(ap, argument);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 第二个参数表明本函数期望传入一个int类型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 但是编译器不会检查到底输入了什么。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; output &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;va_arg&lt;/span&gt;(ap, &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;可变参数：%d&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;, output);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;va_end&lt;/span&gt;(ap);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;main&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;109&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;可变参数：109&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;函数起了作用，但是如果我们需要接受多个参数，应该如何获取呢？&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#e67e80;font-style:italic&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#e67e80;font-style:italic&#34;&gt;&amp;lt;stdarg.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 定义一个使用省略号的函数原型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; argument, ...)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 声明一个va_list类型的变量ap，这是可变参数列表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        va_list ap;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 使用va_start把变量ap初始化为参数列表，此处的第二个参数是最后一个固定参数&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;va_start&lt;/span&gt;(ap, argument);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 第二个参数表明本函数期望传入一个int类型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 但是编译器不会检查到底输入了什么。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; arguments_list[&lt;span style=&#34;color:#d699b6&#34;&gt;4&lt;/span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;4&lt;/span&gt;; i&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                arguments_list[i] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;va_arg&lt;/span&gt;(ap, &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;); &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 按int类型提取参数&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;va_end&lt;/span&gt;(ap);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 遍历参数列表并打印&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;4&lt;/span&gt;; i&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;可变参数%d：%d&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;, i &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;, arguments_list[i]);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;main&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;12&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;111&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;扩展va_start的第二个参数的工作原理&#34;&gt;扩展：&lt;code&gt;va_start&lt;/code&gt;的第二个参数的工作原理&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;va_start&lt;/code&gt;的第二个参数用于定位可变参数列表的起始位置，具体而言，它指向函数参数列表中最后一个固定参数（即省略号前的参数），通过该参数的地址计算出第一个可变参数在内存中的位置。&lt;/p&gt;&#xA;&lt;p&gt;C函数的参数按从右至左顺序入栈（栈底高地址，栈顶低地址）&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;va_start&lt;/code&gt;的第二个参数作为基准点，其地址加上自身大小后，即指向第一个可变参数的起始地址。&lt;/p&gt;&#xA;&lt;p&gt;所以&lt;code&gt;...&lt;/code&gt;前至少要有一个固定参数用于寻址。&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>C语言的头文件与OOP范式的抽象类、接口的异同点</title>
      <link>https://blog.verdant.ee/posts/c%E8%AF%AD%E8%A8%80%E7%9A%84%E5%A4%B4%E6%96%87%E4%BB%B6%E4%B8%8Eoop%E8%8C%83%E5%BC%8F%E7%9A%84%E6%8A%BD%E8%B1%A1%E7%B1%BB%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%BC%82%E5%90%8C%E7%82%B9/</link>
      <pubDate>Fri, 06 Jun 2025 20:16:56 +0000</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/c%E8%AF%AD%E8%A8%80%E7%9A%84%E5%A4%B4%E6%96%87%E4%BB%B6%E4%B8%8Eoop%E8%8C%83%E5%BC%8F%E7%9A%84%E6%8A%BD%E8%B1%A1%E7%B1%BB%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%BC%82%E5%90%8C%E7%82%B9/</guid>
      <description>&lt;h2 id=&#34;引言&#34;&gt;引言&lt;/h2&gt;&#xA;&lt;p&gt;说真的，从去年十一月份开始接触编程，到现在也好几个月了，自认为进步速度较快，但速度快也就导致了深度浅，走马观花式的学习，现在还是有很多理解不深刻的点，今天来仔细研究一下这个问题。我也是先入为主，第一门编程语言学的是Java，所以再接触C，一些概念就混淆了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;c语言模块化设计的核心&#34;&gt;C语言模块化设计的核心&lt;/h2&gt;&#xA;&lt;p&gt;头文件本质是对外提供的接口&lt;strong&gt;契约&lt;/strong&gt;，包含函数声明、宏定义、结构体类型声明。例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// math_utils.h&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#ifndef MATH_UTILS_H&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#define MATH_UTILS_H&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; b);  &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 函数声明&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#endif&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;编译隔离：通过头文件守卫(#ifndef)实现模块之间的编译隔离。&lt;/li&gt;&#xA;&lt;li&gt;虽然头文件可以实现函数，但这会破坏模块化。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;模块化实现规范&#34;&gt;模块化实现规范&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;源文件承载具体实现，通过包含对应头文件实现联系&lt;/li&gt;&#xA;&lt;li&gt;模块间通过&lt;code&gt;extern&lt;/code&gt;关键字声明共享全局变量（谨慎使用）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;面向对象中的抽象机制&#34;&gt;面向对象中的抽象机制&lt;/h2&gt;&#xA;&lt;p&gt;抽象类可以包含部分实现，它的目的是：&lt;strong&gt;描述一个公共基类，有共同的特征，有部分方法实现，但另一部分依赖子类自行实现&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;接口是一系列方法的声明，一个接口就是在描述一种能力。&lt;/p&gt;&#xA;&lt;p&gt;设计目标上，抽象类为了代码复用+多台，而接口是行为契约。&lt;/p&gt;&#xA;&lt;h2 id=&#34;设计范式对比&#34;&gt;设计范式对比&lt;/h2&gt;&#xA;&lt;h3 id=&#34;c模块化-vs-oop抽象&#34;&gt;C模块化 vs OOP抽象&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;抽象粒度：C模块关注功能单元（做什么），而OOP抽象关注对象行为契约（是谁做什么）&lt;/li&gt;&#xA;&lt;li&gt;扩展方式：C通过函数指针传递上下文，OOP通过继承、实现扩展。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
  </channel>
</rss>
