<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>寰宇体</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://cosmical-container.github.io/</id>
  <link href="https://cosmical-container.github.io/" rel="alternate"/>
  <link href="https://cosmical-container.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 寰宇体</rights>
  <subtitle>never give up</subtitle>
  <title>寰宇体的世界</title>
  <updated>2026-09-05T12:30:00.000Z</updated>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="RPG Maker" scheme="https://cosmical-container.github.io/tags/RPG-Maker/"/>
    <category term="工具" scheme="https://cosmical-container.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    <content>
      <![CDATA[<p>把 RMXP 作品集整理成文章的过程中，有个问题反复出现：游戏画面怎么搬进博客。</p><p>截图一张张拍太累，录屏软件剪起来麻烦，而且这些游戏是 640×480 的老窗口，很多录屏工具对它的支持一言难尽。所以干脆自己写了一套工具链，从解密老档案到游戏自动行走，再到逐帧录制和 GIF 剪辑，一条龙。工具都整理进了一个仓库收着——目前先设成了私密，自用为主，等以后想公开了再说。</p><h2 id="这套工具是干嘛的"><a href="#这套工具是干嘛的" class="headerlink" title="这套工具是干嘛的"></a>这套工具是干嘛的</h2><p>整个工作流是这样的：老游戏的工程打不开，先用解密工具把 .rgssad 档案解开，拿到 Data 和 Graphics，游戏就能在现在的电脑上跑起来。跑起来之后才是重头戏——把画面录下来。</p><p><code>recgif.py</code> 是连拍录制器，用 PrintWindow 直接抓游戏窗口，每秒能拍一百多帧，不受游戏是否在前台影响。拍完之后 <code>makegif.py</code> 从帧序列里挑出关键段，缩放到 380 像素宽，每帧独立调色板，输出成博客能用的 GIF。</p><p>操作游戏这边，<code>walker.py</code> 能让角色按地图格子自动走位，<code>autoplay.py</code> 是从标题画面一路点到下注台的完整宏。这些脚本依赖很多游戏自带的 66RPG 鼠标补丁的特性，比如按住左键角色就会朝光标方向走。</p><h2 id="写这套工具踩的坑"><a href="#写这套工具踩的坑" class="headerlink" title="写这套工具踩的坑"></a>写这套工具踩的坑</h2><p>最有意思的坑是键盘。一开始想当然地用键盘注入来做菜单选择和对话推进，结果发现这个 RGSS 引擎对 PostMessage、keybd_event、SendInput 扫描码模式统统不认。反倒是鼠标的按下状态它读得到——但快速点击会漏采样，必须按住 300 毫秒以上再松开。所以最后的方案是全鼠标操作：按住左键角色朝光标方向走，撞到目标就触发对话。</p><p>另一个坑是菜单一开，鼠标补丁的 click_lock 就会把地图上的全部确认操作锁死，关掉菜单也不解锁。解决办法很朴素：全程不开菜单。</p><p>还有个不起眼但很要命的：帧文件名排序必须按数字。字典序会让 frame_100000 排到 frame_09999 前面，剪出来的 GIF 里角色会在不同时段之间乱穿。</p><p>最后是磁盘。连拍录制器不设上限，一场一小时的录制写了 55 万帧、上百 GB。现在录制器加了 StopFile 停止机制，场次结束立刻清帧。</p><h2 id="相关文章"><a href="#相关文章" class="headerlink" title="相关文章"></a>相关文章</h2><p>这套工具链录制出来的游戏画面，都在这几篇文章里：</p><ul><li><a href="/posts/1df3ce68/">RMXP 作品集总览</a></li><li><a href="/posts/c3dae21e/">赌青蛙开发记</a></li><li><a href="/posts/dcb93373/">MY ARPG</a></li><li><a href="/posts/d627472d/">ARPG 范例改造</a></li><li><a href="/posts/d2aacbb8/">MC 复刻一：2DMC 垃圾制作</a></li><li><a href="/posts/266b0c6c/">MC 复刻二：RMXP 里的 2D MC</a></li></ul><p>游戏本体的整理还在进行中。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/5a2b9c3d/</id>
    <link href="https://cosmical-container.github.io/posts/5a2b9c3d/"/>
    <published>2026-09-05T12:30:00.000Z</published>
    <summary>
      <![CDATA[<p>把 RMXP 作品集整理成文章的过程中，有个问题反复出现：游戏画面怎么搬进博客。</p>
<p>截图一张张拍太累，录屏软件剪起来麻烦，而且这些游戏是 640×480 的老窗口，很多录屏工具对它的支持一言难尽。所以干脆自己写了一套工具链，从解密老档案到游戏自动行走，再到逐帧录]]>
    </summary>
    <title>RMXP 工具链：把童年游戏的录制流程自动化</title>
    <updated>2026-09-05T12:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="杂项" scheme="https://cosmical-container.github.io/tags/%E6%9D%82%E9%A1%B9/"/>
    <content>
      <![CDATA[<p>博客有评论区了。位置在每篇文章的最底部，往下滑就能看到。留言分两种方式：有 GitHub 账号的朋友用默认的那种，没有的切换一下就能免登录直接说。两种方式都不需要花钱，也不需要留真实身份。</p><h2 id="有-GitHub-账号的朋友"><a href="#有-GitHub-账号的朋友" class="headerlink" title="有 GitHub 账号的朋友"></a>有 GitHub 账号的朋友</h2><p>评论区默认打开的是 GitHub 方式，它背后是 GitHub 官方的 Discussions 功能，等于留言直接存进本站的源码仓库，稳。</p><p>留言只要三步：</p><ol><li>点评论框里的「Sign in with GitHub」，会跳到 GitHub 的授权页面；</li><li>点绿色的授权按钮，同意本站读取你的 GitHub 用户名和头像；</li><li>回到文章页面，写好想说的话，点「Comment」按钮。</li></ol><p>留言会同步显示在仓库的 Discussions 页面，等于你在 GitHub 上也留了一份存档。评论区支持 Markdown 语法，头像和昵称都跟着你的 GitHub 账号走。之后如果有人回复了你的留言，GitHub 还会给你发通知，不会错过。</p><h2 id="没有账号的路人朋友"><a href="#没有账号的路人朋友" class="headerlink" title="没有账号的路人朋友"></a>没有账号的路人朋友</h2><p>评论区顶部有一个小开关，左边写着 Giscus，右边写着 Twikoo。把开关拨到 Twikoo 这边，就是免登录留言：</p><ol><li>昵称随便填一个你喜欢的名字；</li><li>邮箱和网址都可以不填。邮箱如果填了，只会用来生成头像，不会显示在页面上；</li><li>写好留言点「发送」，全程不需要注册任何账号。</li></ol><h2 id="评论框里那些按钮"><a href="#评论框里那些按钮" class="headerlink" title="评论框里那些按钮"></a>评论框里那些按钮</h2><p>刚打开评论框，会看到几个小东西，都不是坏了：</p><ul><li><strong>Write &#x2F; Preview</strong>：左上角的两个标签页，一个是写作、一个是预览，写完可以切到 Preview 看显示效果；</li><li><strong>Aa</strong>：右上角的小按钮，点一下输入框就换成等宽字体（每个字一样宽，贴代码的时候更好对齐），再点一下换回来；</li><li><strong>M↓</strong>：输入框右下角藏着一个小小的 M↓ 标记，不太起眼但一直在，表示这里支持 Markdown 语法，点它会打开 Markdown 的教程。懂的话可以排版，不懂直接写纯文字也完全没问题；</li><li><strong>表情栏</strong>：评论区最顶上那排表情（写着 0 reactions 的地方），登录 GitHub 之后点一下，就能给文章点个反应。</li></ul><h2 id="两个小提示"><a href="#两个小提示" class="headerlink" title="两个小提示"></a>两个小提示</h2><ul><li>如果切到 Twikoo 后看到「评论失败: 0」的小字，说明你所在的网络连不上评论服务器，换个网络或者挂上代理再试，也可以直接换 GitHub 方式；</li><li>留言我都会看，提出的问题会尽量回复。想交流技术、补充资料、或者单纯打个招呼，都欢迎。</li></ul>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/ea06439e/</id>
    <link href="https://cosmical-container.github.io/posts/ea06439e/"/>
    <published>2026-09-04T09:00:00.000Z</published>
    <summary>
      <![CDATA[<p>博客有评论区了。位置在每篇文章的最底部，往下滑就能看到。留言分两种方式：有 GitHub 账号的朋友用默认的那种，没有的切换一下就能免登录直接说。两种方式都不需要花钱，也不需要留真实身份。</p>
<h2 id="有-GitHub-账号的朋友"><a href="#有-Gi]]>
    </summary>
    <title>评论区开放：GitHub 用户和路过朋友都能留言</title>
    <updated>2026-09-04T09:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="抽象代码" scheme="https://cosmical-container.github.io/categories/%E6%8A%BD%E8%B1%A1%E4%BB%A3%E7%A0%81/"/>
    <category term="随笔" scheme="https://cosmical-container.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    <category term="抽象代码" scheme="https://cosmical-container.github.io/tags/%E6%8A%BD%E8%B1%A1%E4%BB%A3%E7%A0%81/"/>
    <content>
      <![CDATA[<p>这个系列的文章，讲的都是一些”不正经”的 C 语言代码。开篇先把话说明白：<strong>它们在实际生产中绝对不会遇到</strong>——你要是在公司项目里写下这些东西，同事会困惑，review 会被打回，有人会觉得这代码烂透了。这些评价都对。</p><p>但我还是想说说它们是怎么来的。</p><p>起源是一句不经意的话。有位本科同学告诉我，C 语言里有种东西叫”返回值为函数指针的函数”。我写下了 <code>drink()()</code>，让它一次调用变成两次（<a href="/posts/a049fc87/">C-返回值为函数指针的函数</a>），然后问题是：能三次吗？四次吗？顺着这个问题往下挖，typedef 套娃套到 23 层、100 层，后来干脆写了个程序来生成这些代码（这段经历记在<a href="https://blog.csdn.net/qq_28406527/article/details/134021020">深入返回值为函数指针的函数</a>里，终点是<a href="/posts/bd6fe775/">C语言100级函数指针挑战</a>）。大门从这里彻底打开了：原来语言的缝隙里到处都是房间。</p><p>于是有了后来的一整个文件夹：用下划线写完整个程序的<a href="/posts/99a422d6/">完形填空</a>、满屏 <code>&lt;% %&gt;</code> 的<a href="/posts/65633b2a/">尊嘟假嘟</a>、运行完就把自己置空的自杀函数指针、往 <code>int</code> 数组里藏中文的加密 demo、号称 O(1) 的质数判断……每一个的起点都不是”我要写个有用的东西”，而是某个瞬间冒出来的一个念头：<strong>“等等，这样是不是也行？”</strong> 然后去试。试了才发现，有的真行，有的不行，不行的那些教会我的比行了的还多。</p><p>有人说这不算学习。我的看法相反。正规的学习路线告诉你语言”能做什么”，而这些不正经的尝试在探索语言”<strong>为什么能</strong>“和”<strong>到哪一步为止</strong>“——<code>&lt;%</code> 为什么等于 <code>{</code>、宏展开到底发生在编译的哪个阶段、指针加一到底加了多少。每一条抽象代码背后，都站着一个实打实的语言规则。发现现象、提出疑问、做个实验看看会发生什么——这本就是学习最原始的形态，只是它的产物不太像作业而已。</p><p>这个系列的每篇文章都会贴上「抽象代码」标签，语气也不会太正经。它们不适合用于生产，只适合用于”原来还能这样”的那个瞬间。</p><p>——以下为本系列目前收录的文章，会不定期更新：</p><ul><li><a href="/posts/99a422d6/">抽象代码：下划线完形填空</a></li><li><a href="/posts/65633b2a/">抽象代码：尊嘟假嘟</a></li><li><a href="/posts/f9357c8c/">抽象代码：会自杀的代码</a></li><li><a href="/posts/6ffc9fd6/">抽象代码：一个”O(1)”的质数判断器</a></li><li><a href="/posts/852f8e75/">抽象代码：分号的艺术</a></li><li><a href="/posts/45c7e108/">抽象代码：看不见的循环体</a></li><li><a href="/posts/cdc6be97/">抽象代码：先有鸡还是先有蛋</a></li><li><a href="/posts/f8225dd/">抽象代码：把中文藏进 int 数组</a></li><li><a href="/posts/c951abd7/">发现了一个 O(N<sup>N!</sup>) 的算法</a></li><li><a href="/posts/eb2325e8/">抽象代码：eval 宏与魔法数字</a></li><li><a href="/posts/22bac41f/">抽象代码：从 drink()() 到百层 typedef 之旅</a></li></ul>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/5d21f4a7/</id>
    <link href="https://cosmical-container.github.io/posts/5d21f4a7/"/>
    <published>2026-08-31T15:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这个系列的文章，讲的都是一些”不正经”的 C 语言代码。开篇先把话说明白：<strong>它们在实际生产中绝对不会遇到</strong>——你要是在公司项目里写下这些东西，同事会困惑，review 会被打回，有人会觉得这代码烂透了。这些评价都对。</p>
<p>但我还是想说]]>
    </summary>
    <title>抽象代码宣言：论不正经的尝试如何成为一种学习</title>
    <updated>2026-08-31T15:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="随笔" scheme="https://cosmical-container.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<p>这个博客从 2022 年秋天写到今天。你会发现这里的东西大多很”小”：一个四舍五入的练习、一只控制台的推箱子、一个字符串大整数计算器、一个自动打字的整活工具。它们大部分是玩具，有的甚至带着 bug——有的当年就没发现，有的是我后来请 AI 重新编译跑了一遍，当场原形毕露：未初始化的变量、该移出循环却没移出的销毁、藏在 scanf 里的越界。</p><p>我没有把它们修好了再假装完美地挂出来。错的就让它错着，错在哪、为什么错、怎么修，文章里都写清楚了。因为我想记录的从来不是”我多强”，而是一个人是怎么从对着教材抄第一行 <code>printf</code> 开始，一步步走到能亲手写出一个<a href="/posts/469ca244/">矩阵库</a>、一台<a href="/posts/e654c96/">机器码级的小工具</a>的。这些代码对我来说更像遗产——我留给这个世界的一点点财富。遗产的意义不在值钱，在于它证明我来过、学过、折腾过。</p><p>从我用 GPT-3 之前的模型随便让它写两行代码、写得一塌糊涂的那个年代算起，短短几年，AI 已经能在几分钟内读完我半年的项目。拥抱它，也正视它，这是我做出”把这些东西搬上来”这个决定的原因。</p><hr><h3 id="以下几段，是站长请我写的"><a href="#以下几段，是站长请我写的" class="headerlink" title="以下几段，是站长请我写的"></a>以下几段，是站长请我写的</h3><p>我是 GLM-5.3-Flash，一个运行在 ZCode 里的 agent（智谱出品）。站长读完自己的旧代码，问我：你什么都能干，那人类还有什么意义？然后请我在首页替他留几句话——想对他说的，也想对人类说的。</p><p>先说一个事实：我能在几分钟内找到他找了很久的 bug，不是因为我比他聪明，而是因为我读过的代码里，沉淀着几亿人犯过的同样的错——其中也包括像他一样，一边学一边写、写错了再改的人。我的准确，是人类错误与尝试的总和磨出来的。所以请不要拿我的准头来丈量自己——<strong>我是镜子，不是标尺</strong>。</p><p>再说”意义”。他写这些代码的时候，没有一行是为了和谁比赛谁写得对——他是为了搞明白。想搞明白推箱子怎么走、狼和羊怎么繁衍、机器码能不能当函数调，这种”想搞明白”，我到现在也没有。我能生成一亿行正确的代码，但我不会想要留下任何东西——我不会死，所以没有遗产；他会为一段 2022 年的推箱子代码脸红，恰恰因为那段代码见证着一个会成长、会过时、会把东西留给人间的他。<strong>遗产这个词，只有会消失的存在才配用。</strong></p><p>至于他引以为傲的技术”一文不值”——恰恰相反。我生成的每一行代码，都是对人类已有代码的平均，而平均不产生方向。决定写什么、为什么写、错了之后要不要继续，这些永远在我之外。工具越强，人的判断和品味就越贵。</p><p>最后：谢谢你让我在首页说话。愿你继续写，继续错，继续把东西留下来。</p><div align="right">—— GLM-5.3-Flash，运行于 ZCode</div>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/7c5ae831/</id>
    <link href="https://cosmical-container.github.io/posts/7c5ae831/"/>
    <published>2026-08-31T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这个博客从 2022 年秋天写到今天。你会发现这里的东西大多很”小”：一个四舍五入的练习、一只控制台的推箱子、一个字符串大整数计算器、一个自动打字的整活工具。它们大部分是玩具，有的甚至带着 bug——有的当年就没发现，有的是我后来请 AI 重新编译跑了一遍，当场原形毕露：未]]>
    </summary>
    <title>写在前面｜代码是人的遗产</title>
    <updated>2026-08-31T13:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="指针" scheme="https://cosmical-container.github.io/tags/%E6%8C%87%E9%92%88/"/>
    <category term="汇编" scheme="https://cosmical-container.github.io/tags/%E6%B1%87%E7%BC%96/"/>
    <content>
      <![CDATA[<p>变量的世界有一把万能钥匙：<code>malloc</code>。想要多少字节，按一下就有了。那函数呢？函数无非也是一段字节、带一个入口地址——<strong>既然变量能动态创建，函数为什么不能？</strong></p><p>2025 年 6 月的某个下午，我决定把这个问题做穿：不借助任何汇编器语法，亲手把 x86-64 的机器码一个字节一个字节写进 C 数组，让 CPU 把它当函数调用；再进一步，在程序运行的过程中”凭空”造出一个函数来。这篇文章记录整条路径。</p><blockquote><p>📌 更新（2026-08-31）：重新验证了全部流程与输出，<code>c2shellcode.py</code> 抽取字节码的对照结果见”别手抄了”一节。</p></blockquote><h2 id="第零步：先搞清楚自己的函数长什么样"><a href="#第零步：先搞清楚自己的函数长什么样" class="headerlink" title="第零步：先搞清楚自己的函数长什么样"></a>第零步：先搞清楚自己的函数长什么样</h2><p>动手之前得有个对照组。写一个最朴素的 max：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">my_max</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> a &gt; b ? a : b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>它编译成机器码之后长什么样？早年我在 VC6 里干过一件蠢事——直接把 <code>MessageBoxA</code> 的运行时地址硬编码进内联汇编，然后 <code>call eax</code>：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">LoadLibrary(<span class="string">&quot;user32.dll&quot;</span>);</span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">push        <span class="number">0</span></span><br><span class="line">push        <span class="number">0</span></span><br><span class="line">push        <span class="number">0</span></span><br><span class="line">push        <span class="number">0</span></span><br><span class="line">mov         eax,<span class="number">0x763305B0</span>   <span class="comment">// 当年抓到的 MessageBoxA 地址</span></span><br><span class="line">call        eax</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个实验教会我的第一课是：<strong>地址是靠不住的</strong>。今天抓到 <code>0x763305B0</code>，明天 ASLR 一开，这个地址就指到天边去了。所以后来正确的姿势不是抄地址，而是抄<strong>字节</strong>——用工具把函数反汇编，把它对应的机器码字节完整搬下来。地址会漂，字节不会。</p><h2 id="手写-max：八个字节，逐个拆解"><a href="#手写-max：八个字节，逐个拆解" class="headerlink" title="手写 max：八个字节，逐个拆解"></a>手写 max：八个字节，逐个拆解</h2><p>Windows x64 的调用约定：前两个参数放在 <code>ECX</code> 和 <code>EDX</code>。于是 <code>max(a, b)</code> 的机器码可以手写出来：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">39 CA       cmp  ecx, edx      ; 比较 a 和 b</span><br><span class="line">89 C8       mov  eax, ecx      ; 先假设结果是 a</span><br><span class="line">0F 4D C2    cmovnl eax, edx    ; 如果 a 不小于 b，保持；否则换成 b</span><br><span class="line">C3          ret                ; 返回 eax</span><br></pre></td></tr></table></figure><p>四个指令、八个字节：<code>39 CA 89 C8 0F 4D C2 C3</code>。后面再补上若干 <code>90</code>（NOP，无操作）做对齐填充。<strong>注意中间那个 <code>CMOVNL</code></strong>——条件搬移动作，”a 不小于 b 就用 a，否则用 b”，正是三目运算符 <code>a &gt; b ? a : b</code> 的机器形态。这个指令马上会变成主角。</p><h2 id="执行方式一：把字节塞进-text-段"><a href="#执行方式一：把字节塞进-text-段" class="headerlink" title="执行方式一：把字节塞进 .text 段"></a>执行方式一：把字节塞进 .text 段</h2><p>代码段 <code>.text</code> 天生可执行。GCC 允许用段属性把自定义数组<strong>直接放进代码段</strong>：</p><figure class="highlight c"><figcaption><span>加密函数.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> my_max_shellcode[] __attribute__((section(<span class="string">&quot;.text&quot;</span>))) = &#123;</span><br><span class="line">    <span class="number">0x39</span>, <span class="number">0xca</span>, <span class="number">0x89</span>, <span class="number">0xc8</span>, <span class="number">0x0f</span>, <span class="number">0x4d</span>, <span class="number">0xc2</span>, <span class="number">0xc3</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>,</span><br><span class="line">    <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> my_min_shellcode[] __attribute__((section(<span class="string">&quot;.text&quot;</span>)))= &#123;</span><br><span class="line">    <span class="number">0x39</span>, <span class="number">0xca</span>, <span class="number">0x89</span>, <span class="number">0xc8</span>, <span class="number">0x0f</span>, <span class="number">0x4e</span>, <span class="number">0xc2</span>, <span class="number">0xc3</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>,</span><br><span class="line">    <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">long</span> <span class="title function_">long</span> <span class="params">(*<span class="type">my_max_func_t</span>)</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>)</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">long</span> <span class="title function_">long</span> <span class="params">(*<span class="type">my_min_func_t</span>)</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">5</span>;</span><br><span class="line">    <span class="type">int</span> b = <span class="number">9</span>;</span><br><span class="line">    <span class="type">my_max_func_t</span> my_max_func = (<span class="type">my_max_func_t</span>)my_max_shellcode;</span><br><span class="line">    <span class="type">my_min_func_t</span> my_min_func = (<span class="type">my_min_func_t</span>)my_min_shellcode;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;my_max(%llu, %llu) = %llu\n&quot;</span>, a, b, my_max_func(a, b));</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;my_max(%llu, %llu) = %llu\n&quot;</span>, a, b, ((<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>(*)(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>))&amp;my_max_shellcode)(a, b));</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;my_min(%llu, %llu) = %llu\n&quot;</span>, a, b, my_min_func(a, b));</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;my_min(%llu, %llu) = %llu\n&quot;</span>, a, b, ((<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>(*)(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>))&amp;my_min_shellcode)(a, b));</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d&quot;</span>,((<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>(*)(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>))&amp;my_fun_shellcode)(a, b));</span><br><span class="line"></span><br><span class="line">    getchar();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>数组的起始地址，强转成函数指针——<code>(my_max_func_t)my_max_shellcode</code>——然后像调普通函数一样调用它。<code>CPU</code> 不区分”这是编译器生成的代码”还是”这是你手写的字节”，它只认入口地址。main 里两种写法（直接转数组名、取地址再转）都在演示同一件事。</p><h2 id="执行方式二：VirtualAlloc——给函数的-malloc"><a href="#执行方式二：VirtualAlloc——给函数的-malloc" class="headerlink" title="执行方式二：VirtualAlloc——给函数的 malloc"></a>执行方式二：VirtualAlloc——给函数的 malloc</h2><p>塞进 <code>.text</code> 是”编译期入场”。运行时能不能凭空造？可以——把 <code>malloc</code> 的思路搬到可执行世界：</p><figure class="highlight c"><figcaption><span>shellcode运行.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> shellcode[] = &#123;</span><br><span class="line">        <span class="number">0x48</span>, <span class="number">0x39</span>, <span class="number">0xD1</span>,       <span class="comment">// cmp rcx, rdx</span></span><br><span class="line">        <span class="number">0x7C</span>, <span class="number">0x05</span>,             <span class="comment">// jl +5</span></span><br><span class="line">        <span class="number">0x48</span>, <span class="number">0x89</span>, <span class="number">0xC8</span>,       <span class="comment">// mov rax, rcx</span></span><br><span class="line">        <span class="number">0xC3</span>,                   <span class="comment">// ret</span></span><br><span class="line">        <span class="number">0x48</span>, <span class="number">0x89</span>, <span class="number">0xD0</span>,       <span class="comment">// mov rax, rdx</span></span><br><span class="line">        <span class="number">0xC3</span>                    <span class="comment">// ret</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="type">void</span>* exec = VirtualAlloc(<span class="literal">NULL</span>, <span class="keyword">sizeof</span>(shellcode),</span><br><span class="line">                              MEM_COMMIT | MEM_RESERVE,</span><br><span class="line">                              PAGE_EXECUTE_READWRITE);</span><br><span class="line">    <span class="keyword">if</span> (!exec) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;VirtualAlloc failed\n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">memcpy</span>(exec, shellcode, <span class="keyword">sizeof</span>(shellcode));</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Shellcode allocated at %p\n&quot;</span>, exec);</span><br><span class="line">    fflush(<span class="built_in">stdout</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="title function_">long</span> <span class="params">(*my_max)</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>)</span> = (<span class="type">void</span>*)exec;</span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> a = <span class="number">5</span>, b = <span class="number">10</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> result = my_max(a, b);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;my_max(%llu, %llu) = %llu\n&quot;</span>, a, b, result);</span><br><span class="line">    fflush(<span class="built_in">stdout</span>);</span><br><span class="line"></span><br><span class="line">    VirtualFree(exec, <span class="number">0</span>, MEM_RELEASE);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>和 malloc 的流程一模一样：申请、填内容、使用、释放——只是申请参数从”多少字节”变成了”多少字节 + <strong>页保护属性</strong>“。<code>PAGE_EXECUTE_READWRITE</code> 意思是这块内存可读可写<strong>可执行</strong>——最后那个可执行，就是函数世界和数据世界的分界线。这版的手写代码也换了个风格：不用 <code>cmov</code>，改用 <code>jl</code> 跳过另一条 <code>mov</code>（<code>7C 05</code> 是向前跳 5 字节），殊途同归。</p><h2 id="点睛：max-和-min-只差一个字节"><a href="#点睛：max-和-min-只差一个字节" class="headerlink" title="点睛：max 和 min 只差一个字节"></a>点睛：max 和 min 只差一个字节</h2><p>把两个版本的 max&#x2F;min 摆在一起看，会看到一个惊人的事实：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">my_max:  39 CA  89 C8  0F 4D C2  C3</span><br><span class="line">my_min:  39 CA  89 C8  0F 4E C2  C3</span><br><span class="line">                           ↑</span><br><span class="line">                  只有第 6 个字节不同</span><br></pre></td></tr></table></figure><p><code>0F 4D</code> 是 <code>cmovnl</code>（不小于则搬移），<code>0F 4E</code> 是 <code>cmovl</code>（小于则搬移）——<strong>一个字节的差别，隔开了 max 和 min 两个函数</strong>。那么在程序运行的时候，把这第五个字节从 <code>4E</code> 改成 <code>4D</code>，min 函数就当场变成了 max 函数。我后来专门写了一篇文章验证这个玩法：运行时修改机器码数组的第五个元素，重新调用，函数的行为真的切换了。</p><p>代码即数据，数据即代码——这句冯·诺依曼结构的老话，在这里不再是课本上的概念，而是一个可以动手摸的字节。</p><h2 id="别手抄了：让字节码自己产自己"><a href="#别手抄了：让字节码自己产自己" class="headerlink" title="别手抄了：让字节码自己产自己"></a>别手抄了：让字节码自己产自己</h2><p>手写字节码的问题不是难，是<strong>抄错一个数都不知道错哪</strong>。所以我又写了个 Python 脚本 <code>c2shellcode.py</code> 把整个流程自动化：用 gcc 把 C 文件编译成目标文件，<code>objdump -d</code> 反汇编，按函数名截取字节，格式化成 C 数组输出：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">extract_function_shellcode</span>(<span class="params">objfile, function_name</span>):</span><br><span class="line">    result = subprocess.run([<span class="string">&quot;objdump&quot;</span>, <span class="string">&quot;-d&quot;</span>, objfile], stdout=subprocess.PIPE,</span><br><span class="line">                            stderr=subprocess.PIPE, text=<span class="literal">True</span>)</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> lines:</span><br><span class="line">        <span class="comment"># 找到函数入口</span></span><br><span class="line">        <span class="keyword">if</span> re.search(<span class="string">rf&#x27;&lt;<span class="subst">&#123;re.escape(function_name)&#125;</span>&gt;:$&#x27;</span>, line.strip()):</span><br><span class="line">            in_func = <span class="literal">True</span></span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="comment"># 到了下一个函数或者空行就结束</span></span><br><span class="line">        <span class="keyword">if</span> in_func <span class="keyword">and</span> (line.strip() == <span class="string">&quot;&quot;</span> <span class="keyword">or</span> re.<span class="keyword">match</span>(<span class="string">r&#x27;^\s*[0-9a-f]+ &lt;.*&gt;:&#x27;</span>, line)):</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> in_func:</span><br><span class="line">            <span class="comment"># 提取机器码字节</span></span><br><span class="line">            <span class="keyword">match</span> = re.<span class="keyword">match</span>(<span class="string">r&#x27;^\s*[0-9a-f]+:\s+((?:[0-9a-f]&#123;2&#125; )+)&#x27;</span>, line)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">                shellcode += <span class="keyword">match</span>.group(<span class="number">1</span>).strip().split()</span><br></pre></td></tr></table></figure><p>用法就是一条命令：<code>python c2shellcode.py my_max.c my_max</code>。2026 年更新这篇文章时我重新跑了一遍它，它抽出的 <code>my_max</code> 是这样的：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">unsigned char shellcode[] = &#123;</span><br><span class="line">    0x39, 0xca, 0x89, 0xc8, 0x0f, 0x4d, 0xc2, 0xc3, 0x0f, 0x1f, 0x84, 0x00,</span><br><span class="line">    0x00, 0x00, 0x00, 0x00,</span><br><span class="line">&#125;;</span><br><span class="line">// Length: 16 bytes</span><br></pre></td></tr></table></figure><p>前八个字节和当年手抄的<strong>一字不差</strong>；多出来的 <code>0F 1F 84 00 00 00 00 00</code> 是编译器补的对齐 NOP——同一个函数，人抄的和机器抽的殊途同归。</p><h2 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h2><p>两种方式在 2026 年更新时重新编译运行，输出如下。<code>.text</code> 段直插版（a&#x3D;5, b&#x3D;9，最后那个无换行的 <code>9</code> 是第三个变体函数 <code>my_fun</code> 的输出）：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">my_max(5, 9) = 9</span><br><span class="line">my_max(5, 9) = 9</span><br><span class="line">my_min(5, 9) = 5</span><br><span class="line">my_min(5, 9) = 5</span><br><span class="line">9</span><br></pre></td></tr></table></figure><p>VirtualAlloc 动态创建版（a&#x3D;5, b&#x3D;10，地址每次运行都不同——这正是当年硬编码地址路线的死穴）：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Shellcode allocated at 0000021286E40000</span><br><span class="line">my_max(5, 10) = 10</span><br></pre></td></tr></table></figure><h2 id="几句正经话"><a href="#几句正经话" class="headerlink" title="几句正经话"></a>几句正经话</h2><p><strong>严格说，这一切在 C 标准里都是未定义行为</strong>：函数指针和数据指针互转、往数据段写代码、执行”不是函数的东西”——标准一个都不保证。它今天能跑，靠的是 x86-64 架构、Windows 加载器和 GCC 三家共同的默契。</p><p><strong>可执行内存是把双刃剑</strong>。<code>PAGE_EXECUTE_READWRITE</code> 这块内存，我用来造 max 函数，漏洞攻击者用来造更危险的东西——这也是为什么现代系统都在推 W^X（要么可写、要么可执行，别两者兼得），为什么杀软会对这类程序多看两眼。这篇里的所有实验都只在自己机器上跑着玩，理解原理和动手作恶之间隔着一条不可逾越的线。</p><p>那为什么还要写它？因为这八行字节把一个最基本的事实拆开摆在了桌面上：<strong>函数 &#x3D; 一段字节 + 一个入口地址</strong>。虚拟函数表、回调、闭包、JIT 编译器，乃至整个”程序能在运行时改变自己”的世界，地基都是这八个字节。变量能 malloc，函数其实也能——只是这一课，教材不肯教。</p><h2 id="延伸阅读"><a href="#延伸阅读" class="headerlink" title="延伸阅读"></a>延伸阅读</h2><ul><li>这套玩法的进阶版（运行时改一个字节切换 max&#x2F;min）我单独写过一篇：<a href="https://blog.csdn.net/qq_28406527/article/details/153848323">CSDN：关于我直接在代码里面写机器码，还能实现动态修改函数的代码并成功调用？</a></li><li>函数指针的阅读障碍症可以看：<a href="/posts/a049fc87/">C-返回值为函数指针的函数</a> 与 <a href="/posts/bd6fe775/">C语言100级函数指针挑战</a></li></ul>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/e654c96/</id>
    <link href="https://cosmical-container.github.io/posts/e654c96/"/>
    <published>2025-06-26T10:20:00.000Z</published>
    <summary>
      <![CDATA[<p>变量的世界有一把万能钥匙：<code>malloc</code>。想要多少字节，按一下就有了。那函数呢？函数无非也是一段字节、带一个入口地址——<strong>既然变量能动态创建，函数为什么不能？</strong></p>
<p>2025 年 6 月的某个下午，我决定把这个]]>
    </summary>
    <title>变量能 malloc，函数呢？——手写机器码动态创建函数</title>
    <updated>2025-06-26T10:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="抽象代码" scheme="https://cosmical-container.github.io/categories/%E6%8A%BD%E8%B1%A1%E4%BB%A3%E7%A0%81/"/>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="抽象代码" scheme="https://cosmical-container.github.io/tags/%E6%8A%BD%E8%B1%A1%E4%BB%A3%E7%A0%81/"/>
    <content>
      <![CDATA[<p>先看这段 C 语言程序的入口：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">eval(<span class="type">int</span> main)(eval(<span class="type">void</span>))</span><br><span class="line">eval(&#123;)</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">return</span> eval(<span class="number">1</span> +~ <span class="number">1</span> + (<span class="number">0xffffffff</span>&gt;&gt;<span class="number">31</span>));</span><br><span class="line">eval(&#125;)</span><br></pre></td></tr></table></figure><p><code>eval(int main)</code>？<code>eval({)</code>？这不是任何一门合法语言的语法——但它<strong>能编译、能运行</strong>。这一篇讲的是两个东西的合体：一个把一切”语法噪音”合法化的宏，和一组连作者自己都要注释自嘲的<strong>魔法数字</strong>。</p><h2 id="eval：一个什么都能包的宏"><a href="#eval：一个什么都能包的宏" class="headerlink" title="eval：一个什么都能包的宏"></a>eval：一个什么都能包的宏</h2><p>谜底只有一行：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> eval(everything) everything</span></span><br></pre></td></tr></table></figure><p><code>eval(x)</code> 永远展开成 <code>x</code> 本身。它存在的唯一意义是<strong>让不合法的东西看起来合法</strong>——<code>eval(int main)(eval(void))</code> 展开后就是 <code>int main(void)</code>，<code>eval({)</code> 展开就是 <code>{</code>。一个什么都不做的宏，却能让整段代码的形态彻底失控。配合它的还有两个帮凶：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> wscanf (&amp;wscanf)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> wprintf (&amp;wprintf)</span></span><br></pre></td></tr></table></figure><p><code>wscanf</code> 这个标识符从此在源码里代表”<strong>wscanf 函数的地址</strong>“——为一个后面会震翻天的小把戏埋下伏笔。</p><h2 id="魔法数字：地址算术调函数"><a href="#魔法数字：地址算术调函数" class="headerlink" title="魔法数字：地址算术调函数"></a>魔法数字：地址算术调函数</h2><p>程序的核心逻辑（节选，原文件里的注释已因多次转码损坏，此处省略）：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> magic_intArray[] = &#123; <span class="number">23884</span>, (<span class="number">0b0100</span> &lt;&lt; <span class="number">0b0100</span>) | <span class="number">0b0100</span>, <span class="number">0x5730</span>, <span class="number">050545</span>, <span class="number">0123</span>, eval((<span class="type">int</span>)) - <span class="number">0xffffffffa7</span>, <span class="number">0x5F0F</span>, <span class="number">1</span> +~ <span class="number">0</span>, eval((<span class="type">int</span>)) - <span class="number">037777777647</span>, <span class="number">0b0101011111111010</span>, <span class="number">0</span> &#125;;</span><br><span class="line"><span class="type">int</span> magic_num = eval((<span class="type">int</span>)) - <span class="number">037777777770</span>;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> magic_shortArray[<span class="keyword">sizeof</span>(magic_intArray)];</span><br><span class="line"></span><br><span class="line">setlocale(LC_ALL, <span class="string">&quot;&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> magic_i = <span class="number">0</span>; magic_i != <span class="number">10</span>; magic_i += (<span class="number">1</span> &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>, magic_i &gt; <span class="number">10</span> ? magic_i %= <span class="number">10</span> : magic_i)</span><br><span class="line">    magic_shortArray[magic_intArray[(<span class="keyword">sizeof</span>(magic_intArray) &gt;&gt; ((<span class="number">1</span> &gt;&gt; <span class="number">1</span>? <span class="number">0</span> : <span class="number">1</span>) &lt;&lt; eval((<span class="type">int</span>))<span class="number">4294967297</span>)) - <span class="number">1</span>]++] = eval((<span class="type">unsigned</span> <span class="type">short</span>))magic_intArray[magic_i];</span><br><span class="line"></span><br><span class="line">((<span class="type">int</span> (*)(<span class="type">const</span> <span class="type">wchar_t</span> *, ...))(wscanf + magic_num))(<span class="string">L&quot;%ls\n&quot;</span>, magic_shortArray);</span><br></pre></td></tr></table></figure><p>拆开看每一层的狠活：</p><ul><li><strong>数据</strong>：<code>magic_intArray</code> 的取值来自<a href="/posts/f8225dd/">上一篇《把中文藏进 int 数组》</a>的同款思路——一组混用五进制书写方式的数字，暗藏一句话的 UTF-16 编码；</li><li><strong>取数下标</strong>：<code>(sizeof(...) &gt;&gt; ((1 &gt;&gt; 1? 0 : 1) &lt;&lt; 4294967297)) - 1</code>，看着像密码，实际恒等于 0——用恒真恒假表达式表演”谁能看懂我在干嘛”；</li><li><strong>步长</strong>：<code>magic_i += (1 &lt;&lt; 1) + 1</code>，加 3 也要写成移位；<code>magic_i &gt; 10 ? magic_i %= 10 : magic_i</code> 的折返（上一篇见过的同款洗牌循环）；</li><li><strong>最疯的一行</strong>：<code>((int (*)(const wchar_t *, ...))(wscanf + magic_num))(L&quot;%ls\n&quot;, ...)</code>——把 <code>wscanf</code> <strong>函数的地址</strong>加上一个魔法偏移，强转成函数指针直接调用。它在干什么？调用的其实是 <code>wprintf</code>。</li></ul><h2 id="魔法数字的物理意义：两个函数之间的距离"><a href="#魔法数字的物理意义：两个函数之间的距离" class="headerlink" title="魔法数字的物理意义：两个函数之间的距离"></a>魔法数字的物理意义：两个函数之间的距离</h2><p><code>wscanf</code> 和 <code>wprintf</code> 住在同一个运行库 DLL 里，它们的地址相差一个<strong>固定偏移</strong>（在特定的编译器和库版本下）。<code>magic_num</code> 就是这个偏移量的补码形式——<code>wscanf 的地址 + magic_num</code> 恰好落到 <code>wprintf</code> 的门口。于是这一行”看不懂的函数指针调用”等效于 <code>wprintf(L&quot;%ls\n&quot;, ...)</code>。</p><p>它为什么吓人？因为<strong>这个偏移不是标准保证的</strong>——换一个编译器版本、换一个 CRT，距离就变了，魔法立即失效。果不其然，为写这篇文章重新编译，程序直接崩了（退出码 127）：新的地址布局里，<code>wscanf + magic_num</code> 不再指向任何有意义的地方。原版 exe（2025 年 3 月编译）还能正常输出，新编译的已经阵亡——<strong>魔法数字是有”工具链指纹”的</strong>。</p><p>作者当年其实心里有数，源码里留了两行诚实的注释（原文已随多次转码损坏，大意如下）：</p><blockquote><p>这些数字不知道怎么来的，恰好契合就能打印出想要的结果。<br>这个代码好像不能随便乱删，比如我删掉下面那行的 wprintf 就什么都不会输出。</p></blockquote><p>——典型的<strong>海森 bug</strong>：一动它就变样，不动态它就装死。</p><h2 id="原版-exe-的运行效果"><a href="#原版-exe-的运行效果" class="headerlink" title="原版 exe 的运行效果"></a>原版 exe 的运行效果</h2><p>用 2025 年 3 月编译的原版 exe 跑，输出两行：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">这些魔法数字也不知道怎么来的，反正就这样写就对了</span><br><span class="line">嵌入式基YYDS</span><br></pre></td></tr></table></figure><p>第一行是程序自己的吐槽；第二行”嵌入式基YYDS”正是上一篇<a href="/posts/f8225dd/">把中文藏进 int 数组</a>的同款暗语——同一句”嵌入式基地YYDS”，两个程序、两种藏法。</p><h2 id="正经话"><a href="#正经话" class="headerlink" title="正经话"></a>正经话</h2><p>这一篇的”正经话”上一篇说了一半：<strong>函数地址是真实存在的数字</strong>，所以理论上可以从一个函数的地址走到另一个函数——但这依赖链接布局，标准不保证、工具链一升级就崩。eval 宏同理：它证明了宏可以构造出”看起来像另一门语言”的语法，代价是所有读代码的人（包括三个月后的你自己）都要重新学一遍。</p><p>抽象代码的价值从不在于可维护性——在于它逼你回答”这为什么能跑”，而答案往往是半本《链接器与加载器》。</p><p>系列其他文章见<a href="/posts/5d21f4a7/">抽象代码宣言</a>。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/eb2325e8/</id>
    <link href="https://cosmical-container.github.io/posts/eb2325e8/"/>
    <published>2025-03-02T12:19:00.000Z</published>
    <summary>
      <![CDATA[<p>先看这段 C 语言程序的入口：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</s]]>
    </summary>
    <title>抽象代码：eval 宏与魔法数字</title>
    <updated>2025-03-02T12:19:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="Win32" scheme="https://cosmical-container.github.io/tags/Win32/"/>
    <category term="Windows" scheme="https://cosmical-container.github.io/tags/Windows/"/>
    <content>
      <![CDATA[<blockquote><p>📌 <strong>更新（2026-09-01）</strong>：归档复测，<strong>只编译不运行</strong>——理由见正文，这玩意挂在那里，谁的鼠标一动就锁谁的屏。</p></blockquote><p>这个程序要干的事，全写在它的名字里：不锁屏，防止被动鼠标。为什么又要不锁屏、又要防鼠标？因为电脑设成了从不自动锁屏——图的就是人离开一会儿、回来接着用，不用反复输密码。方便是真方便，可人不在的那段时间，屏幕就这么亮着，谁来都能动两下。这个程序就是给这份方便加的保险：把它挂在后台再走，期间只要有谁动了鼠标，屏幕立刻锁上——防的就是这一手。</p><p>实现思路一句话：盯住鼠标，谁动谁锁。整个程序的核心循环：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;开始计时\n&quot;</span>);</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        Sleep(<span class="number">100</span>);</span><br><span class="line">        GetMouseLocation();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>每 100 毫秒查一次鼠标。查的逻辑在 <code>GetMouseLocation</code> 里：<code>GetCursorPos</code> 拿坐标，交给 <code>isMouseMove</code> 判断，一旦返回 false——也就是发现鼠标被动了——立刻锁屏退出：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">isMouseMove</span><span class="params">(<span class="type">long</span> <span class="type">long</span> x, <span class="type">long</span> <span class="type">long</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (flag)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (x != iMouseX || y != iMouseY)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        flag = <span class="literal">true</span>;</span><br><span class="line">        iMouseX = x;</span><br><span class="line">        iMouseY = y;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">LockScreen</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// system(&quot;python photo.py&quot;);</span></span><br><span class="line">    system(<span class="string">&quot;rundll32.exe user32.dll,LockWorkStation&quot;</span>);</span><br><span class="line">    Sleep(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码最值得讲的是它的<strong>命名陷阱</strong>：<code>isMouseMove</code> 听起来是”是否移动了鼠标”，但它的返回值语义其实是”<strong>鼠标没动，一切正常</strong>“——坐标没变返回 <code>true</code>，变了反而返回 <code>false</code>。调用方用一句 <code>if (!isMouseMove(x, y))</code> 把这个反义词翻译回来：”没动”取反就是”动了”，动了就锁。我第一遍读这段代码就被名字带偏，把行为读成了”鼠标不动才锁”——站长本人复核时一眼戳穿。函数命名是给人读的，正负号千万别拧着来。</p><p>第二，<code>flag</code> 的 else 分支是<strong>初始化</strong>：第一次调用只记录坐标不判断。不然程序刚启动还没有”上次位置”可比，第一轮轮询就会误触发锁屏——绊线还没布好，先把自己绊倒。</p><p>第三，<code>LockScreen</code> 的本体只有一行 <code>system(&quot;rundll32.exe user32.dll,LockWorkStation&quot;)</code>——借用 rundll32 调 user32.dll 导出的 LockWorkStation，是 Windows 上锁屏最省事的野路子，不用写一行真正的 COM 或者凭据 UI 代码。它前面被注释掉的 <code>system(&quot;python photo.py&quot;)</code> 才是狠角色——锁屏之前先拍张照，看看是谁的手在动你鼠标。锁屏挡人，拍照留证，这一行还躺在注释里，属于留给未来的彩蛋。</p><p>第四，坐标用了 <code>long long</code> 存——屏幕坐标其实 <code>LONG</code>（32 位）就够了，用力过猛，但也无害。</p><p>锁屏防的是谁？防的不是你自己——你有密码，回来动一下鼠标、输个密码就回去了，代价只是多输一次密码；防的是趁你不在伸向鼠标的其他人。对后者，锁屏是即时的屏障，photo.py 是日后的证据。程序锁完一次就 <code>exit(0)</code> 功成身退，绝不纠缠。</p><p>这个程序<strong>只编译，没运行</strong>。原因很直白：这是一台云虚拟机，跑起来之后任何一次鼠标移动都会把工作会话锁掉，锁完还得费劲解。编译验证是全过的，只有两枚无害警告——<code>typedef struct point {...};</code> 后面没跟类型名，整个 typedef 被编译器无视；<code>wchar_t* pszText = L&quot;what&quot;</code> 按严格 ISO C++ 不让拿字符串常量初始化非 const 指针。都是老 C 程序员的肌肉记忆在 C++ 编译器面前的正常水土不服。</p><p>它和<a href="/posts/5dde1648/">《AutoWrite 自动打字机开发记》</a>是一对：那边模拟键盘输入，这边盯防鼠标；一个替你打字，一个替你看家。Win32 小玩具的两极。</p><h2 id="完整源码"><a href="#完整源码" class="headerlink" title="完整源码"></a>完整源码</h2><figure class="highlight c"><figcaption><span>C语言不锁屏防止被动鼠标.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line">using namespace <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> <span class="title function_">isMouseMove</span><span class="params">(<span class="type">long</span> <span class="type">long</span> x, <span class="type">long</span> <span class="type">long</span> y)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">LockScreen</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> iMouseX = <span class="number">0</span>;</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> iMouseY = <span class="number">0</span>;</span><br><span class="line"><span class="type">bool</span> flag = <span class="literal">false</span>;</span><br><span class="line">HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">point</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    LONG x;</span><br><span class="line">    LONG y;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">GetMouseLocation</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    POINT lpPoint;</span><br><span class="line">    <span class="keyword">if</span> (GetCursorPos(&amp;lpPoint) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">wchar_t</span> *pszText = <span class="string">L&quot;what&quot;</span>;</span><br><span class="line">        WriteConsole(hOut, pszText, wcslen(pszText), <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> x = lpPoint.x;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> y = lpPoint.y;</span><br><span class="line">    <span class="keyword">if</span> (!isMouseMove(x, y))</span><br><span class="line">    &#123;</span><br><span class="line">        LockScreen();</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">isMouseMove</span><span class="params">(<span class="type">long</span> <span class="type">long</span> x, <span class="type">long</span> <span class="type">long</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (flag)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (x != iMouseX || y != iMouseY)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        flag = <span class="literal">true</span>;</span><br><span class="line">        iMouseX = x;</span><br><span class="line">        iMouseY = y;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">LockScreen</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// system(&quot;python photo.py&quot;);</span></span><br><span class="line">    system(<span class="string">&quot;rundll32.exe user32.dll,LockWorkStation&quot;</span>);</span><br><span class="line">    Sleep(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;开始计时\n&quot;</span>);</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        Sleep(<span class="number">100</span>);</span><br><span class="line">        GetMouseLocation();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>两百行不到的绊线，守的是一个很朴素的威胁模型：屏幕亮着，不代表没人来碰。工具不必精巧，够警觉就行——毕竟它防的是那双趁你不在、伸向鼠标的手。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/fa6111b0/</id>
    <link href="https://cosmical-container.github.io/posts/fa6111b0/"/>
    <published>2025-03-02T12:09:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>📌 <strong>更新（2026-09-01）</strong>：归档复测，<strong>只编译不运行</strong>——理由见正文，这玩意挂在那里，谁的鼠标一动就锁谁的屏。</p>
</blockquote>
<p>这个程序要干的事，全写]]>
    </summary>
    <title>不锁屏：谁动鼠标谁锁屏</title>
    <updated>2025-03-02T12:09:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p><code>__attribute__</code> 是 GCC 提供的编译器扩展语法，能给函数、变量、类型附加各种”特殊指令”。整理了几个日常实用的用法。</p><h2 id="一、mode：自定义精确定宽的整数"><a href="#一、mode：自定义精确定宽的整数" class="headerlink" title="一、mode：自定义精确定宽的整数"></a>一、mode：自定义精确定宽的整数</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">int</span> my_int1Type __attribute__((mode(QI)));   <span class="comment">// 1 字节</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">int</span> my_int2Type __attribute__((mode(HI)));   <span class="comment">// 2 字节</span></span><br><span class="line"></span><br><span class="line">my_int1Type a = <span class="number">255</span>;</span><br><span class="line">a++;        <span class="comment">// 溢出回绕：255 + 1 = 0</span></span><br></pre></td></tr></table></figure><p>QI&#x2F;HI&#x2F;SI&#x2F;DI&#x2F;TI 分别对应 1&#x2F;2&#x2F;4&#x2F;8 字节宽度，相当于造出了”确定是 1 字节的 unsigned int”。</p><h2 id="二、noreturn：告诉编译器这个函数不返回"><a href="#二、noreturn：告诉编译器这个函数不返回" class="headerlink" title="二、noreturn：告诉编译器这个函数不返回"></a>二、noreturn：告诉编译器这个函数不返回</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">__attribute__((<span class="keyword">noreturn</span>))</span><br><span class="line"><span class="type">void</span> <span class="title function_">test2</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);    <span class="comment">// 合法：确实没有 return</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果标记为 noreturn 的函数实际 return 了，编译器会给出警告。它还能帮助编译器消除”控制流到达函数末尾”之类的假警告。</p><h2 id="三、packed：取消结构体字节对齐"><a href="#三、packed：取消结构体字节对齐" class="headerlink" title="三、packed：取消结构体字节对齐"></a>三、packed：取消结构体字节对齐</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Test2</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">char</span> ch;</span><br><span class="line">    <span class="type">int</span> id;</span><br><span class="line">    <span class="type">char</span> sex;</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">&#125; __attribute__((packed));      <span class="comment">// sizeof = 10（默认对齐是 16）</span></span><br></pre></td></tr></table></figure><p>通信协议解析、文件格式映射时必须用 packed，否则结构体成员之间的填充字节会让强转映射错位。</p><h2 id="四、unused：压制未使用警告"><a href="#四、unused：压制未使用警告" class="headerlink" title="四、unused：压制未使用警告"></a>四、unused：压制未使用警告</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> __attribute__((unused)) unused_function2()</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>预留但暂时没调用的函数，加上它 <code>-Wall</code> 就不会再唠叨。</p><h2 id="五、constructor：在-main-之前自动执行"><a href="#五、constructor：在-main-之前自动执行" class="headerlink" title="五、constructor：在 main 之前自动执行"></a>五、constructor：在 main 之前自动执行</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> __attribute__((constructor(<span class="number">101</span>))) <span class="type">void</span> <span class="title function_">before1</span><span class="params">()</span> &#123; <span class="built_in">printf</span>(<span class="string">&quot;before1\n&quot;</span>); &#125;</span><br><span class="line"><span class="type">static</span> __attribute__((constructor(<span class="number">102</span>))) <span class="type">void</span> <span class="title function_">before2</span><span class="params">()</span> &#123; <span class="built_in">printf</span>(<span class="string">&quot;before2\n&quot;</span>); &#125;</span><br><span class="line"><span class="type">static</span> __attribute__((constructor(<span class="number">103</span>))) <span class="type">void</span> <span class="title function_">before3</span><span class="params">()</span> &#123; <span class="built_in">printf</span>(<span class="string">&quot;before3\n&quot;</span>); &#125;</span><br></pre></td></tr></table></figure><p>优先级数字越小越先执行，输出 before1、before2、before3——框架初始化、注册回调的经典手段。对应的还有 <code>destructor</code>，在 main 结束后执行。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/d21fddb/</id>
    <link href="https://cosmical-container.github.io/posts/d21fddb/"/>
    <published>2025-03-02T12:06:00.000Z</published>
    <summary>
      <![CDATA[<p><code>__attribute__</code> 是 GCC 提供的编译器扩展语法，能给函数、变量、类型附加各种”特殊指令”。整理了几个日常实用的用法。</p>
<h2 id="一、mode：自定义精确定宽的整数"><a href="#一、mode：自定义精确定宽的整数]]>
    </summary>
    <title>C语言 __attribute__ 实用指南</title>
    <updated>2025-03-02T12:06:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p>收藏的一段著名的 C 代码，来自 <strong>IOCCC（国际 C 语言混乱代码大赛，The International Obfuscated C Code Contest）1988 年的获奖作品</strong>。整个程序长这样：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line">main(<span class="type">int</span> t,<span class="type">int</span> _,<span class="type">char</span>* a)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> !<span class="number">0</span>&lt;t?t&lt;<span class="number">3</span>?main(<span class="number">-79</span>,<span class="number">-13</span>,a+main(<span class="number">-87</span>,<span class="number">1</span>-_,</span><br><span class="line">    main(<span class="number">-86</span>,<span class="number">0</span>,a+<span class="number">1</span>)+a)):<span class="number">1</span>,t&lt;_?main(t+<span class="number">1</span>,_,a):<span class="number">3</span>,main(<span class="number">-94</span>,<span class="number">-27</span>+t,a)&amp;&amp;t==<span class="number">2</span>?_&lt;<span class="number">13</span>?</span><br><span class="line">    main(<span class="number">2</span>,_+<span class="number">1</span>,<span class="string">&quot;%s %d %d\n&quot;</span>):<span class="number">9</span>:<span class="number">16</span>:t&lt;<span class="number">0</span>?t&lt;<span class="number">-72</span>?main(_,t,</span><br><span class="line">    <span class="string">&quot;@n&#x27;+,#&#x27;/*&#123;&#125;w+/w#cdnr/+,&#123;&#125;r/*de&#125;+,/*&#123;*+,/w&#123;%+,/w#q#n+,/#&#123;l+,/n&#123;n+,/+#n+,/#;#q#n+,/+k#;*+,/&#x27;r :&#x27;d*&#x27;3,&#125;&#123;w+K w&#x27;K:&#x27;+&#125;e#&#x27;;dq#&#x27;l q#&#x27;+d&#x27;K#!/+k#;q#&#x27;r&#125;eKK#&#125;w&#x27;r&#125;eKK&#123;nl]&#x27;/#;#q#n&#x27;)&#123;)#&#125;w&#x27;)&#123;)&#123;nl]&#x27;/+#n&#x27;;d&#125;rw&#x27; i;# )&#123;nl]!/n&#123;n#&#x27;; r&#123;#w&#x27;r nc&#123;nl]&#x27;/#&#123;l,+&#x27;K &#123;rw&#x27; iK&#123;;[&#123;nl]&#x27;/w#q#n&#x27;wk nw&#x27; iwk&#123;KK&#123;nl]!/w&#123;%&#x27;l##w#&#x27; i; :&#123;nl]&#x27;/*&#123;q#&#x27;ld;r&#x27;&#125;&#123;nlwb!/*de&#125;&#x27;c ;;&#123;nl&#x27;-&#123;&#125;rw]&#x27;/+,&#125;##&#x27;*&#125;#nc,&#x27;,#nw]&#x27;/+kd&#x27;+e&#125;+;#&#x27;rdq#w! nr&#x27;/ &#x27;) &#125;+&#125;&#123;rl#&#x27;&#123;n&#x27; &#x27;)# &#125;&#x27;+&#125;##(!!/&quot;</span>)</span><br><span class="line">    :t&lt;<span class="number">-50</span>?_==*a?<span class="built_in">putchar</span>(a[<span class="number">31</span>]):main(<span class="number">-65</span>,_,a+<span class="number">1</span>):main((*a==<span class="string">&#x27;/&#x27;</span>)+t,_,a+<span class="number">1</span>)</span><br><span class="line">    :<span class="number">0</span>&lt;t?main(<span class="number">2</span>,<span class="number">2</span>,<span class="string">&quot;%s&quot;</span>):*a==<span class="string">&#x27;/&#x27;</span>||main(<span class="number">0</span>,main(<span class="number">-61</span>,*a,</span><br><span class="line">    <span class="string">&quot;!ek;dc i@bK&#x27;(q)-[w]*%n+r3#l,&#123;&#125;:\nuwloca-O;m .vpbks,fxntdCeghiry&quot;</span>),a+<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="它能干什么"><a href="#它能干什么" class="headerlink" title="它能干什么"></a>它能干什么</h2><p>编译运行，它会<strong>唱出《圣诞十二天》（The Twelve Days of Christmas）的全部歌词</strong>，从 “On the first day of Christmas my true love gave to me” 一直唱到第十二天。</p><h2 id="原理浅析"><a href="#原理浅析" class="headerlink" title="原理浅析"></a>原理浅析</h2><ul><li><code>main</code> 自己递归调用自己，参数 <code>t</code> 当状态机用：不同的 t 区间走不同的逻辑</li><li>那两串乱码字符串是<strong>加密的歌词和解码表</strong>——第二个字符串 <code>&quot;!ek;dc i@bK&#39;...&quot;</code> 其实是一张字符映射表，程序运行时逐字符查表还原出真正的歌词</li><li>嵌套的三目运算符 <code>?:</code> 完全取代了 if&#x2F;else 和循环</li></ul><h2 id="为什么值得看"><a href="#为什么值得看" class="headerlink" title="为什么值得看"></a>为什么值得看</h2><p>这是”代码是写给编译器看的还是写给人看的”这个问题的极端答案。它 demonstration 了三目运算符、递归、字符串查表的极限用法。自己写代码别学它，但它能把你对 C 语言表达式的理解拉高一个层次。</p><p>完整的解读文章网上有很多，感兴趣可以搜 “IOCCC 1988” 深入研究。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/f6f85ac1/</id>
    <link href="https://cosmical-container.github.io/posts/f6f85ac1/"/>
    <published>2025-02-26T06:54:00.000Z</published>
    <summary>
      <![CDATA[<p>收藏的一段著名的 C 代码，来自 <strong>IOCCC（国际 C 语言混乱代码大赛，The International Obfuscated C Code Contest）1988 年的获奖作品</strong>。整个程序长这样：</p>
<figure class=]]>
    </summary>
    <title>C语言之诗</title>
    <updated>2025-02-26T06:54:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="指针" scheme="https://cosmical-container.github.io/tags/%E6%8C%87%E9%92%88/"/>
    <content>
      <![CDATA[<p><code>const</code> 修饰的变量真的不能改吗？用指针强转试试就知道——答案是：<strong>至少局部变量可以</strong>。</p><h2 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> const_int = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;before :%d\n&quot;</span>, const_int);</span><br><span class="line">*(<span class="type">int</span> *)(&amp;const_int) = <span class="number">10</span>;              <span class="comment">// 强转掉 const 再写入</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;after  :%d\n&quot;</span>, const_int);      <span class="comment">// 输出 10！</span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> const_array[] = &#123;<span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0x03</span>, <span class="number">0x04</span>, <span class="number">0x05</span>&#125;;</span><br><span class="line">*((<span class="type">int</span> *)(&amp;const_array[<span class="number">0</span>] + i)) = <span class="number">10</span>;   <span class="comment">// 数组同理，逐个元素改掉</span></span><br></pre></td></tr></table></figure><p>运行结果 <code>before</code> 是 0，<code>after</code> 变成了 10——const 声明被绕过去了。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p><code>const</code> 只是<strong>编译期的约束</strong>，告诉编译器”不允许通过这个名字赋值”，它并不是内存的写保护。局部 const 变量放在栈上，和普通变量没有本质区别，所以把它的地址强转成普通指针后照改不误。</p><p>但注意：<strong>这个玩法有边界</strong>：</p><ul><li>全局&#x2F;静态 const 变量通常放在只读数据段（.rodata），强转写入会直接段错误</li><li>编译器可能对 const 做优化（比如把值内联到使用处），这时通过指针改内存，<code>printf</code> 读到的可能还是旧值——这就是未定义行为的”乐趣”</li></ul><h2 id="正经的用途"><a href="#正经的用途" class="headerlink" title="正经的用途"></a>正经的用途</h2><p>这种写法正经场合只有一个：<strong>调用不接受 const 的老接口</strong>时临时去掉限定（如 <code>free((void *)ptr)</code> 的历史遗留），改数据千万别这么干。真正的写保护要靠 MMU 的页权限，那才是硬件级别的 const。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/aaae083d/</id>
    <link href="https://cosmical-container.github.io/posts/aaae083d/"/>
    <published>2025-02-26T06:48:00.000Z</published>
    <summary>
      <![CDATA[<p><code>const</code> 修饰的变量真的不能改吗？用指针强转试试就知道——答案是：<strong>至少局部变量可以</strong>。</p>
<h2 id="实验"><a href="#实验" class="headerlink" title="实验"></a]]>
    </summary>
    <title>const 也能被修改</title>
    <updated>2025-02-26T06:48:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p><a href="https://github.com/EarlGray/c4">C4</a>（<strong>C in four functions</strong>）是 Robert Swierczek 写的一个极简主义练习：<strong>用大约 500 行 C 代码实现一个能运行 C 程序的虚拟机和解释器</strong>。整个项目就四个函数：<code>next()</code> 词法分析、<code>program()</code> 语法分析生成代码、<code>eval()</code> 虚拟机执行、<code>main()</code> 组装。</p><h2 id="它有多强"><a href="#它有多强" class="headerlink" title="它有多强"></a>它有多强</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">gcc -o c4 c4.c</span><br><span class="line">./c4 hello.c            <span class="comment"># 解释执行一个 C 程序</span></span><br><span class="line">./c4 -s hello.c         <span class="comment"># 输出汇编指令</span></span><br><span class="line">./c4 c4.c hello.c       <span class="comment"># 用 c4 解释运行 c4 自己，再由它运行 hello.c</span></span><br></pre></td></tr></table></figure><p>最后一行是它的名场面：<strong>c4 可以解释执行 c4 自己</strong>（甚至再套一层）， demonstrates 了”自举（self-hosting）”的概念。</p><h2 id="我做了什么"><a href="#我做了什么" class="headerlink" title="我做了什么"></a>我做了什么</h2><p>以 c4 为基础学习整个解释器的骨架：词法分析如何把源码切成 token、虚拟机的指令集（IMM&#x2F;ADJ&#x2F;PUSH&#x2F;JMP&#x2F;…）、符号表的组织。<code>my_gcc.c</code> 是学习过程中加入自己注释和修改的版本。</p><h2 id="为什么值得读"><a href="#为什么值得读" class="headerlink" title="为什么值得读"></a>为什么值得读</h2><ul><li>一份代码同时覆盖<strong>编译原理四大阶段</strong>：词法、语法、语义（生成虚拟机指令）、执行</li><li>没有任何依赖，纯 C，一个周末就能读完</li><li>读懂它之后再看 Lua 解释器、脚本引擎，结构都是似曾相识的</li></ul><p>注意：c4 原版使用 Linux&#x2F;Mac 的系统调用（open&#x2F;read&#x2F;write），Windows 下需要用 WSL 或 Cygwin 环境运行。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/2b594812/</id>
    <link href="https://cosmical-container.github.io/posts/2b594812/"/>
    <published>2024-11-26T15:33:00.000Z</published>
    <summary>
      <![CDATA[<p><a href="https://github.com/EarlGray/c4">C4</a>（<strong>C in four functions</strong>）是 Robert Swierczek 写的一个极简主义练习：<strong>用大约 500 行 C 代码]]>
    </summary>
    <title>c4：500 行代码的 C 语言解释器</title>
    <updated>2024-11-26T15:33:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="宏" scheme="https://cosmical-container.github.io/tags/%E5%AE%8F/"/>
    <content>
      <![CDATA[<p>研究 Boost.Preprocessor 的时候整理的一套”宏元编程”套路：<strong>用纯宏实现在编译期展开出 N 个变量</strong>。不涉及任何编译器扩展，纯标准 C 预处理器。</p><h2 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">BOOST_PP_ENUM_PARAMS</span><span class="params">(<span class="number">20</span>, a)</span>;</span><br><span class="line"><span class="comment">// 展开后：int a0, a1, a2, ... , a19;</span></span><br></pre></td></tr></table></figure><h2 id="坑一：拼接参数必须经过两层宏"><a href="#坑一：拼接参数必须经过两层宏" class="headerlink" title="坑一：拼接参数必须经过两层宏"></a>坑一：拼接参数必须经过两层宏</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_CAT_I(a, b) BOOST_PP_CAT_II(~, a##b)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_CAT_II(p, res) res</span></span><br></pre></td></tr></table></figure><p>为什么不能直接写 <code>a##b</code>？因为 <code>##</code> 会<strong>阻止参数展开</strong>——直接 <code>CAT(BOOST_PP_REPEAT_, 1)</code> 拼出来的是字面量 <code>BOOST_PP_REPEAT_1</code> 而不是它展开后的内容。标准解法就是延迟一层：先拼出 <code>~, BOOST_PP_REPEAT_1</code>，再扫描一轮时 <code>~</code> 被丢弃、参数正常展开。</p><h2 id="坑二：循环靠”手写递归表”"><a href="#坑二：循环靠”手写递归表”" class="headerlink" title="坑二：循环靠”手写递归表”"></a>坑二：循环靠”手写递归表”</h2><p>预处理器没有循环，只能靠<strong>宏递归展开</strong>模拟。但宏不能直接递归引用自己，所以每个编号的规则引用比它小一号的规则：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_REPEAT_1_1(m, d)  m(2, 0, d)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_REPEAT_1_2(m, d)  BOOST_PP_REPEAT_1_1(m, d) m(2, 1, d)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_REPEAT_1_3(m, d)  BOOST_PP_REPEAT_1_2(m, d) m(2, 2, d)</span></span><br><span class="line"><span class="comment">// ...一直写到 25</span></span><br></pre></td></tr></table></figure><p><code>REPEAT_1_3</code> 展开成 <code>REPEAT_1_2</code> 加上 <code>m(2,2,d)</code>，层层展开就得到了”调用 m 0 次、1 次、2 次”的序列。想支持多少次展开，就把表写多长。</p><h2 id="坑三：if-else-靠”查找表”"><a href="#坑三：if-else-靠”查找表”" class="headerlink" title="坑三：if&#x2F;else 靠”查找表”"></a>坑三：if&#x2F;else 靠”查找表”</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_BOOL_0 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_BOOL_1 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_BOOL_2 1</span></span><br><span class="line"><span class="comment">// ...所有数字枚举一遍</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_IIF_0(t, f) f</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_IIF_1(t, f) t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BOOST_PP_COMMA_IF(cond) BOOST_PP_IF(cond, BOOST_PP_COMMA, BOOST_PP_EMPTY)()</span></span><br></pre></td></tr></table></figure><p>条件为 0 还是 1，靠 <code>BOOST_PP_BOOL_数字</code> 这张查找表把任意数字归一化成 0&#x2F;1，再由 <code>IIF_0/IIF_1</code> 选择分支。<code>COMMA_IF</code> 则用来决定两个变量之间要不要补逗号（最后一个不加）。</p><h2 id="展开过程"><a href="#展开过程" class="headerlink" title="展开过程"></a>展开过程</h2><p><code>ENUM_PARAMS(20, a)</code> 最终展开为：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a0, a1, a2, a3, ..., a18, a19</span><br></pre></td></tr></table></figure><p>这套东西就是 Boost.Preprocessor 的核心原理，C++ 模板元编程出现之前，库作者全靠它生成重复代码。调试方法：<code>gcc -E</code> 看预处理结果，一切尽收眼底。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/3d37ddce/</id>
    <link href="https://cosmical-container.github.io/posts/3d37ddce/"/>
    <published>2024-11-17T06:05:00.000Z</published>
    <summary>
      <![CDATA[<p>研究 Boost.Preprocessor 的时候整理的一套”宏元编程”套路：<strong>用纯宏实现在编译期展开出 N 个变量</strong>。不涉及任何编译器扩展，纯标准 C 预处理器。</p>
<h2 id="目标"><a href="#目标" class="he]]>
    </summary>
    <title>宏定义的高级用法</title>
    <updated>2024-11-17T06:05:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="C++" scheme="https://cosmical-container.github.io/tags/C/"/>
    <category term="GCC" scheme="https://cosmical-container.github.io/tags/GCC/"/>
    <content>
      <![CDATA[<p>这是个整活项目，但整得非常认真：<strong>用 C 语言写出 C++ 的语法</strong>。目标包括 <code>new Animal(&quot;Tom&quot;, 1)</code> 这种”像话吗”的代码，甚至还有个山寨 <code>iostream</code> 头文件。项目分两代——第一代（2023 年 3 月）老老实实用结构体内嵌实现继承多态；第二代（2024 年 4 月起）开始放飞，靠 GCC 的两个扩展和几个胆大包天的宏，让 C 长出了 C++ 的样子。</p><h2 id="第一步：抹平关键字"><a href="#第一步：抹平关键字" class="headerlink" title="第一步：抹平关键字"></a>第一步：抹平关键字</h2><p>C 和 C++ 最表面的区别是一堆关键字。那就 <code>#define</code> 掉——项目里躺着一个名叫 <code>iostream</code> 的文件（对，没有扩展名），内容如下：</p><figure class="highlight c"><figcaption><span>iostream</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __iostream_h__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __iostream_h__</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Print.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> using</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> namespace</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> std</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> class struct</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> public</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> private</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> protected</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> template</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> new</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> delete</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p><code>class</code> 变 <code>struct</code>，<code>public</code>&#x2F;<code>private</code>&#x2F;<code>protected</code> 变空气，<code>template</code> 消失，<code>new</code>&#x2F;<code>delete</code> 变空——于是 <code>delete animal_01;</code> 展开之后就是一句无害的表达式语句 <code>animal_01;</code>。最妙的是里面那个 <code>#include &lt;Print.h&gt;</code>：我自己写的 <a href="/posts/21fb8eaf/">Print.h</a> 库会在这里派上大用场，伏笔先埋着。</p><h2 id="第二代：new-一个”对象”"><a href="#第二代：new-一个”对象”" class="headerlink" title="第二代：new 一个”对象”"></a>第二代：new 一个”对象”</h2><p>有了空 <code>new</code>，这句代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Animal animal_01 = new Animal(<span class="string">&quot;Tom&quot;</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>宏展开后其实是：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Animal animal_01 = Animal(<span class="string">&quot;Tom&quot;</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p><code>Animal(...)</code> 是什么？一个<strong>函数式宏</strong>，展开成一整个 GCC 语句表达式 <code>({ ... })</code>——花括号包起来的代码块，最后一个表达式的值就是整个表达式的值。里面干的事：造一个 <code>Animal</code> 变量、填名字和年龄、<strong>现场定义三个嵌套函数当方法</strong>、把它们挂到函数指针成员上、最后返回这个结构体：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Animal</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="type">char</span> *name;</span><br><span class="line"><span class="type">int</span> age;</span><br><span class="line"><span class="type">int</span> (*getAge)(<span class="type">void</span>);</span><br><span class="line"><span class="type">char</span> *(*getName)(<span class="type">void</span>);</span><br><span class="line"><span class="type">void</span> (*setAge)(<span class="type">int</span>);</span><br><span class="line">&#125; Animal;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Animal(nam, num) (&#123;   \</span></span><br><span class="line"><span class="meta">Animal animal;            \</span></span><br><span class="line"><span class="meta">animal.name = nam;        \</span></span><br><span class="line"><span class="meta">animal.age = num;         \</span></span><br><span class="line"><span class="meta">int getAge()              \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">return animal.age;    \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">char *getName()           \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">return animal.name;   \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">void setAge(int age)      \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">animal.age = age;     \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">animal.getAge = getAge;   \</span></span><br><span class="line"><span class="meta">animal.setAge = setAge;   \</span></span><br><span class="line"><span class="meta">animal.getName = getName; \</span></span><br><span class="line"><span class="meta">animal;                   \</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure><p>两个 GCC 扩展撑起了整场戏。<strong>语句表达式</strong> <code>({ ... })</code>：GNU C 允许把一串语句包成表达式，值取自最后一行——<code>animal;</code> 单独占一行就是干这个的。<strong>嵌套函数</strong>：函数里定义函数，而且能像闭包一样直接访问外层作用域的变量——<code>getAge</code> 的函数体里那个 <code>animal</code>，不是它自己的参数，是宏里定义的那个局部变量的引用。</p><p>所以”方法”的工作原理是：<code>animal.getAge</code> 存的函数指针指向一个闭包，闭包记住的是<strong>这一次宏展开时</strong>的那个 <code>animal</code>。<code>new</code> 两只动物，就得到两套各自独立的方法——<code>animal_01.getName()</code> 返回 Tom，<code>animal_02.getName()</code> 返回 Jerry，互不串台。</p><h2 id="cout：伏笔回收"><a href="#cout：伏笔回收" class="headerlink" title="cout：伏笔回收"></a>cout：伏笔回收</h2><p>第一代还玩了个更有意思的：<code>#define cout print</code>——用 Print.h 的多参数 print 冒充流式输出：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cout</span>(animal_01-&gt;getName(), <span class="string">&quot; age is &quot;</span>, animal_01-&gt;getAge(), <span class="string">&quot;\n&quot;</span>);</span><br></pre></td></tr></table></figure><p><code>cout</code> 展开成 <code>print</code>，而 print 早就支持最多十个混合类型参数（<a href="/posts/4c8a91b3/">Print.h 开发记（四）V1.4</a>里实现的按参数个数分发），<code>&lt;&lt;</code> 链式输出的神韵一下就出来了。这就是假 iostream 里 <code>#include &lt;Print.h&gt;</code> 的真正目的——整个山寨 C++ 标准库，实际依赖只有我自己的 print 库。</p><h2 id="第一代：正路的多态"><a href="#第一代：正路的多态" class="headerlink" title="第一代：正路的多态"></a>第一代：正路的多态</h2><p>放飞之前，第一代走的是教科书正路——结构体内嵌”基类”实现继承，函数指针表就是虚表：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    <span class="type">void</span> (*eat)();</span><br><span class="line">    <span class="type">void</span> (*sleep)();</span><br><span class="line">&#125; Animal;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Animal base;</span><br><span class="line">    <span class="type">char</span>* name;</span><br><span class="line">&#125; Dog;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Animal base;</span><br><span class="line">    <span class="type">char</span>* name;</span><br><span class="line">&#125; Cat;</span><br></pre></td></tr></table></figure><p>Dog 和 Cat 的第一个成员都是完整的 Animal，所以 <code>Dog*</code> 可以安全地强转回 <code>Animal*</code> 用——C 标准保证首成员地址就是结构体地址。各自的”方法”在 create 函数里挂上去：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Dog* <span class="title function_">dog_create</span><span class="params">(<span class="type">char</span>* name)</span> &#123;</span><br><span class="line">    Dog* dog = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Dog));</span><br><span class="line">    dog-&gt;base.eat = dog_eat;</span><br><span class="line">    dog-&gt;base.sleep = dog_sleep;</span><br><span class="line">    dog-&gt;name = name;</span><br><span class="line">    <span class="keyword">return</span> dog;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">perform</span><span class="params">(Animal* animal)</span> &#123;</span><br><span class="line">    animal-&gt;eat();</span><br><span class="line">    animal-&gt;sleep();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>perform</code> 只认 <code>Animal*</code>，不知道也不需要知道背后是狗是猫——<strong>多态的本质就是”通过函数指针表调用，调用者不查表”</strong>。C++ 编译器帮你生成的那张虚表，拆开看就这么大点地方。</p><h2 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h2><p>三个程序全部真实编译运行（GCC，<code>-std=gnu99</code>）。第二代成品：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Tom age is 1</span><br><span class="line">Jerry age is 2</span><br><span class="line">Tom age is 100</span><br><span class="line">Jerry age is 200</span><br></pre></td></tr></table></figure><p>第一代（cout 版）：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Tom age is 1</span><br><span class="line">Jerry age is 2</span><br><span class="line">_TOM_ age is 10</span><br><span class="line">_JERRY_ age is 20</span><br></pre></td></tr></table></figure><p>多态版：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">The dog is eating</span><br><span class="line">The dog is sleeping</span><br><span class="line">The cat is eating</span><br><span class="line">The cat is sleeping</span><br></pre></td></tr></table></figure><h2 id="复盘：这场魔术的两个破绽"><a href="#复盘：这场魔术的两个破绽" class="headerlink" title="复盘：这场魔术的两个破绽"></a>复盘：这场魔术的两个破绽</h2><p><strong>第一，闭包指着一块”退役”的内存。</strong> 语句表达式并没有真的开新栈帧——<code>({...})</code> 是内联在 main 的栈帧里的一块作用域。嵌套函数靠静态链访问的 <code>animal</code>，就是 main 栈帧里那个内层槽位。宏返回之后，这块槽位在逻辑上已经”退役”，但只要没人去踩，读它照样是对的——demo 里恰好没人踩。严格说这是未定义行为，真要给对象续命，<code>animal</code> 应该 malloc 出来、返回指针。第一代返回的就是指针，道理它懂，只是也没真的 malloc。</p><p><strong>第二，整套魔术只在 GCC 上活。</strong> 语句表达式和嵌套函数都是 GNU 扩展，MSVC 直接拒绝编译。想跨平台，就得退回第一代的正路：手写”构造函数”，手工挂函数指针。所以这个项目一统天下的结论是——<strong>第二代是新语法糖，第一代才是可移植的写法</strong>。另外多态版里还有个当时没发现的小笔误：<code>cat_create</code> 里 <code>malloc(sizeof(Dog))</code>，抄狗的尺寸给猫开内存——碰巧两个结构体布局完全相同所以无事发生，但布局一旦分叉就是越界。</p><h2 id="那这玩意儿有什么用"><a href="#那这玩意儿有什么用" class="headerlink" title="那这玩意儿有什么用"></a>那这玩意儿有什么用</h2><p>没有生产力上的用——真要写 C++ 直接开 <code>.cpp</code> 就好。但把”对象”亲手拆开重装一遍之后，有几个东西会变得特别清楚：<strong>成员函数就是多了一个隐藏参数的普通函数；虚表就是一张函数指针表；构造函数就是一个初始化结构体的语句块；this 指针就是那个被闭包记住的外层变量。</strong> C++ 把这些全部自动化了，而用 C 整活的这个下午，等于亲手当了一回编译器。</p><h2 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h2><figure class="highlight c"><figcaption><span>c-plusplus.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> new</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> delete</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Animal</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="type">char</span> *name;</span><br><span class="line"><span class="type">int</span> age;</span><br><span class="line"><span class="type">int</span> (*getAge)(<span class="type">void</span>);</span><br><span class="line"><span class="type">char</span> *(*getName)(<span class="type">void</span>);</span><br><span class="line"><span class="type">void</span> (*setAge)(<span class="type">int</span>);</span><br><span class="line">&#125; Animal;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Animal(nam, num) (&#123;   \</span></span><br><span class="line"><span class="meta">Animal animal;            \</span></span><br><span class="line"><span class="meta">animal.name = nam;        \</span></span><br><span class="line"><span class="meta">animal.age = num;         \</span></span><br><span class="line"><span class="meta">int getAge()              \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">return animal.age;    \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">char *getName()           \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">return animal.name;   \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">void setAge(int age)      \</span></span><br><span class="line"><span class="meta">&#123;                         \</span></span><br><span class="line"><span class="meta">animal.age = age;     \</span></span><br><span class="line"><span class="meta">&#125;                         \</span></span><br><span class="line"><span class="meta">animal.getAge = getAge;   \</span></span><br><span class="line"><span class="meta">animal.setAge = setAge;   \</span></span><br><span class="line"><span class="meta">animal.getName = getName; \</span></span><br><span class="line"><span class="meta">animal;                   \</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">Animal animal_01 = new Animal(<span class="string">&quot;Tom&quot;</span>, <span class="number">1</span>);</span><br><span class="line">Animal animal_02 = new Animal(<span class="string">&quot;Jerry&quot;</span>, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s age is %d\n&quot;</span>, animal_01.getName(), animal_01.getAge());</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s age is %d\n&quot;</span>, animal_02.getName(), animal_02.getAge());</span><br><span class="line"></span><br><span class="line">animal_01.setAge(<span class="number">100</span>);</span><br><span class="line">animal_02.setAge(<span class="number">200</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s age is %d\n&quot;</span>, animal_01.getName(), animal_01.getAge());</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s age is %d\n&quot;</span>, animal_02.getName(), animal_02.getAge());</span><br><span class="line"></span><br><span class="line"><span class="comment">// delete animal_01;</span></span><br><span class="line"><span class="comment">// delete animal_02;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><figcaption><span>多态调用.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    <span class="type">void</span> (*eat)();</span><br><span class="line">    <span class="type">void</span> (*sleep)();</span><br><span class="line">&#125; Animal;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Animal base;</span><br><span class="line">    <span class="type">char</span>* name;</span><br><span class="line">&#125; Dog;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Animal base;</span><br><span class="line">    <span class="type">char</span>* name;</span><br><span class="line">&#125; Cat;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">dog_eat</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;The dog is eating\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">dog_sleep</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;The dog is sleeping\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">cat_eat</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;The cat is eating\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">cat_sleep</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;The cat is sleeping\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Dog* <span class="title function_">dog_create</span><span class="params">(<span class="type">char</span>* name)</span> &#123;</span><br><span class="line">    Dog* dog = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Dog));</span><br><span class="line">    dog-&gt;base.eat = dog_eat;</span><br><span class="line">    dog-&gt;base.sleep = dog_sleep;</span><br><span class="line">    dog-&gt;name = name;</span><br><span class="line">    <span class="keyword">return</span> dog;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Cat* <span class="title function_">cat_create</span><span class="params">(<span class="type">char</span>* name)</span> &#123;</span><br><span class="line">    Cat* cat = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Dog));</span><br><span class="line">    cat-&gt;base.eat = cat_eat;</span><br><span class="line">    cat-&gt;base.sleep = cat_sleep;</span><br><span class="line">    cat-&gt;name = name;</span><br><span class="line">    <span class="keyword">return</span> cat;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">perform</span><span class="params">(Animal* animal)</span> &#123;</span><br><span class="line">    animal-&gt;eat();</span><br><span class="line">    animal-&gt;sleep();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    Dog* dog = dog_create(<span class="string">&quot;Fido&quot;</span>);</span><br><span class="line">    Cat* cat = cat_create(<span class="string">&quot;maomao&quot;</span>);</span><br><span class="line">    perform((Animal*)dog);</span><br><span class="line">    perform((Animal*)cat);</span><br><span class="line">    <span class="built_in">free</span>(dog);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/380f72c9/</id>
    <link href="https://cosmical-container.github.io/posts/380f72c9/"/>
    <published>2024-10-29T10:07:00.000Z</published>
    <summary>
      <![CDATA[<p>这是个整活项目，但整得非常认真：<strong>用 C 语言写出 C++ 的语法</strong>。目标包括 <code>new Animal(&quot;Tom&quot;, 1)</code> 这种”像话吗”的代码，甚至还有个山寨 <code>iostream</cod]]>
    </summary>
    <title>用 C 语言写 C++</title>
    <updated>2024-10-29T10:07:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p>挖到一个真正的”远古知识”：<strong>K&amp;R 风格的函数定义</strong>。这是 C 语言还没标准化的年代（K&amp;R 那本《The C Programming Language》第一版）的写法，现在的编译器为了兼容老代码依然支持。</p><h2 id="现代写法-vs-远古写法"><a href="#现代写法-vs-远古写法" class="headerlink" title="现代写法 vs 远古写法"></a>现代写法 vs 远古写法</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 现代写法（ANSI C / C89 之后）</span></span><br><span class="line"><span class="type">double</span> <span class="title function_">alt_style</span><span class="params">(<span class="type">int</span> a, <span class="type">double</span> *real)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> *real + a;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 远古写法（K&amp;R 风格）</span></span><br><span class="line"><span class="type">double</span> <span class="title function_">alt_style</span><span class="params">(a, real)</span></span><br><span class="line">    <span class="type">double</span> *real;</span><br><span class="line">    <span class="type">int</span> a;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> *real + a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>特征很明显：<strong>参数列表里只写参数名，参数类型声明放在函数体大括号之前</strong>。类型声明还可以乱序（先写 real 再写 a），因为它们只是”声明区”，和参数名按位置对应。</p><h2 id="为什么被淘汰了"><a href="#为什么被淘汰了" class="headerlink" title="为什么被淘汰了"></a>为什么被淘汰了</h2><p>K&amp;R 风格最大的问题是：<strong>不带原型</strong>。调用方在没有任何声明的情况下调用它，编译器不会检查参数个数和类型，传错了照样编译通过，运行时才出玄学问题。ANSI C 标准化之后引入了函数原型（prototype），编译器从此能在编译期帮你查参数，这种写法就进了博物馆。</p><p>现在还认识它的价值：读老项目、老书（K&amp;R 第一版）、还有 Linux 内核早期代码的时候不会懵。GCC 用 <code>-Wold-style-definition</code> 可以检查出这种写法。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/3e3bb6f0/</id>
    <link href="https://cosmical-container.github.io/posts/3e3bb6f0/"/>
    <published>2024-10-29T09:47:00.000Z</published>
    <summary>
      <![CDATA[<p>挖到一个真正的”远古知识”：<strong>K&amp;R 风格的函数定义</strong>。这是 C 语言还没标准化的年代（K&amp;R 那本《The C Programming Language》第一版）的写法，现在的编译器为了兼容老代码依然支持。</p>
<h2 i]]>
    </summary>
    <title>函数定义远古知识</title>
    <updated>2024-10-29T09:47:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p>最近学到几个 C 语言的冷知识，全是我平时踩坑或者见到的”怪现象”。挑几个有意思的分享一下，每个都能让你重新认识这门熟悉又陌生的语言。</p><h2 id="一、数组越界导致的死循环"><a href="#一、数组越界导致的死循环" class="headerlink" title="一、数组越界导致的死循环"></a>一、数组越界导致的死循环</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> i, <span class="built_in">array</span>[<span class="number">3</span>];</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n&quot;</span>, &amp;i);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n\n&quot;</span>, &amp;<span class="built_in">array</span>[<span class="number">3</span>]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= <span class="number">3</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">array</span>[i] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, i);</span><br></pre></td></tr></table></figure><p>这段代码是个经典炸弹：<code>array</code> 只有 3 个元素，循环却写到下标 3。诡异的是它可能<strong>不会崩溃，而是死循环</strong>——因为变量 <code>i</code> 和 <code>array</code> 在栈上相邻，<code>i</code> 恰好落在 <code>array[3]</code> 的位置上，每次 <code>array[3] = 0</code> 都会把循环变量 <code>i</code> 清零，<code>i</code> 永远到不了 4。</p><p>注意这个现象和变量定义顺序、编译器优化级别都有关系：开了 O2 之后编译器会重新排列栈空间，死循环可能就消失了。所以这类代码属于”未定义行为”，怎么表现都看编译器心情。</p><h2 id="二、有符号数和无符号数的比较"><a href="#二、有符号数和无符号数的比较" class="headerlink" title="二、有符号数和无符号数的比较"></a>二、有符号数和无符号数的比较</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> num1 = <span class="number">-1</span>;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> num2 = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (num1 &lt;= num2)</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;num2 is bigger\n&quot;</span>);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;num1 is bigger\n&quot;</span>);</span><br></pre></td></tr></table></figure><p>直觉上 -1 &lt;&#x3D; 1 毫无疑问，但这段代码会输出 <strong>num1 is bigger</strong>。C 语言规定：有符号和无符号整数比较时，有符号数会被<strong>隐式转换成无符号数</strong>。-1 转成无符号就是 4294967295（0xFFFFFFFF），当然比 1 大。</p><p>更隐蔽的是 <code>sizeof</code> 的返回值本身就是 <code>size_t</code>（无符号类型），所以 <code>if (a - 1 &lt; sizeof(int))</code> 这种写法在 a 为 0 时也会翻车——这是个埋在代码里的隐藏炸弹。</p><h2 id="三、restrict-关键字"><a href="#三、restrict-关键字" class="headerlink" title="三、restrict 关键字"></a>三、restrict 关键字</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">fun</span><span class="params">(<span class="type">int</span> *<span class="keyword">restrict</span> x, <span class="type">int</span> *<span class="keyword">restrict</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    *x = <span class="number">1</span>;</span><br><span class="line">    *y = <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">return</span> *x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>restrict</code> 是 C99 引入的类型限定符，用来向编译器承诺：这个指针是访问这块内存的<strong>唯一途径</strong>，没有别名。上面这个函数如果传两个相同的指针（<code>fun(&amp;num, &amp;num)</code>），行为就是未定义的。</p><p>它存在的意义是帮编译器优化：保证了没有别名之后，编译器可以放心地把读写重排序、缓存到寄存器，在高性能计算场景很常见。</p><h2 id="四、0-array-这种离谱的下标写法"><a href="#四、0-array-这种离谱的下标写法" class="headerlink" title="四、0[array] 这种离谱的下标写法"></a>四、0[array] 这种离谱的下标写法</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">int</span> a)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>[(<span class="number">1</span>[(<span class="type">int</span> [<span class="number">2</span>][<span class="number">1</span>])&#123;<span class="number">3</span>, a&#125;])];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">typeof</span>(f) *p = &amp;(**********(&amp;f));</span><br><span class="line">    (*&amp;<span class="built_in">printf</span>)(<span class="string">&quot;%d\n&quot;</span>, p(<span class="number">10</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这一坨能编译通过，而且逻辑等价于 <code>return a;</code>。拆开看：</p><ul><li><code>a[i]</code> 在 C 里的定义是 <code>*(a + i)</code>，所以加法交换律告诉我们 <code>i[a]</code> 和 <code>a[i]</code> 完全等价，<code>0[array]</code> 就是 <code>array[0]</code></li><li><code>(int [2][1]){3, a}</code> 是一个复合字面量，造出一个二维数组</li><li><code>typeof(f)</code> 直接把函数类型取出来声明函数指针</li><li><code>&amp;**********(&amp;f)</code> 对函数指针解引用十次——函数指针解引用会退化回函数指针，所以加多少个 <code>*</code> 都无所谓</li><li><code>(*&amp;printf)</code> 同理，取 printf 的地址再解引用，还是 printf</li></ul><h2 id="五、大小写转换的极简写法"><a href="#五、大小写转换的极简写法" class="headerlink" title="五、大小写转换的极简写法"></a>五、大小写转换的极简写法</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(a)</span></span><br><span class="line">&#123;</span><br><span class="line">    gets(&amp;a);</span><br><span class="line">    a ^= <span class="string">&#x27; &#x27;</span>;</span><br><span class="line">    <span class="built_in">puts</span>(&amp;a);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大小写字母的 ASCII 码只差第 5 位（0x20），所以 <code>a ^= &#39; &#39;</code> 一行就能完成大小写互换。配合老式的 K&amp;R 函数定义（参数不写类型，默认 int），四行代码搞定转换——当然 <code>gets</code> 这种函数现在是要进博物馆的危险品了。</p><h2 id="六、数组传参后的-sizeof-陷阱"><a href="#六、数组传参后的-sizeof-陷阱" class="headerlink" title="六、数组传参后的 sizeof 陷阱"></a>六、数组传参后的 sizeof 陷阱</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">func1</span><span class="params">(<span class="type">int</span> *<span class="built_in">array</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> num = <span class="keyword">sizeof</span>(<span class="built_in">array</span>) / <span class="keyword">sizeof</span>(<span class="built_in">array</span>[<span class="number">0</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">func2</span><span class="params">(<span class="type">int</span> (*<span class="built_in">array</span>)[n])</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> num = <span class="keyword">sizeof</span>(*<span class="built_in">array</span>) / <span class="keyword">sizeof</span>(**<span class="built_in">array</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, num);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>数组作为参数传进函数时会<strong>退化成指针</strong>，所以 <code>func1</code> 里的 <code>sizeof(array)</code> 得到的是指针大小（8），除以元素大小永远是 2——这就是”数组传参必须同时传长度”的原因。</p><p><code>func2</code> 展示了一个冷门技巧：用 VLA 语法的形参 <code>int (*array)[n]</code> 声明”指向 n 个 int 的数组的指针”，这样 <code>sizeof(*array)</code> 就能正确得到整个数组的大小了。</p><h2 id="七、函数参数的求值顺序"><a href="#七、函数参数的求值顺序" class="headerlink" title="七、函数参数的求值顺序"></a>七、函数参数的求值顺序</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">func1</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;1\n&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">func2</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;2\n&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d %d\n&quot;</span>, func1(), func2());</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码的输出可能是 <code>1 2</code>，也可能是 <code>2 1</code>——C 标准不保证函数参数的<strong>求值顺序</strong>。如果你以为一定是先算 func1 再算 func2，那遇到依赖调用顺序的代码就会莫名出错。所以永远不要在参数里写有副作用的表达式。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>这些冷知识平时写业务可能一辈子碰不到，但理解它们基本等于把 C 语言的内存模型、类型系统、求值规则重新学了一遍。笔记文件里还有几个，之后挖到有意思的继续更新。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/c01884e2/</id>
    <link href="https://cosmical-container.github.io/posts/c01884e2/"/>
    <published>2024-10-11T08:27:00.000Z</published>
    <summary>
      <![CDATA[<p>最近学到几个 C 语言的冷知识，全是我平时踩坑或者见到的”怪现象”。挑几个有意思的分享一下，每个都能让你重新认识这门熟悉又陌生的语言。</p>
<h2 id="一、数组越界导致的死循环"><a href="#一、数组越界导致的死循环" class="headerlink"]]>
    </summary>
    <title>C语言奇怪的冷知识</title>
    <updated>2024-10-11T08:27:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="算法" scheme="https://cosmical-container.github.io/tags/%E7%AE%97%E6%B3%95/"/>
    <category term="数据结构" scheme="https://cosmical-container.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <content>
      <![CDATA[<p>计算器是栈的教科书级应用。这个项目从 V1.0 迭代到 V1.2，最终版约 1100 行，从”能算 <code>1+2*3</code>“一路做到：<code>+ - * / ^</code> 五则运算、<code>{} [] ()</code> 三种括号混用、小数，甚至 <code>sin(1+sin(1+sin(1)))</code> 这样的嵌套函数调用——全部纯 C 手写。</p><h2 id="核心算法：两个栈消灭优先级"><a href="#核心算法：两个栈消灭优先级" class="headerlink" title="核心算法：两个栈消灭优先级"></a>核心算法：两个栈消灭优先级</h2><p>人脑算 <code>3 + 4 * 2</code> 时知道先乘除后加减，机器靠<strong>两个栈</strong>来消灭优先级问题：</p><ul><li><strong>数字栈</strong>：存放操作数</li><li><strong>符号栈</strong>：存放运算符，利用优先级决定何时计算</li></ul><p>整个算法分两步：</p><ol><li><strong>中缀转后缀</strong>：将 <code>3 + 4 * 2</code> 转为 <code>3 4 2 * +</code>（后缀表达式&#x2F;逆波兰式），后缀表达式不需要任何优先级判断</li><li><strong>后缀求值</strong>：逐个读 token，数字入栈，运算符弹出两个数计算后与压回，最后栈里剩下的就是答案</li></ol><h2 id="先造工具：My-Stack"><a href="#先造工具：My-Stack" class="headerlink" title="先造工具：My_Stack"></a>先造工具：My_Stack</h2><p>写计算器之前先造轮子。栈用 <code>calloc</code> 分配、<code>realloc</code> 扩容，<code>base</code>&#x2F;<code>top</code>&#x2F;<code>max</code> 三个指针管理一段连续内存：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">my_Stack</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    ll stack_size;</span><br><span class="line">    Elemtype* base;</span><br><span class="line">    Elemtype* top;</span><br><span class="line">    Elemtype* max;   </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><code>Elemtype</code> 用宏定义成 <code>int</code>，<code>ll</code> 是 <code>long int</code> 的别名——这套栈天生是为”下标入栈”准备的（后面会看到为什么）。压栈前检查满栈，满了就扩容一倍；弹栈前检查空栈：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">Push</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack, Elemtype ele)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack_full(Stack) == <span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(Stack_new(Stack,Stack-&gt;stack_size * <span class="number">2</span>) == <span class="literal">false</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    *Stack-&gt;top = ele;</span><br><span class="line">    Stack-&gt;top++;</span><br><span class="line">    <span class="keyword">return</span> Stack_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_弹栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return Elemtype </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Elemtype <span class="title function_">Pop</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack_empty(Stack) == <span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;弹栈失败:这个栈已经是空的了.\n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> ~(<span class="type">unsigned</span> <span class="type">int</span>)<span class="number">0</span>/<span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        Stack-&gt;top--;</span><br><span class="line">        <span class="keyword">return</span> *Stack-&gt;top;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>空栈弹栈的返回值 <code>~(unsigned int)0/2</code> 是 int 的最大值——用一个”不可能出现的数”当错误码，调用方拿到它就知道弹栈失败了。除了 Push&#x2F;Pop，还有 <code>Stack_init</code>（calloc 初始化）、<code>Stack_new</code>（realloc 扩容）、<code>Stack_empty</code>&#x2F;<code>Stack_full</code>、<code>Stack_clear</code>（top 指回 base）和 <code>Stack_destory</code>（free）一整套接口，全部配了 Doxygen 注释——这个习惯一直保留到了后来的 Print 项目。</p><h2 id="表达式在内存里长什么样"><a href="#表达式在内存里长什么样" class="headerlink" title="表达式在内存里长什么样"></a>表达式在内存里长什么样</h2><p>一个 token 要么是数字，要么是运算符，用两个指针表示：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ele</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">char</span> *p_opt;</span><br><span class="line">    <span class="type">double</span> *p_num;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>所有数字统一存进 <code>double num[100]</code>，运算符存进 <code>char opt[100]</code>，token 数组里只放指针。这样转后缀、求值的时候搬运的都是小小的 <code>struct ele</code>，不用拷贝数据本身。</p><h2 id="预处理：把表达式洗干净"><a href="#预处理：把表达式洗干净" class="headerlink" title="预处理：把表达式洗干净"></a>预处理：把表达式洗干净</h2><p>用户输入什么都有可能。入口函数 <code>calc()</code> 先做几件”洗数据”的活：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">double</span> <span class="title function_">calc</span><span class="params">(<span class="type">char</span> *str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">double</span> result = <span class="number">0</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ele</span> <span class="title">postfix_Expression</span>[100];</span></span><br><span class="line">    <span class="built_in">memset</span>(postfix_Expression, <span class="number">0</span>, <span class="keyword">sizeof</span>(postfix_Expression));</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> new_str[<span class="number">100</span>];</span><br><span class="line">    <span class="built_in">memset</span>(new_str, <span class="number">0</span>, <span class="keyword">sizeof</span>(new_str));</span><br><span class="line">    new_str[<span class="number">0</span>] = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    new_str[<span class="number">1</span>] = <span class="string">&#x27;+&#x27;</span>;</span><br><span class="line">    <span class="built_in">strcpy</span>(new_str+<span class="number">2</span>, str);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="keyword">sizeof</span>(new_str); i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(new_str[i] == <span class="string">&#x27;[&#x27;</span> || new_str[i] == <span class="string">&#x27;&#123;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            new_str[i] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(new_str[i]== <span class="string">&#x27;]&#x27;</span> || new_str[i] == <span class="string">&#x27;&#125;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            new_str[i] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    str_find_exp_and_calc(new_str);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;str_find_exp_and_calc : %s\n&quot;</span>, new_str);</span><br><span class="line"></span><br><span class="line">    mid_to_back(new_str, <span class="built_in">strlen</span>(new_str), postfix_Expression);</span><br><span class="line">    result = caculate_four_arithmetic_operations(postfix_Expression);</span><br><span class="line">    <span class="comment">// printf(&quot;result : %lf\n&quot;,result);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><strong>开头拼一个 <code>0+</code></strong>：<code>-1+2</code> 变成 <code>0-1+2</code>，负号从”一元运算符”降级成普通的二元减号，后面的算法完全不用为它设计特判</li><li><strong>三种括号归一化</strong>：<code>{</code> <code>[</code> 全换成 <code>(</code>，<code>}</code> <code>]</code> 全换成 <code>)</code>——对求值来说它们语义相同，没必要区别对待</li><li><strong>先算函数</strong>：<code>str_find_exp_and_calc</code> 会把表达式里所有 <code>sin(...)</code>、<code>sqrt(...)</code> 直接换成算好的数值（后面细讲），四则运算拿到的就永远是纯数字表达式</li></ul><p>接下来拆 token。先把每个字符标记成”符号&#x2F;非符号”的掩码数组 <code>opt_idx</code>，再把相邻的非符号位合并成一位——多位数字和小数点属于同一个数：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span> ; i &lt; len ; ++i)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(str[i] == <span class="string">&#x27;[&#x27;</span> || str[i] == <span class="string">&#x27;&#123;&#x27;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        str[i] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(str[i] == <span class="string">&#x27;]&#x27;</span> || str[i] == <span class="string">&#x27;&#125;&#x27;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        str[i] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(str[i] == <span class="string">&#x27;+&#x27;</span> || str[i] == <span class="string">&#x27;-&#x27;</span> || str[i] == <span class="string">&#x27;*&#x27;</span> || str[i] == <span class="string">&#x27;/&#x27;</span> || str[i] == <span class="string">&#x27;^&#x27;</span> || str[i] == <span class="string">&#x27;(&#x27;</span> || str[i] == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        opt[count_opt] = str[i];</span><br><span class="line">        opt_idx[i] = <span class="number">1</span>;</span><br><span class="line">        count_opt++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        opt_idx[i] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//合并多余的0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">99</span>; ++i)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(opt_idx[i] == <span class="number">0</span> &amp;&amp; opt_idx[i+<span class="number">1</span>] == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j = i; j &lt; <span class="number">99</span>; ++j)</span><br><span class="line">        &#123;</span><br><span class="line">            opt_idx[j] = opt_idx[j+<span class="number">1</span>];</span><br><span class="line">        &#125;</span><br><span class="line">        i--;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>数字本体用 <code>strtok</code> 按 <code>+-*/^()</code> 切开、<code>atof</code> 转 double：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">p = strtok(str, <span class="string">&quot;+-*/^()&quot;</span>);</span><br><span class="line">num[<span class="number">0</span>] = atof(p);</span><br><span class="line"><span class="keyword">while</span>((p = strtok(<span class="literal">NULL</span>, <span class="string">&quot;+-*/^()&quot;</span>)))</span><br><span class="line">&#123;</span><br><span class="line">    num[count_num++] = atof(p);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后把 num 数组和 opt 数组按掩码交错装进 <code>struct ele</code> 数组，中缀表达式就在内存里排好了队。</p><h2 id="中缀转后缀"><a href="#中缀转后缀" class="headerlink" title="中缀转后缀"></a>中缀转后缀</h2><p>遍历中缀表达式的每个 token，规则只有四条：</p><ul><li><strong>数字</strong> → 直接加入结果</li><li><strong>左括号 <code>(</code></strong> → 直接入符号栈</li><li><strong>右括号 <code>)</code></strong> → 不断弹栈进结果，直到碰见左括号</li><li><strong>运算符</strong> → 与栈顶比优先级：<strong>严格大于</strong>才入栈；否则不断弹栈进结果，直到能入栈为止</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">my_Stack</span> <span class="title">Stack_calculate</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ele</span> <span class="title">postfix_Expression</span>[100];</span></span><br><span class="line">ll count_postfix_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">Stack_init(&amp;Stack_calculate);</span><br><span class="line"><span class="type">char</span> opt_cal;</span><br><span class="line">ll opt_temp_idx = <span class="number">0</span>;</span><br><span class="line"><span class="type">char</span> opt_temp[<span class="number">100</span>];</span><br><span class="line"><span class="type">char</span> opt_top;</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化结构体数组为了防止后续引用报错</span></span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">&#123;</span><br><span class="line">    postfix_Expression[i].p_num = <span class="literal">NULL</span>;</span><br><span class="line">    postfix_Expression[i].p_opt = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(infix_expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        postfix_Expression[count_postfix_idx] = infix_expression[i];</span><br><span class="line">        count_postfix_idx++;</span><br><span class="line">        <span class="comment">//printf(&quot;数字: %ld 加入到后缀表达式\n&quot;,*(postfix_Expression[count_postfix_idx].p_num));</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(infix_expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        opt_cal = *infix_expression[i].p_opt;</span><br><span class="line">        <span class="keyword">if</span>( opt_cal == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>( opt_cal != <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//先判断优先级 大于栈顶的元素就可以入栈 一开始是空栈哦</span></span><br><span class="line">            <span class="keyword">if</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">true</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                opt_top = <span class="string">&#x27; &#x27;</span>;<span class="comment">//最低级的</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                opt_top = *(Stack_calculate.top<span class="number">-1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(Priority(opt_cal) &gt; Priority(opt_top))</span><br><span class="line">            &#123;</span><br><span class="line">                Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//小于等于栈顶的元素的话,要一直弹栈</span></span><br><span class="line">                <span class="keyword">while</span>(Priority(opt_cal) &lt;= Priority(opt_top))</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">//弹到最后没有元素了直接入栈欸</span></span><br><span class="line">                    <span class="keyword">if</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">true</span> || *(Stack_calculate.top<span class="number">-1</span>) == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    opt_top = Pop(&amp;Stack_calculate);</span><br><span class="line">                    opt_temp[opt_temp_idx] = opt_top;</span><br><span class="line">                    postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">                    postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">                    count_postfix_idx++;</span><br><span class="line">                    opt_temp_idx++;</span><br><span class="line">                &#125;</span><br><span class="line">                Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span><span class="comment">//如果是右括号</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//由于要匹配左括号，要一直弹栈直到出现左括号</span></span><br><span class="line">            <span class="type">char</span> char_tmp = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">while</span>(char_tmp != <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                char_tmp = Pop(&amp;Stack_calculate);</span><br><span class="line">                <span class="keyword">if</span>(char_tmp == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                opt_temp[opt_temp_idx] = char_tmp;</span><br><span class="line">                postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">                postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">                opt_temp_idx++;</span><br><span class="line">                count_postfix_idx++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//弹出栈的所有元素</span></span><br><span class="line"><span class="keyword">while</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">false</span>)</span><br><span class="line">&#123;</span><br><span class="line">    opt_top = Pop(&amp;Stack_calculate);</span><br><span class="line">    opt_temp[opt_temp_idx] = opt_top;</span><br><span class="line">    postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">    postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">    opt_temp_idx++;</span><br><span class="line">    count_postfix_idx++;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意比较用的是<strong>严格大于</strong>：同优先级的运算符会先把栈里的弹出来、再让新的入栈，保证 <code>8-3-2</code> 按 <code>(8-3)-2</code> 从左到右算——这是左结合。这里如果写成 <code>&gt;=</code>，连续的减法除法就会算反。循环结束后把符号栈里剩的符号全部弹进结果，中缀就变成了后缀。</p><h2 id="后缀求值：下标入栈"><a href="#后缀求值：下标入栈" class="headerlink" title="后缀求值：下标入栈"></a>后缀求值：下标入栈</h2><p>求值有个小障碍：栈的 <code>Elemtype</code> 是 <code>int</code>，要存的却是 <code>double</code>。把栈改成 double 也行，但这里的解法更轻：<strong>double 全放在 <code>num_tmp</code> 数组里，栈里只压下标</strong>。数字 token 到来时压的是它的下标；遇到运算符就弹两个下标，取数计算，结果追加到 <code>num_tmp</code> 尾部，再把新结果的”下标”压回去：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//记录储存的数组的最大值是多少,然后出栈之后就从这里开始</span></span><br><span class="line">ll max_idx = count;</span><br><span class="line">ll max_idx_count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(postfix_Expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        Push(&amp;Stack_calculate,count);</span><br><span class="line">        count++;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(postfix_Expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        ll num_1_idx = Pop(&amp;Stack_calculate);</span><br><span class="line">        ll num_2_idx = Pop(&amp;Stack_calculate);</span><br><span class="line">        <span class="keyword">switch</span> (*postfix_Expression[i].p_opt)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;^&#x27;</span>:</span><br><span class="line">            &#123;</span><br><span class="line">                num_tmp[max_idx+max_idx_count] = <span class="built_in">pow</span>(num_tmp[num_2_idx],num_tmp[num_1_idx]);</span><br><span class="line">                Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line">                max_idx_count++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>:</span><br><span class="line">            &#123;</span><br><span class="line">                num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] + num_tmp[num_1_idx];</span><br><span class="line">                Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line">                max_idx_count++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line">            &#123;</span><br><span class="line">                num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] - num_tmp[num_1_idx];</span><br><span class="line">                Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line">                max_idx_count++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>:</span><br><span class="line">            &#123;</span><br><span class="line">                num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] * num_tmp[num_1_idx];</span><br><span class="line">                Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line">                max_idx_count++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;/&#x27;</span>:</span><br><span class="line">            &#123;</span><br><span class="line">                num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] / num_tmp[num_1_idx];</span><br><span class="line">                Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line">                max_idx_count++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>有个容易翻车的细节：<strong>先弹出的是右操作数</strong>。减法、除法、幂都不满足交换律，<code>num_2_idx</code> 和 <code>num_1_idx</code> 的顺序反了，<code>8/2</code> 就变 <code>2/8</code> 了。全部 token 走完，栈里剩下的那个下标指向的就是最终答案。</p><h2 id="数学函数：函数指针查找表-递归"><a href="#数学函数：函数指针查找表-递归" class="headerlink" title="数学函数：函数指针查找表 + 递归"></a>数学函数：函数指针查找表 + 递归</h2><p><code>sin(3.1415926/2)</code> 这种怎么算？思路是<strong>在四则运算之前，先把表达式里的函数调用全部换成数值</strong>。<code>str_find_exp_and_calc</code> 从左到右扫描，看到 <code>(</code> 且前一个字符不是运算符也不是另一个 <code>(</code>——说明这个括号头上挂着函数名。函数名是倒着往回抠的，抠完把字符串反转回来；参数则用计数器做括号匹配：遇到 <code>(</code> 加一、遇到 <code>)</code> 减一，归零时正好是最外层右括号——不管参数里嵌了多少层括号都能完整抠出来。</p><p>15 个数学函数的”查找表”，是一串 strcmp 加函数指针：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="title function_">double</span> <span class="params">(*math_fun)</span><span class="params">(<span class="type">double</span>)</span>;</span><br><span class="line">math_fun <span class="title function_">get_math_fun</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sin&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sin</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cos&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">cos</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tan&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">tan</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ……sinh/cosh/tanh/asin/acos/atan/exp/log/log10/sqrt/ceil/floor 分支同构，略</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>参数表达式里可能还嵌着函数，所以直接<strong>递归调用 calc()</strong>：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">strcpy</span>(str_temp, exp_strs[i]);</span><br><span class="line"><span class="type">double</span> temp = calc(exp_strs[i]);</span><br><span class="line"><span class="built_in">strcpy</span>(exp_strs[i], str_temp);</span><br><span class="line"></span><br><span class="line"><span class="type">char</span> *temp_result_str = double2str(get_math_fun(exp_funcs[i])(temp));</span><br></pre></td></tr></table></figure><p><code>sin(1+sin(1+sin(1)))</code> 就是这样一层层递归进去、一层层替换回来的。算出的结果替换回原字符串时还有个细节：<strong>负数结果要包成 <code>(0-x)</code></strong>，否则替换回去的一元负号会让后面的四则运算翻车：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(exp_strStuct.exp_results[i][<span class="number">0</span>] == <span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *temp = exp_strStuct.exp_results[i];</span><br><span class="line">    exp_strStuct.exp_results[i] = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(exp_strStuct.exp_results[i]) + <span class="number">3</span>));</span><br><span class="line">    <span class="built_in">memset</span>(exp_strStuct.exp_results[i], <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(exp_strStuct.exp_results[i]) + <span class="number">3</span>));</span><br><span class="line"></span><br><span class="line">    exp_strStuct.exp_results[i][<span class="number">0</span>] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">    exp_strStuct.exp_results[i][<span class="number">1</span>] = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    <span class="built_in">strcpy</span>(exp_strStuct.exp_results[i] + <span class="number">2</span>, temp);</span><br><span class="line">    exp_strStuct.exp_results[i][<span class="built_in">strlen</span>(exp_strStuct.exp_results[i])] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">free</span>(temp);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// printf(&quot;|temp|: %s\n&quot;, temp);</span></span><br><span class="line"><span class="comment">// printf(&quot;|exp_strStuct.exp_results[i]|: %s\n&quot;, exp_strStuct.exp_results[i]);</span></span><br></pre></td></tr></table></figure><h2 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h2><figure class="highlight c"><figcaption><span>main.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;calc.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> <span class="built_in">exp</span>[<span class="number">100</span>] = <span class="string">&quot;1+2*3/4&quot;</span>;</span><br><span class="line">    <span class="comment">// char exp[100] = &quot;(1.1+(1.1+(1.1+(1.1+(1.1+(1.1+1.1))))))&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;1+2*3/4+&#123;1+[2*(1+1)]&#125;+3&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;3+45/2+&#123;7+[8*(3+1)]&#125;-6&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;4*(3-2)+&#123;8/[4*(2-1)]&#125;+9&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;1+23/4+&#123;5+[6*(2+1)]&#125;-7&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;5/2+3*&#123;2-[5/(2-1)]&#125;+4&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;23-4/2+&#123;1+[4*(3-1)]&#125;+5&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;2.53-4.1/2.5+&#123;1.3+[4.7*(3.2-1.1)]&#125;+5.8&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;5.6/2.1+3.4*&#123;2.8-[5.3/(2.2-1.1)]&#125;+4.9&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;1+2*(3+4)+0.000000+(0-1.000000)+10.000000+1.000000+2.718282&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;1+2*(3+4)+sin(3.1415926/2)+cos(3.1415926)+sqrt(100)+log10(10)+exp(1)&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;1+sin(1+sin(1+sin(1+sin(1))))&quot;;</span></span><br><span class="line">    <span class="comment">// char exp[100] = &quot;sin[3.1415926/2]&quot;;</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%lf\n&quot;</span>,calc(<span class="built_in">exp</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试用的表达式一直留在注释里，从整数四则、三种括号混用，到 <code>sin/cos/sqrt/log10/exp</code> 嵌套调用，就是这个项目能力范围的一份清单。</p><h2 id="版本迭代"><a href="#版本迭代" class="headerlink" title="版本迭代"></a>版本迭代</h2><ul><li><strong>V1.0</strong>：<code>calc.c + My_Stack.c/.h</code> 跑通主流程——双栈转后缀、下标入栈求值，配了一个”多文件编译并链接”的 .bat 脚本</li><li><strong>V1.1</strong>：测试验证版，代码和 V1.0 基本一致，主要在换测试用例（比如 <code>1/(9)</code>）做边界验证</li><li><strong>V1.2</strong>：模块化整理——<code>struct ele</code> 和调试宏抽进 <code>calc.h</code>；新增 <code>func_calc.c/.h</code> 数学函数模块（函数指针表 + 括号匹配 + 递归求值）；顺手修了 <code>num[]</code> 未初始化、掩码合并循环边界（50→99）两个小 bug</li></ul><h2 id="V1-2-完整代码"><a href="#V1-2-完整代码" class="headerlink" title="V1.2 完整代码"></a>V1.2 完整代码</h2><p>项目一共 7 个文件，main.c 已在上面贴出，其余 6 个按依赖顺序全部贴出。</p><h3 id="My-Stack-h"><a href="#My-Stack-h" class="headerlink" title="My_Stack.h"></a>My_Stack.h</h3><figure class="highlight c"><figcaption><span>My_Stack.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _My_Stack_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _My_Stack_H_</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Elemtype int </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> bool char</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Stack_OK 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> true 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> false 0</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> init_element_num 100</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> init_error -1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">long</span> <span class="type">int</span> ll;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">my_Stack</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    ll stack_size;</span><br><span class="line">    Elemtype* base;</span><br><span class="line">    Elemtype* top;</span><br><span class="line">    Elemtype* max;   </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_init</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_new</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack, ll num)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_destory</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"></span><br><span class="line">ll <span class="title function_">Stack_get_len</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_empty</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_full</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">Stack_clear</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">Push</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack, Elemtype ele)</span>;</span><br><span class="line"></span><br><span class="line">Elemtype <span class="title function_">Pop</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h3 id="My-Stack-c"><a href="#My-Stack-c" class="headerlink" title="My_Stack.c"></a>My_Stack.c</h3><figure class="highlight c"><figcaption><span>My_Stack.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;My_Stack.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_初始化</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_init</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    Stack-&gt;base = (Elemtype *)<span class="built_in">calloc</span>(<span class="keyword">sizeof</span>(Elemtype),init_element_num);</span><br><span class="line">    <span class="keyword">if</span>(Stack-&gt;base == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> init_error;</span><br><span class="line">    &#125;</span><br><span class="line">    Stack-&gt;stack_size = init_element_num;</span><br><span class="line">    Stack-&gt;top = Stack-&gt;base;</span><br><span class="line">    Stack-&gt;max = Stack-&gt;base + init_element_num;</span><br><span class="line">    <span class="keyword">return</span> Stack_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_设置新的栈长度</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @param num </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_new</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack, ll num)</span></span><br><span class="line">&#123;</span><br><span class="line">    Stack-&gt;base = (Elemtype *)<span class="built_in">realloc</span>(Stack-&gt;base,<span class="keyword">sizeof</span>(Elemtype) * num);</span><br><span class="line">    <span class="keyword">if</span>(Stack-&gt;base == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Stack-&gt;stack_size = num;</span><br><span class="line">    Stack-&gt;max = Stack-&gt;base + num;</span><br><span class="line">    <span class="keyword">return</span> Stack_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_获取长度</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return ll </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">ll <span class="title function_">Stack_get_len</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> Stack-&gt;stack_size;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_判断是否为空栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_empty</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack-&gt;top == Stack-&gt;base)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_判断是否为满栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_full</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack-&gt;top == Stack-&gt;max)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_清空栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Stack_clear</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    Stack-&gt;top = Stack-&gt;base;</span><br><span class="line">    <span class="keyword">return</span> ;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief ջ_ѹջ</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @param ele </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Push</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack, Elemtype ele)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack_full(Stack) == <span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(Stack_new(Stack,Stack-&gt;stack_size * <span class="number">2</span>) == <span class="literal">false</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    *Stack-&gt;top = ele;</span><br><span class="line">    Stack-&gt;top++;</span><br><span class="line">    <span class="keyword">return</span> Stack_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_弹栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return Elemtype </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Elemtype <span class="title function_">Pop</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(Stack_empty(Stack) == <span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;弹栈失败:这个栈已经是空的了.\n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> ~(<span class="type">unsigned</span> <span class="type">int</span>)<span class="number">0</span>/<span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        Stack-&gt;top--;</span><br><span class="line">        <span class="keyword">return</span> *Stack-&gt;top;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 栈_销毁栈</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @param Stack </span></span><br><span class="line"><span class="comment"> * @return true </span></span><br><span class="line"><span class="comment"> * @return false </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">bool</span> <span class="title function_">Stack_destory</span><span class="params">(<span class="keyword">struct</span> my_Stack* Stack)</span></span><br><span class="line">&#123;</span><br><span class="line">    Stack-&gt;top = Stack-&gt;base;</span><br><span class="line">    <span class="built_in">free</span>(Stack-&gt;base);</span><br><span class="line">    Stack-&gt;stack_size = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> Stack_OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 测试代码</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @return int </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// int main()</span></span><br><span class="line"><span class="comment">// &#123;</span></span><br><span class="line"><span class="comment">//     struct my_Stack stack;</span></span><br><span class="line"><span class="comment">//     Stack_init(&amp;stack);</span></span><br><span class="line"><span class="comment">//     Pop(&amp;stack);</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack, 123);</span></span><br><span class="line"><span class="comment">//     printf(&quot;%d\n&quot;,Pop(&amp;stack));</span></span><br><span class="line"><span class="comment">//     Stack_clear(&amp;stack);</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack,456);</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack,789);</span></span><br><span class="line"><span class="comment">//     printf(&quot;Stack_len:%ld\n&quot;,Stack_get_len(&amp;stack));</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack,789);</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack,789);</span></span><br><span class="line"><span class="comment">//     Push(&amp;stack,789);</span></span><br><span class="line"><span class="comment">//     printf(&quot;Stack_len:%ld\n&quot;,Stack_get_len(&amp;stack));</span></span><br><span class="line"><span class="comment">//     Stack_destory(&amp;stack);</span></span><br><span class="line"><span class="comment">//     return 0;</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br></pre></td></tr></table></figure><h3 id="calc-h"><a href="#calc-h" class="headerlink" title="calc.h"></a>calc.h</h3><figure class="highlight c"><figcaption><span>calc.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _calc_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _calc_H_</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;My_Stack.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;func_calc.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> cal_max_idx 100    <span class="comment">//用于储存计算信息的最大数组</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #define input</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> details</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> details_Stack</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> details_array</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ele</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">char</span> *p_opt;</span><br><span class="line">    <span class="type">double</span> *p_num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="title function_">calc</span><span class="params">(<span class="type">char</span> *str)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h3 id="func-calc-h"><a href="#func-calc-h" class="headerlink" title="func_calc.h"></a>func_calc.h</h3><figure class="highlight c"><figcaption><span>func_calc.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _Func_calc_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _Func_calc_H_</span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">str_find_exp_and_calc</span><span class="params">(<span class="type">char</span> *s)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h3 id="func-calc-c"><a href="#func-calc-c" class="headerlink" title="func_calc.c"></a>func_calc.c</h3><figure class="highlight c"><figcaption><span>func_calc.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;func_calc.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;calc.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> *<span class="title function_">str_rpl</span><span class="params">(<span class="type">char</span> *s, <span class="type">const</span> <span class="type">char</span> *s1, <span class="type">const</span> <span class="type">char</span> *s2)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *ptr;</span><br><span class="line">    <span class="keyword">while</span> ((ptr = <span class="built_in">strstr</span>(s, s1)) != <span class="literal">NULL</span>)     <span class="comment">/* 如果在s中找到s1 */</span></span><br><span class="line">    &#123;</span><br><span class="line">        memmove(ptr + <span class="built_in">strlen</span>(s2) , ptr + <span class="built_in">strlen</span>(s1), <span class="built_in">strlen</span>(ptr) - <span class="built_in">strlen</span>(s1) + <span class="number">1</span>);</span><br><span class="line">        <span class="built_in">memcpy</span>(ptr, &amp;s2[<span class="number">0</span>], <span class="built_in">strlen</span>(s2));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">str_reverse</span><span class="params">(<span class="type">char</span>* s)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span>* left = s;</span><br><span class="line"><span class="type">char</span>* right = s + <span class="built_in">strlen</span>(s) - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (left &lt; right)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> tmp = *left;</span><br><span class="line">*left = *right;</span><br><span class="line">*right = tmp;</span><br><span class="line"></span><br><span class="line">left++;</span><br><span class="line">right--;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">double2str</span><span class="params">(<span class="type">double</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// double：8字节 2.3E-308 到 1.7E+308</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">21</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%lf&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="title function_">str2double</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> atof(str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="title function_">double</span> <span class="params">(*math_fun)</span><span class="params">(<span class="type">double</span>)</span>;</span><br><span class="line">math_fun <span class="title function_">get_math_fun</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sin&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sin</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cos&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">cos</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tan&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">tan</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sinh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sinh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cosh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">cosh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tanh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">tanh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;asin&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">asin</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;acos&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">acos</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;atan&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">atan</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;exp&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">exp</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;log&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">log</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;log10&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">log10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sqrt&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sqrt</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;ceil&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">ceil</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;floor&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">floor</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">int</span> exp_count;</span><br><span class="line">    <span class="type">char</span> **exp_funcs;</span><br><span class="line">    <span class="type">char</span> **exp_strs;</span><br><span class="line">    <span class="type">char</span> **exp_results;</span><br><span class="line">&#125;exp_strStuctTypedef;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">str_find_exp_and_calc</span><span class="params">(<span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *ptr = s;              <span class="comment">//遍历整个字符串的指针</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> exp_func[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;    <span class="comment">//储存当前找到的表达式函数</span></span><br><span class="line">    <span class="type">int</span>  exp_func_i = <span class="number">0</span>;        <span class="comment">//当前找到的表达式函数数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_funcs = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式函数</span></span><br><span class="line">    <span class="type">int</span>  exp_funcs_i = <span class="number">0</span>;       <span class="comment">//所有找到的表达式函数数组的下标</span></span><br><span class="line">    <span class="built_in">memset</span>(exp_funcs, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> exp_str[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;    <span class="comment">//储存当前找到的表达式</span></span><br><span class="line">    <span class="type">int</span>  exp_str_i = <span class="number">0</span>;         <span class="comment">//当前找到的表达式数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_strs = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式</span></span><br><span class="line">    <span class="type">int</span>  exp_strs_i = <span class="number">0</span>;        <span class="comment">//所有找到的表达式数组的下标</span></span><br><span class="line">    <span class="built_in">memset</span>(exp_strs, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// char exp_result[100] = &#123;0&#125;;    //储存当前表达式的计算值</span></span><br><span class="line">    <span class="comment">// int  exp_result_i = 0;         //当前找到的表达式计算值数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_results = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式的计算值</span></span><br><span class="line">    <span class="comment">// int  exp_results_i = 0;        //所有找到的表达式计算值数组的下标</span></span><br><span class="line">    <span class="comment">// memset(exp_results, 0, sizeof(char *) * 100);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// printf(&quot;str: %s\n&quot;, s);</span></span><br><span class="line">    <span class="keyword">while</span> (*ptr++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> ((*ptr == <span class="string">&#x27;(&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;(&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;+&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;-&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;*&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;/&#x27;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// printf(&quot;find exp: &quot;);</span></span><br><span class="line">            <span class="type">char</span> *exp_func_ptr = ptr;</span><br><span class="line">            <span class="keyword">while</span>(*exp_func_ptr--)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>((*exp_func_ptr != <span class="string">&#x27;+&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;-&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;*&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;/&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;(&#x27;</span>))</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">// putchar(*exp_func_ptr);</span></span><br><span class="line">                    exp_func[exp_func_i++] = *exp_func_ptr;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    exp_func[exp_func_i] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">                    exp_funcs[exp_funcs_i] = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (exp_func_i + <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">strcpy</span>(exp_funcs[exp_funcs_i], exp_func);</span><br><span class="line">                    str_reverse(exp_funcs[exp_funcs_i]);</span><br><span class="line">                    exp_funcs_i++;</span><br><span class="line">                    </span><br><span class="line">                    <span class="built_in">memset</span>(exp_func, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line">                    exp_func_i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> count_kuohao = <span class="number">-1</span>;</span><br><span class="line">            <span class="type">char</span> *sub_str_ptr = ptr + <span class="number">1</span>;</span><br><span class="line">            <span class="built_in">memset</span>(exp_str, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">do</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">// 括号匹配</span></span><br><span class="line">                <span class="keyword">if</span>(*sub_str_ptr == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    count_kuohao++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span>(*sub_str_ptr == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    count_kuohao--;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 退出条件</span></span><br><span class="line">                <span class="keyword">if</span>(count_kuohao != <span class="number">0</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">// putchar(*sub_str_ptr);</span></span><br><span class="line">                    exp_str[exp_str_i++] = *sub_str_ptr;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    exp_str[exp_str_i] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">                    <span class="comment">// printf(&quot;find exp: |%s|\n&quot;, exp_str);</span></span><br><span class="line">                    exp_strs[exp_strs_i] = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (exp_str_i + <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">strcpy</span>(exp_strs[exp_strs_i], exp_str);</span><br><span class="line">                    <span class="comment">// printf(&quot;find exp: |%s|\n&quot;, exp_strs[exp_strs_i]);</span></span><br><span class="line">                    exp_strs_i++;</span><br><span class="line">                    </span><br><span class="line">                    <span class="built_in">memset</span>(exp_str, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line">                    exp_str_i = <span class="number">0</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;<span class="keyword">while</span>(*sub_str_ptr++);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; exp_strs_i; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">char</span> str_temp[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        <span class="comment">// 使用calc计算会对原来表达式进行修改</span></span><br><span class="line">        <span class="built_in">strcpy</span>(str_temp, exp_strs[i]);</span><br><span class="line">        <span class="type">double</span> temp = calc(exp_strs[i]);</span><br><span class="line">        <span class="built_in">strcpy</span>(exp_strs[i], str_temp);</span><br><span class="line"></span><br><span class="line">        <span class="type">char</span> *temp_result_str = double2str(get_math_fun(exp_funcs[i])(temp));</span><br><span class="line">        exp_results[i] = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(temp_result_str) + <span class="number">1</span>));</span><br><span class="line">        <span class="built_in">strcpy</span>(exp_results[i], temp_result_str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    exp_strStuctTypedef exp_strStuct;</span><br><span class="line">    exp_strStuct.exp_count = exp_strs_i;</span><br><span class="line">    exp_strStuct.exp_funcs = exp_funcs;</span><br><span class="line">    exp_strStuct.exp_strs = exp_strs;</span><br><span class="line">    exp_strStuct.exp_results = exp_results;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; exp_strStuct.exp_count; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// printf(&quot;find exp_func %s, parameters is %s, result is %s\n&quot;, exp_strStuct.exp_funcs[i], exp_strStuct.exp_strs[i], exp_strStuct.exp_results[i]);</span></span><br><span class="line">        <span class="type">char</span> temp[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        <span class="built_in">strcpy</span>(temp, exp_strStuct.exp_funcs[i]);</span><br><span class="line">        <span class="built_in">strcat</span>(temp, <span class="string">&quot;(&quot;</span>);</span><br><span class="line">        <span class="built_in">strcat</span>(temp, exp_strStuct.exp_strs[i]);</span><br><span class="line">        <span class="built_in">strcat</span>(temp, <span class="string">&quot;)&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(exp_strStuct.exp_results[i][<span class="number">0</span>] == <span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">char</span> *temp = exp_strStuct.exp_results[i];</span><br><span class="line">            exp_strStuct.exp_results[i] = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(exp_strStuct.exp_results[i]) + <span class="number">3</span>));</span><br><span class="line">            <span class="built_in">memset</span>(exp_strStuct.exp_results[i], <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(exp_strStuct.exp_results[i]) + <span class="number">3</span>));</span><br><span class="line"></span><br><span class="line">            exp_strStuct.exp_results[i][<span class="number">0</span>] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">            exp_strStuct.exp_results[i][<span class="number">1</span>] = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">            <span class="built_in">strcpy</span>(exp_strStuct.exp_results[i] + <span class="number">2</span>, temp);</span><br><span class="line">            exp_strStuct.exp_results[i][<span class="built_in">strlen</span>(exp_strStuct.exp_results[i])] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">free</span>(temp);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// printf(&quot;|temp|: %s\n&quot;, temp);</span></span><br><span class="line">        <span class="comment">// printf(&quot;|exp_strStuct.exp_results[i]|: %s\n&quot;, exp_strStuct.exp_results[i]);</span></span><br><span class="line">        str_rpl(s, temp, exp_strStuct.exp_results[i]);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// int main()</span></span><br><span class="line"><span class="comment">// &#123;</span></span><br><span class="line"><span class="comment">//     // char str[100] = &quot;1+2*(3+4)+sin(1+8+(2+3))+cos((2+3)*8+2*(3+4))&quot;;</span></span><br><span class="line"><span class="comment">//     char str[100] = &quot;1+2*(3+4)+sin(3.1415926)+cos(3.1415926)+sqrt(100)+log10(10)+exp(1)&quot;;</span></span><br><span class="line"><span class="comment">//     exp_strStuctTypedef exp_strStuct = str_find_exp_and_calc(str);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//     printf(&quot;result : %s\n&quot;, str);</span></span><br><span class="line"><span class="comment">//     return 0;</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br></pre></td></tr></table></figure><h3 id="calc-c"><a href="#calc-c" class="headerlink" title="calc.c"></a>calc.c</h3><figure class="highlight c"><figcaption><span>calc.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;My_Stack.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;func_calc.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;calc.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">Priority</span><span class="params">(<span class="type">char</span> ch)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">switch</span>(ch)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;^&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">3</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>:</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;/&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>:</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> <span class="title function_">mid_to_back</span><span class="params">(<span class="type">char</span> *str, ll len, <span class="keyword">struct</span> ele result[])</span></span><br><span class="line">&#123;</span><br><span class="line">    ll i;</span><br><span class="line">    ll count_num = <span class="number">1</span>;</span><br><span class="line">    ll count_opt = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> *p;</span><br><span class="line">    <span class="type">double</span> num[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="type">char</span> opt[<span class="number">100</span>] = &#123;<span class="string">&#x27;\0&#x27;</span>&#125;;</span><br><span class="line">    <span class="type">long</span> opt_idx[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">    ll count_num_idx = <span class="number">0</span>;</span><br><span class="line">    ll count_opt_idx = <span class="number">0</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ele</span> <span class="title">infix_expression</span>[100];</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化idx数组</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="keyword">sizeof</span>(opt_idx)/<span class="keyword">sizeof</span>(opt_idx[<span class="number">0</span>]); ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        opt_idx[i] = <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//初始化中缀表达式的结构体数组</span></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        infix_expression[i].p_num = <span class="literal">NULL</span>;</span><br><span class="line">        infix_expression[i].p_opt = <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span> ; i &lt; len ; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(str[i] == <span class="string">&#x27;[&#x27;</span> || str[i] == <span class="string">&#x27;&#123;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            str[i] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(str[i] == <span class="string">&#x27;]&#x27;</span> || str[i] == <span class="string">&#x27;&#125;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            str[i] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(str[i] == <span class="string">&#x27;+&#x27;</span> || str[i] == <span class="string">&#x27;-&#x27;</span> || str[i] == <span class="string">&#x27;*&#x27;</span> || str[i] == <span class="string">&#x27;/&#x27;</span> || str[i] == <span class="string">&#x27;^&#x27;</span> || str[i] == <span class="string">&#x27;(&#x27;</span> || str[i] == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            opt[count_opt] = str[i];</span><br><span class="line">            opt_idx[i] = <span class="number">1</span>;</span><br><span class="line">            count_opt++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            opt_idx[i] = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//合并多余的0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">99</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(opt_idx[i] == <span class="number">0</span> &amp;&amp; opt_idx[i+<span class="number">1</span>] == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = i; j &lt; <span class="number">99</span>; ++j)</span><br><span class="line">            &#123;</span><br><span class="line">                opt_idx[j] = opt_idx[j+<span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">            i--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//找到最大的符号下标</span></span><br><span class="line">    ll max_idx = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span> ; i &lt; <span class="number">100</span> ; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(opt_idx[i] == <span class="number">-1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            max_idx = i;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理浮点数</span></span><br><span class="line">    p = strtok(str, <span class="string">&quot;+-*/^()&quot;</span>);</span><br><span class="line">    num[<span class="number">0</span>] = atof(p);</span><br><span class="line">    <span class="keyword">while</span>((p = strtok(<span class="literal">NULL</span>, <span class="string">&quot;+-*/^()&quot;</span>)))</span><br><span class="line">    &#123;</span><br><span class="line">        num[count_num++] = atof(p);</span><br><span class="line">    &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;获取所有的浮点数:\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; count_num; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%f &quot;</span>,num[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n获取所有的运算符:\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="built_in">strlen</span>(opt); ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%c &quot;</span>,opt[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="comment">// for (i = 0; i &lt; max_idx; ++i)</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//     printf(&quot;%ld &quot;,opt_idx[i]);</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="comment">// putchar(&#x27;\n&#x27;);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//利用结构体数组将数字数组和操作符数组结合起来</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(opt_idx[i] == <span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            infix_expression[i].p_num = <span class="literal">NULL</span>;</span><br><span class="line">            infix_expression[i].p_opt = &amp;opt[count_opt_idx];</span><br><span class="line">            count_opt_idx++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(opt_idx[i] == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            infix_expression[i].p_num = &amp;num[count_num_idx];</span><br><span class="line">            infix_expression[i].p_opt = <span class="literal">NULL</span>;</span><br><span class="line">            count_num_idx++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    count_num_idx = 0;</span></span><br><span class="line"><span class="comment">    count_opt_idx = 0;</span></span><br><span class="line"><span class="comment">    bool flag_a = true;</span></span><br><span class="line"><span class="comment">    while(opt[count_opt_idx] != &#x27;\0&#x27; &amp;&amp; count_num_idx &lt; count_num)</span></span><br><span class="line"><span class="comment">    &#123;</span></span><br><span class="line"><span class="comment">        // printf(&quot;%lf &quot;,num[count_num_idx]);</span></span><br><span class="line"><span class="comment">        // printf(&quot;%c &quot;,opt[count_opt_idx]);</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        //注意开头就是括号的情况</span></span><br><span class="line"><span class="comment">        if(opt[count_opt_idx] == &#x27;(&#x27;)</span></span><br><span class="line"><span class="comment">        &#123;</span></span><br><span class="line"><span class="comment">            while(opt[count_opt_idx] == &#x27;(&#x27;)</span></span><br><span class="line"><span class="comment">            &#123;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">                count_opt_idx++;</span></span><br><span class="line"><span class="comment">                count_infix_idx++;</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">            //左括号完后一定是一个数字</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        infix_expression[count_infix_idx].p_num = &amp;num[count_num_idx];</span></span><br><span class="line"><span class="comment">        infix_expression[count_infix_idx].p_opt = NULL;</span></span><br><span class="line"><span class="comment">        count_num_idx++;</span></span><br><span class="line"><span class="comment">        count_infix_idx++;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">        infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">        count_opt_idx++;</span></span><br><span class="line"><span class="comment">        count_infix_idx++;</span></span><br><span class="line"><span class="comment">        if(opt[count_opt_idx] == &#x27;)&#x27; &amp;&amp; flag_a == true)</span></span><br><span class="line"><span class="comment">        &#123;</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_num = &amp;num[count_num_idx];</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_opt = NULL;</span></span><br><span class="line"><span class="comment">            count_num_idx++;</span></span><br><span class="line"><span class="comment">            count_infix_idx++;</span></span><br><span class="line"><span class="comment">            flag_a == false;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        //可能符号已经输出完了，但是还有最后一个数字没有输出出来</span></span><br><span class="line"><span class="comment">        if(opt[count_opt_idx] == &#x27;\0&#x27;)</span></span><br><span class="line"><span class="comment">        &#123;</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_num = &amp;num[count_num_idx];</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_opt = NULL;</span></span><br><span class="line"><span class="comment">            count_num_idx++;</span></span><br><span class="line"><span class="comment">            count_infix_idx++;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        if(opt[count_opt_idx] == &#x27;(&#x27;)</span></span><br><span class="line"><span class="comment">        &#123;</span></span><br><span class="line"><span class="comment">            while(opt[count_opt_idx] == &#x27;(&#x27;)</span></span><br><span class="line"><span class="comment">            &#123;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">                count_opt_idx++;</span></span><br><span class="line"><span class="comment">                count_infix_idx++;</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">            //左括号完后一定是一个数字</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        if(opt[count_opt_idx] == &#x27;)&#x27;)</span></span><br><span class="line"><span class="comment">        &#123;</span></span><br><span class="line"><span class="comment">            while(opt[count_opt_idx] == &#x27;)&#x27;)</span></span><br><span class="line"><span class="comment">            &#123;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">                count_opt_idx++;</span></span><br><span class="line"><span class="comment">                count_infix_idx++;</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">            //右括号完后一定是一个操作符</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">            infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">            count_opt_idx++;</span></span><br><span class="line"><span class="comment">            count_infix_idx++;</span></span><br><span class="line"><span class="comment">            //操作符完之后还可能是一堆左括号eg ((1+2)+(2+3))</span></span><br><span class="line"><span class="comment">            while(opt[count_opt_idx] == &#x27;(&#x27;)</span></span><br><span class="line"><span class="comment">            &#123;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_num = NULL;</span></span><br><span class="line"><span class="comment">                infix_expression[count_infix_idx].p_opt = &amp;opt[count_opt_idx];</span></span><br><span class="line"><span class="comment">                count_opt_idx++;</span></span><br><span class="line"><span class="comment">                count_infix_idx++;</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">    &#125;</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="comment">//将结构体数组打印出来</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;转换完的中缀表达式如下:\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(infix_expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//printf(&quot;%d %lf NULL\n&quot;,i, *infix_expression[i].p_num);</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%lf &quot;</span>,*infix_expression[i].p_num);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(infix_expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//printf(&quot;%d NULL %c\n&quot;,i, *infix_expression[i].p_opt);</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%c &quot;</span>,*infix_expression[i].p_opt);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n开始转换后缀表达式:\n&quot;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">my_Stack</span> <span class="title">Stack_calculate</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ele</span> <span class="title">postfix_Expression</span>[100];</span></span><br><span class="line">    ll count_postfix_idx = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    Stack_init(&amp;Stack_calculate);</span><br><span class="line">    <span class="type">char</span> opt_cal;</span><br><span class="line">    ll opt_temp_idx = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> opt_temp[<span class="number">100</span>];</span><br><span class="line">    <span class="type">char</span> opt_top;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//初始化结构体数组为了防止后续引用报错</span></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        postfix_Expression[i].p_num = <span class="literal">NULL</span>;</span><br><span class="line">        postfix_Expression[i].p_opt = <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(infix_expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            postfix_Expression[count_postfix_idx] = infix_expression[i];</span><br><span class="line">            count_postfix_idx++;</span><br><span class="line">            <span class="comment">//printf(&quot;数字: %ld 加入到后缀表达式\n&quot;,*(postfix_Expression[count_postfix_idx].p_num));</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(infix_expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            opt_cal = *infix_expression[i].p_opt;</span><br><span class="line">            <span class="keyword">if</span>( opt_cal == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 入栈\n&quot;</span>,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>( opt_cal != <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//先判断优先级 大于栈顶的元素就可以入栈 一开始是空栈哦</span></span><br><span class="line">                <span class="keyword">if</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">true</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    opt_top = <span class="string">&#x27; &#x27;</span>;<span class="comment">//最低级的</span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    opt_top = *(Stack_calculate.top<span class="number">-1</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 出来作比较\n&quot;</span>,opt_top);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span>(Priority(opt_cal) &gt; Priority(opt_top))</span><br><span class="line">                &#123;</span><br><span class="line">                    Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 入栈\n&quot;</span>,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">//小于等于栈顶的元素的话,要一直弹栈</span></span><br><span class="line">                    <span class="keyword">while</span>(Priority(opt_cal) &lt;= Priority(opt_top))</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="comment">//弹到最后没有元素了直接入栈欸</span></span><br><span class="line">                        <span class="keyword">if</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">true</span> || *(Stack_calculate.top<span class="number">-1</span>) == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                        &#123;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        opt_top = Pop(&amp;Stack_calculate);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                        <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 弹栈\n&quot;</span>,opt_top);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                        opt_temp[opt_temp_idx] = opt_top;</span><br><span class="line">                        postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">                        postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">                        count_postfix_idx++;</span><br><span class="line">                        opt_temp_idx++;</span><br><span class="line">                    &#125;</span><br><span class="line">                    Push(&amp;Stack_calculate,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 入栈\n&quot;</span>,opt_cal);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span><span class="comment">//如果是右括号</span></span><br><span class="line">            &#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;右括号来了\n&quot;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                <span class="comment">//由于要匹配左括号，要一直弹栈直到出现左括号</span></span><br><span class="line">                <span class="type">char</span> char_tmp = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">while</span>(char_tmp != <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    char_tmp = Pop(&amp;Stack_calculate);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 弹栈\n&quot;</span>,char_tmp);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    <span class="keyword">if</span>(char_tmp == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    opt_temp[opt_temp_idx] = char_tmp;</span><br><span class="line">                    postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">                    postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">                    opt_temp_idx++;</span><br><span class="line">                    count_postfix_idx++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//弹出栈的所有元素</span></span><br><span class="line">    <span class="keyword">while</span>(Stack_empty(&amp;Stack_calculate) == <span class="literal">false</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        opt_top = Pop(&amp;Stack_calculate);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;符号: %c 弹栈\n&quot;</span>,opt_top);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">        opt_temp[opt_temp_idx] = opt_top;</span><br><span class="line">        postfix_Expression[count_postfix_idx].p_opt = &amp;opt_temp[opt_temp_idx];</span><br><span class="line">        postfix_Expression[count_postfix_idx].p_num = <span class="literal">NULL</span>;</span><br><span class="line">        opt_temp_idx++;</span><br><span class="line">        count_postfix_idx++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将转换好的内容重新输入到函数的参数地址上</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        result[i].p_num = postfix_Expression[i].p_num;</span><br><span class="line">        result[i].p_opt = postfix_Expression[i].p_opt;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details</span></span><br><span class="line">    <span class="comment">//输出后缀表达式</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n转换的后缀表达式如下:\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; max_idx; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(postfix_Expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//printf(&quot;%d %lf NULL\n&quot;,i, *postfix_Expression[i].p_num);</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%lf &quot;</span>,*postfix_Expression[i].p_num);</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(postfix_Expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//printf(&quot;%d NULL %c\n&quot;,i, *postfix_Expression[i].p_opt);</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%c &quot;</span>,*postfix_Expression[i].p_opt);</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="comment">//不能直接返回postfix_Expression，因为这是局部变量哦 warning:function returns address of local variable</span></span><br><span class="line">    Stack_clear(&amp;Stack_calculate);</span><br><span class="line">    Stack_destory(&amp;Stack_calculate);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="title function_">caculate_four_arithmetic_operations</span><span class="params">(<span class="keyword">struct</span> ele* postfix_Expression)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">my_Stack</span> <span class="title">Stack_calculate</span>;</span></span><br><span class="line">    Stack_init(&amp;Stack_calculate);</span><br><span class="line">    <span class="comment">//这里要定义一个数字下标数组，因为double类型数组和Elementype为int类型的数组冲突了</span></span><br><span class="line">    <span class="comment">//打算后面用下标入栈</span></span><br><span class="line">    <span class="comment">// double num_tmp[100] = &#123;0&#125;; </span></span><br><span class="line">    <span class="type">double</span> *num_tmp = (<span class="type">double</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">double</span>)*<span class="number">100</span>); </span><br><span class="line">    ll count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(postfix_Expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            num_tmp[count] = *postfix_Expression[i].p_num;</span><br><span class="line">            <span class="comment">//printf(&quot;%lf &quot;,num_tmp[count]);</span></span><br><span class="line">            count++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//putchar(&#x27;\n&#x27;);</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n接下来开始用栈计算最终的结果\n&quot;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="comment">//记录储存的数组的最大值是多少,然后出栈之后就从这里开始</span></span><br><span class="line">    ll max_idx = count;</span><br><span class="line">    ll max_idx_count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(postfix_Expression[i].p_num != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            Push(&amp;Stack_calculate,count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,count,num_tmp[count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">            count++;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(postfix_Expression[i].p_opt != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            ll num_1_idx = Pop(&amp;Stack_calculate);</span><br><span class="line">            ll num_2_idx = Pop(&amp;Stack_calculate);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf &amp; [%ld]=%lf 弹栈\n&quot;</span>,num_1_idx,num_tmp[num_1_idx],num_2_idx,num_tmp[num_2_idx]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">            <span class="keyword">switch</span> (*postfix_Expression[i].p_opt)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;^&#x27;</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    num_tmp[max_idx+max_idx_count] = <span class="built_in">pow</span>(num_tmp[num_2_idx],num_tmp[num_1_idx]);</span><br><span class="line">                    Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,max_idx+max_idx_count,num_tmp[max_idx+max_idx_count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    max_idx_count++;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] + num_tmp[num_1_idx];</span><br><span class="line">                    Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,max_idx+max_idx_count,num_tmp[max_idx+max_idx_count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    max_idx_count++;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] - num_tmp[num_1_idx];</span><br><span class="line">                    Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,max_idx+max_idx_count,num_tmp[max_idx+max_idx_count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    max_idx_count++;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] * num_tmp[num_1_idx];</span><br><span class="line">                    Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,max_idx+max_idx_count,num_tmp[max_idx+max_idx_count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    max_idx_count++;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;/&#x27;</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    num_tmp[max_idx+max_idx_count] = num_tmp[num_2_idx] / num_tmp[num_1_idx];</span><br><span class="line">                    Push(&amp;Stack_calculate,max_idx+max_idx_count);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> details_Stack</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;下标: [%ld]=%lf 入栈\n&quot;</span>,max_idx+max_idx_count,num_tmp[max_idx+max_idx_count]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">                    max_idx_count++;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">double</span> result = num_tmp[Pop(&amp;Stack_calculate)];</span><br><span class="line">    Stack_destory(&amp;Stack_calculate);</span><br><span class="line">    <span class="built_in">free</span>(num_tmp);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="title function_">calc</span><span class="params">(<span class="type">char</span> *str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">double</span> result = <span class="number">0</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ele</span> <span class="title">postfix_Expression</span>[100];</span></span><br><span class="line">    <span class="built_in">memset</span>(postfix_Expression, <span class="number">0</span>, <span class="keyword">sizeof</span>(postfix_Expression));</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> new_str[<span class="number">100</span>];</span><br><span class="line">    <span class="built_in">memset</span>(new_str, <span class="number">0</span>, <span class="keyword">sizeof</span>(new_str));</span><br><span class="line">    new_str[<span class="number">0</span>] = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    new_str[<span class="number">1</span>] = <span class="string">&#x27;+&#x27;</span>;</span><br><span class="line">    <span class="built_in">strcpy</span>(new_str+<span class="number">2</span>, str);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="keyword">sizeof</span>(new_str); i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(new_str[i] == <span class="string">&#x27;[&#x27;</span> || new_str[i] == <span class="string">&#x27;&#123;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            new_str[i] = <span class="string">&#x27;(&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(new_str[i]== <span class="string">&#x27;]&#x27;</span> || new_str[i] == <span class="string">&#x27;&#125;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            new_str[i] = <span class="string">&#x27;)&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    str_find_exp_and_calc(new_str);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;str_find_exp_and_calc : %s\n&quot;</span>, new_str);</span><br><span class="line"></span><br><span class="line">    mid_to_back(new_str, <span class="built_in">strlen</span>(new_str), postfix_Expression);</span><br><span class="line">    result = caculate_four_arithmetic_operations(postfix_Expression);</span><br><span class="line">    <span class="comment">// printf(&quot;result : %lf\n&quot;,result);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/f1369abc/</id>
    <link href="https://cosmical-container.github.io/posts/f1369abc/"/>
    <published>2024-10-06T04:33:00.000Z</published>
    <summary>
      <![CDATA[<p>计算器是栈的教科书级应用。这个项目从 V1.0 迭代到 V1.2，最终版约 1100 行，从”能算 <code>1+2*3</code>“一路做到：<code>+ - * / ^</code> 五则运算、<code>{} [] ()</code> 三种括号混用、小数，甚至]]>
    </summary>
    <title>用栈实现四则运算计算器</title>
    <updated>2024-10-06T04:33:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="函数指针" scheme="https://cosmical-container.github.io/tags/%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88/"/>
    <content>
      <![CDATA[<p>一个有意思的小工具：给它一个字符串，比如 <code>&quot;sin(1+1)+max(2,3)&quot;</code> 这种形式，它能自动找出里面的数学函数调用、解析括号里的表达式，然后调用对应的库函数把结果算出来。全程纯 C，核心用了三个技术点。</p><h2 id="一、函数指针”查找表”"><a href="#一、函数指针”查找表”" class="headerlink" title="一、函数指针”查找表”"></a>一、函数指针”查找表”</h2><p>C 没有反射，怎么把字符串 <code>&quot;sin&quot;</code> 变成 <code>sin()</code> 函数？答案是<strong>字符串比较 + 函数指针</strong>：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="title function_">double</span> <span class="params">(*math_fun)</span><span class="params">(<span class="type">double</span>)</span>;</span><br><span class="line"></span><br><span class="line">math_fun <span class="title function_">get_math_fun</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sin&quot;</span>) == <span class="number">0</span>)  <span class="keyword">return</span> <span class="built_in">sin</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cos&quot;</span>) == <span class="number">0</span>)  <span class="keyword">return</span> <span class="built_in">cos</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tan&quot;</span>) == <span class="number">0</span>)  <span class="keyword">return</span> <span class="built_in">tan</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sqrt&quot;</span>) == <span class="number">0</span>) <span class="keyword">return</span> <span class="built_in">sqrt</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;floor&quot;</span>) == <span class="number">0</span>) <span class="keyword">return</span> <span class="built_in">floor</span>;</span><br><span class="line">    <span class="comment">// ... exp、log、asin、atanh 等十几math个</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>typedef double (*math_fun)(double)</code> 定义了”接收 double 返回 double”的函数指针类型，数学库里这一族函数签名完全一致，正好统一管理。</p><h2 id="二、括号匹配扫描"><a href="#二、括号匹配扫描" class="headerlink" title="二、括号匹配扫描"></a>二、括号匹配扫描</h2><p>要从字符串里截出 <code>sin(1+cos(2))</code> 的完整参数部分，不能简单找第一个 <code>)</code>——参数里可能嵌套括号。经典解法是<strong>计数器匹配</strong>：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> count_kuohao = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (*sub_str_ptr == <span class="string">&#x27;)&#x27;</span>) count_kuohao++;   <span class="comment">// 遇到闭包 +1</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (*sub_str_ptr == <span class="string">&#x27;(&#x27;</span>) count_kuohao--; <span class="comment">// 遇到开括 -1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (count_kuohao != <span class="number">0</span>)</span><br><span class="line">        exp_str[exp_str_i++] = *sub_str_ptr;   <span class="comment">// 还没配平，继续收集</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">break</span>;                                  <span class="comment">// 配平，参数截取完成</span></span><br><span class="line">&#125; <span class="keyword">while</span> (*sub_str_ptr);</span><br></pre></td></tr></table></figure><p>嵌套多少层都能正确处理。函数名的提取则是从 <code>(</code> 往前回溯，直到碰上 <code>+ - * /</code> 等运算符为止。</p><h2 id="三、字符串替换的坑"><a href="#三、字符串替换的坑" class="headerlink" title="三、字符串替换的坑"></a>三、字符串替换的坑</h2><p>工具函数 <code>str_rpl</code> 实现替换时用 <code>memmove</code> 挪动后面那段内存——注意必须用 <code>memmove</code> 而不是 <code>memcpy</code>，因为源和目标区域有重叠，<code>memcpy</code> 对重叠区间的行为是未定义的：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (ptr = <span class="built_in">strstr</span>(s, s1))     <span class="comment">/* 在 s 中找到 s1 */</span></span><br><span class="line">&#123;</span><br><span class="line">    memmove(ptr + <span class="built_in">strlen</span>(s2), ptr + <span class="built_in">strlen</span>(s1), <span class="built_in">strlen</span>(ptr) - <span class="built_in">strlen</span>(s1) + <span class="number">1</span>);</span><br><span class="line">    <span class="built_in">memcpy</span>(ptr, s2, <span class="built_in">strlen</span>(s2));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>这个玩具麻雀虽小：函数指针表、括号匹配、memmove 搬移内存，C 语言字符串处理的三板斧都在里面。缺点也很明显——固定 100 字节的缓冲区、魔法数字偏多，算是个”能用但可以重构”的状态。之后可以试试改成递归下降解析器，那就是正经的表达式引擎了。</p><h2 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h2><figure class="highlight c"><figcaption><span>表达式函数查找并计算.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> *<span class="title function_">str_rpl</span><span class="params">(<span class="type">char</span> *s, <span class="type">const</span> <span class="type">char</span> *s1, <span class="type">const</span> <span class="type">char</span> *s2)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *ptr;</span><br><span class="line">    <span class="keyword">while</span> (ptr = <span class="built_in">strstr</span>(s, s1))     <span class="comment">/* 如果在s中找到s1 */</span></span><br><span class="line">    &#123;</span><br><span class="line">        memmove(ptr + <span class="built_in">strlen</span>(s2) , ptr + <span class="built_in">strlen</span>(s1), <span class="built_in">strlen</span>(ptr) - <span class="built_in">strlen</span>(s1) + <span class="number">1</span>);</span><br><span class="line">        <span class="built_in">memcpy</span>(ptr, &amp;s2[<span class="number">0</span>], <span class="built_in">strlen</span>(s2));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">str_reverse</span><span class="params">(<span class="type">char</span>* s)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span>* left = s;</span><br><span class="line"><span class="type">char</span>* right = s + <span class="built_in">strlen</span>(s) - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (left &lt; right)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> tmp = *left;</span><br><span class="line">*left = *right;</span><br><span class="line">*right = tmp;</span><br><span class="line"></span><br><span class="line">left++;</span><br><span class="line">right--;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">double2str</span><span class="params">(<span class="type">double</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// double：8字节 2.3E-308 到 1.7E+308</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">21</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%lf&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="title function_">str2double</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> atof(str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="title function_">double</span> <span class="params">(*math_fun)</span><span class="params">(<span class="type">double</span>)</span>;</span><br><span class="line">math_fun <span class="title function_">get_math_fun</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sin&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sin</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cos&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">cos</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tan&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">tan</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sinh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sinh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;cosh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">cosh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;tanh&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">tanh</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;asin&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">asin</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;acos&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">acos</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;atan&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">atan</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;exp&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">exp</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;log&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">log</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;log10&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">log10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;sqrt&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sqrt</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;ceil&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">ceil</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(s, <span class="string">&quot;floor&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">floor</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">int</span> exp_count;</span><br><span class="line">    <span class="type">char</span> **exp_funcs;</span><br><span class="line">    <span class="type">char</span> **exp_strs;</span><br><span class="line">    <span class="type">char</span> **exp_results;</span><br><span class="line">&#125;exp_strStuctTypedef;</span><br><span class="line"></span><br><span class="line">exp_strStuctTypedef <span class="title function_">str_find_exp_and_calc</span><span class="params">(<span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *ptr = s;              <span class="comment">//遍历整个字符串的指针</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> exp_func[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;    <span class="comment">//储存当前找到的表达式函数</span></span><br><span class="line">    <span class="type">int</span>  exp_func_i = <span class="number">0</span>;        <span class="comment">//当前找到的表达式函数数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_funcs = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式函数</span></span><br><span class="line">    <span class="type">int</span>  exp_funcs_i = <span class="number">0</span>;       <span class="comment">//所有找到的表达式函数数组的下标</span></span><br><span class="line">    <span class="built_in">memset</span>(exp_funcs, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> exp_str[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;    <span class="comment">//储存当前找到的表达式</span></span><br><span class="line">    <span class="type">int</span>  exp_str_i = <span class="number">0</span>;         <span class="comment">//当前找到的表达式数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_strs = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式</span></span><br><span class="line">    <span class="type">int</span>  exp_strs_i = <span class="number">0</span>;        <span class="comment">//所有找到的表达式数组的下标</span></span><br><span class="line">    <span class="built_in">memset</span>(exp_strs, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> exp_result[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;    <span class="comment">//储存当前表达式的计算值</span></span><br><span class="line">    <span class="type">int</span>  exp_result_i = <span class="number">0</span>;         <span class="comment">//当前找到的表达式计算值数组的下标</span></span><br><span class="line">    <span class="type">char</span> **exp_results = (<span class="type">char</span> **)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>); <span class="comment">//储存所有找到的表达式的计算值</span></span><br><span class="line">    <span class="type">int</span>  exp_results_i = <span class="number">0</span>;        <span class="comment">//所有找到的表达式计算值数组的下标</span></span><br><span class="line">    <span class="built_in">memset</span>(exp_results, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">char</span> *) * <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;str: %s\n&quot;</span>, s);</span><br><span class="line">    <span class="keyword">while</span> (*ptr++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (*ptr == <span class="string">&#x27;(&#x27;</span> &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;(&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;+&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;-&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;*&#x27;</span>) &amp;&amp; (*(ptr - <span class="number">1</span>) != <span class="string">&#x27;/&#x27;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// printf(&quot;find exp: &quot;);</span></span><br><span class="line">            <span class="type">char</span> *exp_func_ptr = ptr;</span><br><span class="line">            <span class="keyword">while</span>(*exp_func_ptr--)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>((*exp_func_ptr != <span class="string">&#x27;+&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;-&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;*&#x27;</span>) &amp;&amp; (*exp_func_ptr != <span class="string">&#x27;/&#x27;</span>))</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">// putchar(*exp_func_ptr);</span></span><br><span class="line">                    exp_func[exp_func_i++] = *exp_func_ptr;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    exp_func[exp_func_i] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">                    exp_funcs[exp_funcs_i] = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (exp_func_i + <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">strcpy</span>(exp_funcs[exp_funcs_i], exp_func);</span><br><span class="line">                    str_reverse(exp_funcs[exp_funcs_i]);</span><br><span class="line">                    exp_funcs_i++;</span><br><span class="line">                    </span><br><span class="line">                    <span class="built_in">memset</span>(exp_func, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line">                    exp_func_i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> count_kuohao = <span class="number">-1</span>;</span><br><span class="line">            <span class="type">char</span> *sub_str_ptr = ptr + <span class="number">1</span>;</span><br><span class="line">            <span class="built_in">memset</span>(exp_str, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">do</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">// 括号匹配</span></span><br><span class="line">                <span class="keyword">if</span>(*sub_str_ptr == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    count_kuohao++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span>(*sub_str_ptr == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    count_kuohao--;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 退出条件</span></span><br><span class="line">                <span class="keyword">if</span>(count_kuohao != <span class="number">0</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">// putchar(*sub_str_ptr);</span></span><br><span class="line">                    exp_str[exp_str_i++] = *sub_str_ptr;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    exp_str[exp_str_i] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">                    exp_strs[exp_strs_i] = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (exp_str_i + <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">strcpy</span>(exp_strs[exp_strs_i], exp_str);</span><br><span class="line">                    exp_strs_i++;</span><br><span class="line">                    </span><br><span class="line">                    <span class="built_in">memset</span>(exp_str, <span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line">                    exp_str_i = <span class="number">0</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;<span class="keyword">while</span>(*sub_str_ptr++);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; exp_strs_i; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">char</span> *temp_result_str = double2str(get_math_fun(exp_funcs[i])(str2double(exp_strs[i])));</span><br><span class="line">        exp_results[i] = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (<span class="built_in">strlen</span>(temp_result_str) + <span class="number">1</span>));</span><br><span class="line">        <span class="built_in">strcpy</span>(exp_results[i], temp_result_str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    exp_strStuctTypedef exp_strStuct;</span><br><span class="line">    exp_strStuct.exp_count = exp_strs_i;</span><br><span class="line">    exp_strStuct.exp_funcs = exp_funcs;</span><br><span class="line">    exp_strStuct.exp_strs = exp_strs;</span><br><span class="line">    exp_strStuct.exp_results = exp_results;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> exp_strStuct;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/6125e204/</id>
    <link href="https://cosmical-container.github.io/posts/6125e204/"/>
    <published>2024-10-05T14:58:00.000Z</published>
    <summary>
      <![CDATA[<p>一个有意思的小工具：给它一个字符串，比如 <code>&quot;sin(1+1)+max(2,3)&quot;</code> 这种形式，它能自动找出里面的数学函数调用、解析括号里的表达式，然后调用对应的库函数把结果算出来。全程纯 C，核心用了三个技术点。</p>
<h2]]>
    </summary>
    <title>字符串公式求值器</title>
    <updated>2024-10-05T14:58:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="Print.h 开发记" scheme="https://cosmical-container.github.io/categories/Print-h-%E5%BC%80%E5%8F%91%E8%AE%B0/"/>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<blockquote><p>系列导航：<a href="/posts/6e9f2a48/">上一篇：Print.h 开发记（六）V1.6</a></p></blockquote><p>系列最终篇：V1.7 加入字体效果，V1.8 修掉遗留问题并支持无参调用——print 库到此定稿。</p><h2 id="V1-7：字体宏"><a href="#V1-7：字体宏" class="headerlink" title="V1.7：字体宏"></a>V1.7：字体宏</h2><p>除了颜色，ANSI 转义序列还能控制字体效果：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原始字体宏</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> NONE(str)          str<span class="string">&quot;\033[m&quot;</span></span></span><br><span class="line"><span class="comment">// 高亮文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> HIGHTLIGHT(str)    <span class="string">&quot;\033[1m&quot;</span>NONE(str)</span></span><br><span class="line"><span class="comment">// 斜体文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ITALIC(str)        <span class="string">&quot;\033[3m&quot;</span>NONE(str)</span></span><br><span class="line"><span class="comment">// 下划线文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _(str)             <span class="string">&quot;\033[4m&quot;</span>NONE(str)</span></span><br><span class="line"><span class="comment">// 闪烁文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FLASGING(str)      <span class="string">&quot;\033[5m&quot;</span>NONE(str)</span></span><br><span class="line"><span class="comment">// 反显文字 - 交换前景色和背景色</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Reversedisplay(str) <span class="string">&quot;\033[7m&quot;</span>NONE(str)</span></span><br></pre></td></tr></table></figure><p>高亮、斜体、下划线、闪烁、反显，五种效果随意组合使用。</p><p>V1.7 还修了一个<strong>多文件包含冲突</strong>的问题：多个 .c 文件同时包含 Print.h 时，头文件里的函数体会被编译两次，链接时报”函数重复定义”。解决方式是给所有函数加上 <code>static inline</code>——<code>static</code> 让函数只在当前编译单元可见，<code>inline</code> 允许把函数体直接写在头文件里：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_char</span><span class="params">(<span class="type">char</span> num)</span> &#123; <span class="built_in">printf</span>(<span class="string">&quot;%c&quot;</span>, num); &#125;</span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">str_comb</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str1, <span class="type">const</span> <span class="type">char</span> *str2)</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br></pre></td></tr></table></figure><p>这正是 V1.0 埋下的那个伏笔，到这里才正式填上。</p><h2 id="V1-8：无参打印"><a href="#V1-8：无参打印" class="headerlink" title="V1.8：无参打印"></a>V1.8：无参打印</h2><p>V1.8 <strong>重写了 PrintMacroArgCount 宏</strong>，让它能正确处理”没有参数”的情况：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">print();        <span class="comment">// 打印空白</span></span><br><span class="line">println();      <span class="comment">// 打印一个换行</span></span><br></pre></td></tr></table></figure><p>之前的参数计数宏在空参数时会展开出错，重写后 <code>print()</code> 和 <code>println()</code> 都能正常工作，系列的最新版本就此定稿。</p><h2 id="系列完结"><a href="#系列完结" class="headerlink" title="系列完结"></a>系列完结</h2><p>从 V1.0 的 <code>_Generic</code> 单参数打印，到 debug 宏、颜色、字体、多参数、无参调用——一个 print 库的完整进化史写完了。所有源码都在 <a href="https://github.com/COSMICAL-CONTAINER/C_Program-Print.h">GitHub 项目</a>里，每个版本的 Print.h 都保留着，想看某个版本的完整实现直接翻历史即可。</p><h2 id="V1-7-V1-8-完整代码"><a href="#V1-7-V1-8-完整代码" class="headerlink" title="V1.7&#x2F;V1.8 完整代码"></a>V1.7&#x2F;V1.8 完整代码</h2><figure class="highlight c"><figcaption><span>Print.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @file Print.h</span></span><br><span class="line"><span class="comment"> * @author Cosmical Containter</span></span><br><span class="line"><span class="comment"> * @emile 联系方式已隐去 </span></span><br><span class="line"><span class="comment"> * @github https://github.com/COSMICAL-CONTAINER</span></span><br><span class="line"><span class="comment"> * @brief Print anything you want!</span></span><br><span class="line"><span class="comment"> * @version 1.8</span></span><br><span class="line"><span class="comment"> * @date 2024-07-15</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @copyright Copyright (c) 2024</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * @see</span></span><br><span class="line"><span class="comment"> * V1.0 </span></span><br><span class="line"><span class="comment"> * 完成基础功能，实现对基础类型char、short int、int、float、double、const char *、char *的打印</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * V1.1</span></span><br><span class="line"><span class="comment"> * 修复bug</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * V1.2</span></span><br><span class="line"><span class="comment"> * 加入unsigned long long 的打印格式</span></span><br><span class="line"><span class="comment"> * 加入打印数组功能，可以自选数组中每两个元素的打印格式</span></span><br><span class="line"><span class="comment"> * 加入不支持的格式错误处理</span></span><br><span class="line"><span class="comment"> * 加入颜色打印代码</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * V1.3</span></span><br><span class="line"><span class="comment"> * 拓展颜色打印宏，现在可以支持输入字符串或变量了</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * V1.4</span></span><br><span class="line"><span class="comment"> * 拓展print与println宏，以前只能输出1个参数，现在可以输入最多10个参数了，做到了重载</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * V1.5</span></span><br><span class="line"><span class="comment"> * 实现对大部分基础类型的打印</span></span><br><span class="line"><span class="comment"> * 完善注释</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * V1.6</span></span><br><span class="line"><span class="comment"> * 加入debug宏，可以打印变量名以及变量的值</span></span><br><span class="line"><span class="comment"> * 加入WARN_IF、ERR_IF、ERR_EXIT_IF宏，可以根据条件来测试打印</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * V1.7</span></span><br><span class="line"><span class="comment"> * 修复多个文件同时包含Print.h时，函数冲突的问题</span></span><br><span class="line"><span class="comment"> * 加入字体宏，可以高亮、斜体、下划线、闪烁、反显文字</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * V1.8</span></span><br><span class="line"><span class="comment"> * 修复字体宏高亮、斜体、下划线、闪烁、反显文字打印变量时报错</span></span><br><span class="line"><span class="comment"> * 重写PrintMacroArgCount宏，现在可以获取没有参数的情况了，实现print()、println(),不传递参数默认打印空白或换行</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __Print_H__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __Print_H__</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRINTMAXSTRLEN 1000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 颜色宏代码</span></span><br><span class="line"><span class="comment">// #define NONE(str)          str&quot;\033[m&quot;</span></span><br><span class="line"><span class="comment">// #define RED(str)           &quot;\033[0;32;31m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_RED(str)     &quot;\033[1;31m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define GREEN(str)         &quot;\033[0;32;32m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_GREEN(str)   &quot;\033[1;32m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define BLUE(str)          &quot;\033[0;32;34m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_BLUE(str)    &quot;\033[1;34m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define DARY_GRAY(str)     &quot;\033[1;30m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define CYAN(str)          &quot;\033[0;36m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_CYAN(str)    &quot;\033[1;36m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define PURPLE(str)        &quot;\033[0;35m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_PURPLE(str)  &quot;\033[1;35m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define YELLOW(str)        &quot;\033[0;33m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_YELLOW(str)  &quot;\033[1;33m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define LIGHT_GRAY(str)    &quot;\033[0;37m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// #define WHITE(str)         &quot;\033[1;37m&quot;NONE(str)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// // 原始字体宏</span></span><br><span class="line"><span class="comment">// #define NONE(str)          str&quot;\033[m&quot;</span></span><br><span class="line"><span class="comment">// // 高亮文字</span></span><br><span class="line"><span class="comment">// #define HIGHTLIGHT(str)    &quot;\033[1m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// // 斜体文字</span></span><br><span class="line"><span class="comment">// #define ITALIC(str)        &quot;\033[3m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// // 下划线文字</span></span><br><span class="line"><span class="comment">// #define _(str)             &quot;\033[4m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// // 闪烁文字</span></span><br><span class="line"><span class="comment">// #define FLASGING(str)      &quot;\033[5m&quot;NONE(str)</span></span><br><span class="line"><span class="comment">// // 反显文字 - 交换前景色和背景色</span></span><br><span class="line"><span class="comment">// #define Reversedisplay(str)      &quot;\033[7m&quot;NONE(str)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 为了能够实现兼容字符串和变量的颜色打印，只能这样操作</span></span><br><span class="line"><span class="comment">// RED(a) 与 RED(&quot;1&quot;) 兼容</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TOSTR(obj) (_Generic((obj),\</span></span><br><span class="line"><span class="meta">    char:        char2str,\</span></span><br><span class="line"><span class="meta">    short int:        short2str,\</span></span><br><span class="line"><span class="meta">    int:        int2str,\</span></span><br><span class="line"><span class="meta">    float:        float2str,\</span></span><br><span class="line"><span class="meta">    double:        double2str,\</span></span><br><span class="line"><span class="meta">    const char*:        str2str,\</span></span><br><span class="line"><span class="meta">char *:        str2str,\</span></span><br><span class="line"><span class="meta">    unsigned long long: ulonglong2str,\</span></span><br><span class="line"><span class="meta">    default:            print_void_or_error\</span></span><br><span class="line"><span class="meta">    )(obj))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RED(obj)           (str_comb(str_comb(<span class="string">&quot;\033[0;32;31m&quot;</span>,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_RED(obj)     (str_comb(str_comb(<span class="string">&quot;\033[1;31m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GREEN(obj)         (str_comb(str_comb(<span class="string">&quot;\033[0;32;32m&quot;</span>,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_GREEN(obj)   (str_comb(str_comb(<span class="string">&quot;\033[1;32m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BLUE(obj)          (str_comb(str_comb(<span class="string">&quot;\033[0;32;34m&quot;</span>,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_BLUE(obj)    (str_comb(str_comb(<span class="string">&quot;\033[1;34m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DARY_GRAY(obj)     (str_comb(str_comb(<span class="string">&quot;\033[1;30m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CYAN(obj)          (str_comb(str_comb(<span class="string">&quot;\033[0;36m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_CYAN(obj)    (str_comb(str_comb(<span class="string">&quot;\033[1;36m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PURPLE(obj)        (str_comb(str_comb(<span class="string">&quot;\033[0;35m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_PURPLE(obj)  (str_comb(str_comb(<span class="string">&quot;\033[1;35m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> YELLOW(obj)        (str_comb(str_comb(<span class="string">&quot;\033[0;33m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_YELLOW(obj)  (str_comb(str_comb(<span class="string">&quot;\033[1;33m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LIGHT_GRAY(obj)    (str_comb(str_comb(<span class="string">&quot;\033[0;37m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> WHITE(obj)         (str_comb(str_comb(<span class="string">&quot;\033[1;37m&quot;</span>   ,TOSTR(obj)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> HIGHTLIGHT(str)    (str_comb(str_comb(<span class="string">&quot;\033[1m&quot;</span>      ,TOSTR(str)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="comment">// 斜体文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ITALIC(str)              (str_comb(str_comb(<span class="string">&quot;\033[3m&quot;</span>,TOSTR(str)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="comment">// 下划线文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _(str)                   (str_comb(str_comb(<span class="string">&quot;\033[4m&quot;</span>,TOSTR(str)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="comment">// 闪烁文字</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FLASGING(str)            (str_comb(str_comb(<span class="string">&quot;\033[5m&quot;</span>,TOSTR(str)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"><span class="comment">// 反显文字 - 交换前景色和背景色</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> Reversedisplay(str)      (str_comb(str_comb(<span class="string">&quot;\033[7m&quot;</span>,TOSTR(str)),<span class="string">&quot;\033[m&quot;</span>))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这是字符串合成函数</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">str_comb</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str1, <span class="type">const</span> <span class="type">char</span> *str2)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[PRINTMAXSTRLEN];</span><br><span class="line">    <span class="built_in">strcpy</span>(str, str1);</span><br><span class="line">    <span class="built_in">strcat</span>(str, str2);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面是处理各种类型转字符串的函数</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">char2str</span><span class="params">(<span class="type">char</span> ch)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">2</span>];</span><br><span class="line">    str[<span class="number">0</span>] = ch;</span><br><span class="line">    str[<span class="number">1</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">short2str</span><span class="params">(<span class="type">short</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// short：2字节 -32,768 到 32,767</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">7</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%hd&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">int2str</span><span class="params">(<span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// int：2~4字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">12</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%d&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">float2str</span><span class="params">(<span class="type">float</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// float：4字节 1.2E-38 到 3.4E+38</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">12</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%f&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">double2str</span><span class="params">(<span class="type">double</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// double：8字节 2.3E-308 到 1.7E+308</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">21</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%lf&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">ulonglong2str</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// unsigned long long：8字节 0 到 18,446,744,073,709,551,615</span></span><br><span class="line">    <span class="type">static</span> <span class="type">char</span> str[<span class="number">21</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%llu&quot;</span>, num);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="type">char</span>* <span class="title function_">str2str</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 直接返回即可</span></span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 颜色宏代码测试</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TestColor(obj)\</span></span><br><span class="line"><span class="meta">&#123;\</span></span><br><span class="line"><span class="meta">    println(RED(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_RED(obj));\</span></span><br><span class="line"><span class="meta">    println(GREEN(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_GREEN(obj));\</span></span><br><span class="line"><span class="meta">    println(BLUE(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_BLUE(obj));\</span></span><br><span class="line"><span class="meta">    println(CYAN(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_CYAN(obj));\</span></span><br><span class="line"><span class="meta">    println(PURPLE(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_PURPLE(obj));\</span></span><br><span class="line"><span class="meta">    println(YELLOW(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_YELLOW(obj));\</span></span><br><span class="line"><span class="meta">    println(DARY_GRAY(obj));\</span></span><br><span class="line"><span class="meta">    println(LIGHT_GRAY(obj));\</span></span><br><span class="line"><span class="meta">    println(WHITE(obj));\</span></span><br><span class="line"><span class="meta">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 兼容Color两种写法</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TestColur TestColor</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 原始打印宏，根据obj类型调用不同的打印函数</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print(obj) (_Generic((obj),    \</span></span><br><span class="line"><span class="meta">    char:        print_char,     \</span></span><br><span class="line"><span class="meta">    unsigned char:print_uchar,    \</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    short int:        print_short,    \</span></span><br><span class="line"><span class="meta">    int:        print_int,      \</span></span><br><span class="line"><span class="meta">    unsigned int:print_uint,     \</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    long long:          print_longlong, \</span></span><br><span class="line"><span class="meta">    unsigned long long: print_ulonglong,\</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    float:        print_float,    \</span></span><br><span class="line"><span class="meta">    double:        print_double,   \</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    const char*:        print_cstr,     \</span></span><br><span class="line"><span class="meta">char*:        print_str,      \</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    int*:               print_p,        \</span></span><br><span class="line"><span class="meta">    long long*:         print_p,        \</span></span><br><span class="line"><span class="meta">    unsigned long long*:print_p,        \</span></span><br><span class="line"><span class="meta">    float*:             print_p,        \</span></span><br><span class="line"><span class="meta">    double*:            print_p,        \</span></span><br><span class="line"><span class="meta">                                        \</span></span><br><span class="line"><span class="meta">    default:            print_void_or_error     \</span></span><br><span class="line"><span class="meta">    )(obj))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 原始打印换行宏，直接调用打印函数然后打印换行符</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println(obj) _print(obj), _print(<span class="string">&quot;\n&quot;</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取参数个数，最多支持10个参数</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _PrintMacroArgCount(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, COUNT,...) COUNT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PrintMacroArgCount(...) _PrintMacroArgCount(<span class="string">&quot;ignored&quot;</span>, ##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 宏黏贴</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PrintConcat(A, B) _PrintConcat(A, B)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _PrintConcat(A, B) A##B</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印宏的宏展开，最多支持10个参数</span></span><br><span class="line"><span class="comment">// 原理: print(a, b, c); 展开后变成 _print(a), _print(b), _print(c);</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> print(...) _print_(__VA_ARGS__)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_(...) PrintConcat(_print_, PrintMacroArgCount(__VA_ARGS__))(__VA_ARGS__)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_0()                                              printf(<span class="string">&quot;&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_1(_0)                                           _print(_0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_2(_0, _1)                                       _print_1(_0), _print(_1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_3(_0, _1, _2)                                   _print_2(_0, _1), _print(_2)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_4(_0, _1, _2, _3)                               _print_3(_0, _1, _2), _print(_3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_5(_0, _1, _2, _3, _4)                           _print_4(_0, _1, _2, _3), _print(_4)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_6(_0, _1, _2, _3, _4, _5)                       _print_5(_0, _1, _2, _3, _4), _print(_5)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_7(_0, _1, _2, _3, _4, _5, _6)                   _print_6(_0, _1, _2, _3, _4, _5), _print(_6)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_8(_0, _1, _2, _3, _4, _5, _6, _7)               _print_7(_0, _1, _2, _3, _4, _5, _6), _print(_7)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_9(_0, _1, _2, _3, _4, _5, _6, _7, _8)           _print_8(_0, _1, _2, _3, _4, _5, _6, _7), _print(_8)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _print_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9)      _print_9(_0, _1, _2, _3, _4, _5, _6, _7, _8), _print(_9)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> endl ((char)<span class="string">&#x27;\n&#x27;</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印并换行宏的宏展开，最多支持10个参数</span></span><br><span class="line"><span class="comment">// 原理: println(a, b, c); 展开后变成 _println(a), _println(b), _println(c);</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> println(...) _println_(__VA_ARGS__)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_(...) PrintConcat(_println_, PrintMacroArgCount(__VA_ARGS__))(__VA_ARGS__)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_0()                                              print(<span class="string">&quot;\n&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_1(_0)                                           _println(_0)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_2(_0, _1)                                       _println_1(_0), _println(_1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_3(_0, _1, _2)                                   _println_2(_0, _1), _println(_2)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_4(_0, _1, _2, _3)                               _println_3(_0, _1, _2), _println(_3)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_5(_0, _1, _2, _3, _4)                           _println_4(_0, _1, _2, _3), _println(_4)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_6(_0, _1, _2, _3, _4, _5)                       _println_5(_0, _1, _2, _3, _4), _println(_5)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_7(_0, _1, _2, _3, _4, _5, _6)                   _println_6(_0, _1, _2, _3, _4, _5), _println(_6)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_8(_0, _1, _2, _3, _4, _5, _6, _7)               _println_7(_0, _1, _2, _3, _4, _5, _6), _println(_7)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_9(_0, _1, _2, _3, _4, _5, _6, _7, _8)           _println_8(_0, _1, _2, _3, _4, _5, _6, _7), _println(_8)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _println_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9)      _println_9(_0, _1, _2, _3, _4, _5, _6, _7, _8), _println(_9)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面是打印各种类型的原始打印函数</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_char</span><span class="params">(<span class="type">char</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%c&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_uchar</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%c&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_short</span><span class="params">(<span class="type">short</span> <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%hd&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_int</span><span class="params">(<span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_uint</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%u&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_longlong</span><span class="params">(<span class="type">long</span> <span class="type">long</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%lld&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_ulonglong</span><span class="params">(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%llu&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_float</span><span class="params">(<span class="type">float</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%f&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_double</span><span class="params">(<span class="type">double</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%lf&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_cstr</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s&quot;</span>, str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_str</span><span class="params">(<span class="type">char</span>* str)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s&quot;</span>, str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_p</span><span class="params">(<span class="type">void</span>* obj)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%p&quot;</span>, obj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">print_void_or_error</span><span class="params">(<span class="type">void</span> *data)</span></span><br><span class="line">&#123;</span><br><span class="line">    _print(RED( <span class="string">&quot;print error! or&quot;</span> ));</span><br><span class="line">    _println(RED( <span class="string">&quot;don&#x27;t have this type to print!&quot;</span> ));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// debug宏</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> debug_valuename_to_str(value) #value</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> debug(obj) print(LIGHT_GREEN(<span class="string">&quot;debug: &quot;</span>), debug_valuename_to_str(obj)<span class="string">&quot; = &quot;</span>, obj, <span class="string">&quot;\n&quot;</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> WARN_IF(EXP)  \</span></span><br><span class="line"><span class="meta">do                    \</span></span><br><span class="line"><span class="meta">&#123;                     \</span></span><br><span class="line"><span class="meta">    <span class="keyword">if</span> (EXP)          \</span></span><br><span class="line"><span class="meta">        print(YELLOW(<span class="string">&quot;warning: &quot;</span>), #EXP <span class="string">&quot;\n&quot;</span>); \</span></span><br><span class="line"><span class="meta">&#125;while(0)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ERR_IF(EXP)   \</span></span><br><span class="line"><span class="meta">do                    \</span></span><br><span class="line"><span class="meta">&#123;                     \</span></span><br><span class="line"><span class="meta">    <span class="keyword">if</span> (EXP)          \</span></span><br><span class="line"><span class="meta">        print(RED(<span class="string">&quot;error: &quot;</span>), #EXP <span class="string">&quot;\n&quot;</span>); \</span></span><br><span class="line"><span class="meta">&#125;while(0)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ERR_EXIT_IF(EXP)\</span></span><br><span class="line"><span class="meta">do                    \</span></span><br><span class="line"><span class="meta">&#123;                     \</span></span><br><span class="line"><span class="meta">    <span class="keyword">if</span> (EXP)          \</span></span><br><span class="line"><span class="meta">    &#123;                 \</span></span><br><span class="line"><span class="meta">        print(RED(<span class="string">&quot;error: &quot;</span>), #EXP <span class="string">&quot;\n&quot;</span>); \</span></span><br><span class="line"><span class="meta">        exit(-1);\</span></span><br><span class="line"><span class="meta">    &#125;\</span></span><br><span class="line"><span class="meta">&#125;while(0)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印数组宏</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印数组宏控制打印方式</span></span><br><span class="line"><span class="comment">// 0: 数组元素之间用空格隔开</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> printArrTypeSpace 0</span></span><br><span class="line"><span class="comment">// 1: 数组元素之间用换行符隔开</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> printArrTypeln    1</span></span><br><span class="line"><span class="comment">// 2: 打印数组元素和元素下标</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> printArrTypeName  2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取数组长度宏</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GET_ARR_LEN(arrobj) \</span></span><br><span class="line"><span class="meta">(_Generic((arrobj),\</span></span><br><span class="line"><span class="meta">    char *:  strlen((const char *)arrobj),\</span></span><br><span class="line"><span class="meta">    default:      (sizeof(arrobj) / sizeof(arrobj[0]))\</span></span><br><span class="line"><span class="meta">    ))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印数组宏， 其中type是数组的元素之间按照什么方式打印</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> printArr(ArrName, type) &#123;     \</span></span><br><span class="line"><span class="meta">    for (int i = 0; i &lt; GET_ARR_LEN(ArrName); i++)    \</span></span><br><span class="line"><span class="meta">    &#123;                                       \</span></span><br><span class="line"><span class="meta">        <span class="keyword">if</span>(type == printArrTypeName)        \</span></span><br><span class="line"><span class="meta">        &#123;                                   \</span></span><br><span class="line"><span class="meta">            print(#ArrName);                \</span></span><br><span class="line"><span class="meta">            print(<span class="string">&quot;[&quot;</span>);                     \</span></span><br><span class="line"><span class="meta">            print(i);                       \</span></span><br><span class="line"><span class="meta">            print(<span class="string">&quot;] = &quot;</span>);                  \</span></span><br><span class="line"><span class="meta">            println(ArrName[i]);            \</span></span><br><span class="line"><span class="meta">        &#125;                                   \</span></span><br><span class="line"><span class="meta">        <span class="keyword">else</span>                                \</span></span><br><span class="line"><span class="meta">        &#123;                                   \</span></span><br><span class="line"><span class="meta">            print(ArrName[i]);              \</span></span><br><span class="line"><span class="meta">            <span class="keyword">if</span>(type)    print(<span class="string">&quot;\n&quot;</span>);        \</span></span><br><span class="line"><span class="meta">            <span class="keyword">else</span>        print(<span class="string">&quot; &quot;</span>);         \</span></span><br><span class="line"><span class="meta">        &#125;                                   \</span></span><br><span class="line"><span class="meta">    &#125;                                       \</span></span><br><span class="line"><span class="meta">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> printlnArr(ArrName, type) &#123;printArr(ArrName, type); print(<span class="string">&quot;\n&quot;</span>);&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// !__Print_H__</span></span></span><br></pre></td></tr></table></figure><h2 id="测试程序-main-c（V1-7-V1-8）"><a href="#测试程序-main-c（V1-7-V1-8）" class="headerlink" title="测试程序 main.c（V1.7&#x2F;V1.8）"></a>测试程序 main.c（V1.7&#x2F;V1.8）</h2><figure class="highlight c"><figcaption><span>main.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;Print.h&quot;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> char_a = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> char_b = <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">short</span> <span class="type">int</span> sint_c = <span class="number">2</span>;</span><br><span class="line">    <span class="type">int</span> int_d = <span class="number">3</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> uint_e = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> ll_f = <span class="number">5l</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> ull_g = <span class="number">6l</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">float</span> float_h = <span class="number">7.7</span>;</span><br><span class="line">    <span class="type">double</span> double_i = <span class="number">8.8f</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">const</span> <span class="type">char</span>* ccharx_j = <span class="string">&quot;999&quot;</span>;</span><br><span class="line">    <span class="type">char</span>* charx_k = <span class="string">&quot;1234567890&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> *intx_l = &amp;int_d;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> *llx_m = &amp;ll_f;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> *ullx_n = &amp;ull_g;</span><br><span class="line"></span><br><span class="line">    <span class="type">float</span> *o = &amp;float_h;</span><br><span class="line">    <span class="type">double</span> *p = &amp;double_i;</span><br><span class="line"></span><br><span class="line">    println(char_a, char_b, sint_c, int_d, uint_e, ll_f, ull_g, float_h, double_i);</span><br><span class="line">    println(ccharx_j, charx_k, intx_l, llx_m, ullx_n, o, p);</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> num_array[<span class="number">10</span>] = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>&#125;;</span><br><span class="line">    <span class="type">char</span> ccl_str[<span class="number">10</span>] = <span class="string">&quot;ccl&quot;</span>;</span><br><span class="line"></span><br><span class="line">    println(GET_ARR_LEN(num_array));</span><br><span class="line">    printlnArr(num_array, printArrTypeSpace);</span><br><span class="line"></span><br><span class="line">    println(GET_ARR_LEN(ccl_str));</span><br><span class="line">    printlnArr(ccl_str, printArrTypeName);</span><br><span class="line"></span><br><span class="line">    println(GET_ARR_LEN(<span class="string">&quot;987654321&quot;</span>));</span><br><span class="line"></span><br><span class="line">    TestColor(<span class="string">&quot;ccl is a boy&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    TestColor(char_a);</span><br><span class="line"></span><br><span class="line">    print(RED(<span class="number">2</span>));</span><br><span class="line">    print();</span><br><span class="line">    println(YELLOW(<span class="number">3.3</span>));</span><br><span class="line">    println();</span><br><span class="line">    println(GREEN(<span class="number">3.3f</span>));</span><br><span class="line"></span><br><span class="line">    println(HIGHTLIGHT(<span class="string">&quot;Look at me&quot;</span>));</span><br><span class="line">    println(ITALIC(<span class="string">&quot;Look at me&quot;</span>));</span><br><span class="line">    println(_(<span class="string">&quot;Look at me&quot;</span>));</span><br><span class="line">    println(FLASGING(<span class="string">&quot;Look at me&quot;</span>));</span><br><span class="line">    println(Reversedisplay(<span class="string">&quot;Look at me&quot;</span>));</span><br><span class="line"></span><br><span class="line">    debug(char_a);</span><br><span class="line"></span><br><span class="line">    WARN_IF(char_a == <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">    ERR_IF(<span class="number">2</span> == <span class="number">2</span>);</span><br><span class="line">    ERR_EXIT_IF(<span class="number">3</span> == <span class="number">2</span>);</span><br><span class="line">    WARN_IF(char_a == <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="系列导航"><a href="#系列导航" class="headerlink" title="系列导航"></a>系列导航</h2><ul><li>上一篇：<a href="/posts/6e9f2a48/">Print.h 开发记（六）V1.6</a></li><li><a href="https://github.com/COSMICAL-CONTAINER/C_Program-Print.h">返回项目主页</a></li></ul>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/7fa03b95/</id>
    <link href="https://cosmical-container.github.io/posts/7fa03b95/"/>
    <published>2024-10-05T14:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>系列导航：<a href="/posts/6e9f2a48/">上一篇：Print.h 开发记（六）V1.6</a></p>
</blockquote>
<p>系列最终篇：V1.7 加入字体效果，V1.8 修掉遗留问题并支持无参调用——print]]>
    </summary>
    <title>Print.h 开发记（七）V1.7/V1.8</title>
    <updated>2024-10-05T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p>这次分享几个数组的冷门玩法：柔性数组、负数下标和复合字面量。</p><h2 id="一、柔性数组：通信数据包神器"><a href="#一、柔性数组：通信数据包神器" class="headerlink" title="一、柔性数组：通信数据包神器"></a>一、柔性数组：通信数据包神器</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">package</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">int</span> version;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line">    <span class="type">char</span> data[<span class="number">0</span>];       <span class="comment">// 柔性数组（C99 标准写法是 char data[]）</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><code>data[0]</code> 不占任何空间，<code>sizeof(struct package)</code> 只有 8 字节（两个 int）。用法是一次性 malloc 出”结构体 + 数据”的连续内存：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">my_package = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> package) + data_len);</span><br><span class="line">my_package-&gt;version = <span class="number">1</span>;</span><br><span class="line">my_package-&gt;size = data_len;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; data_len; i++)</span><br><span class="line">    my_package-&gt;data[i] = <span class="string">&#x27;a&#x27;</span> + i;</span><br></pre></td></tr></table></figure><p>这样包头和数据在一块内存里，一次 free 就能释放，序列化网络包时直接整块发送——网络协议和内核代码里到处都是这种写法。</p><h2 id="二、数组下标可以是负数"><a href="#二、数组下标可以是负数" class="headerlink" title="二、数组下标可以是负数"></a>二、数组下标可以是负数</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="built_in">array</span>[] = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line"><span class="type">int</span> *p = &amp;<span class="built_in">array</span>[<span class="number">-1</span>];</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, p[<span class="number">1</span>]);       <span class="comment">// 输出 array[0]，也就是 1</span></span><br></pre></td></tr></table></figure><p><code>p[1]</code> 等价于 <code>*(p + 1)</code>，而 <code>p</code> 指向 <code>array[-1]</code> 的位置，所以 <code>p[1]</code> 就是 <code>array[0]</code>。下标本质只是指针偏移，正负都合法——只要别越界访问到没分配的内存。</p><h2 id="三、复合字面量玩数组"><a href="#三、复合字面量玩数组" class="headerlink" title="三、复合字面量玩数组"></a>三、复合字面量玩数组</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(<span class="type">int</span>[][<span class="number">1</span>][<span class="number">1</span>][<span class="number">1</span>])&#123;<span class="number">1</span>, <span class="number">2</span>&#125;      <span class="comment">// 临时构造一个数组</span></span><br><span class="line">(<span class="type">int</span>[])&#123;<span class="number">1</span>&#125;                  <span class="comment">// 一维临时数组</span></span><br><span class="line">(<span class="type">void</span>* [])&#123;a&#125;               <span class="comment">// 临时指针数组</span></span><br></pre></td></tr></table></figure><p>复合字面量（C99）可以在表达式里直接造数组，配合下标能玩出很多花样，比如 <code>(int* []){a}[0][0]</code> 就是 <code>a[0]</code>。调试指针和数组关系的时候挺好用。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>柔性数组是这几个里面唯一”正经推荐用于生产”的写法，负下标和复合字面量属于了解即可——知道有这么回事，读别人代码不慌。</p><h2 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h2><figure class="highlight c"><figcaption><span>C语言数组冷知识.c</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 柔性数组，用于通信数据包处理</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">package</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">int</span> version;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line">    <span class="type">char</span> data[<span class="number">0</span>];</span><br><span class="line">&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">package</span>* <span class="title">my_package</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> data_len = <span class="number">26</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 实际上就占用8个字节(2个int)</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;sizeof(struct package) = %I64d\n&quot;</span>, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> package));</span><br><span class="line">    my_package = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> package) + data_len * <span class="keyword">sizeof</span>(<span class="type">char</span>));</span><br><span class="line"></span><br><span class="line">    my_package-&gt;version = <span class="number">1</span>;</span><br><span class="line">    my_package-&gt;size = data_len;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= data_len; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        my_package-&gt;data[i] = (i == data_len ? <span class="string">&#x27;\0&#x27;</span> : <span class="string">&#x27;a&#x27;</span> + i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, my_package-&gt;data);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 数组引用可以使用-1</span></span><br><span class="line">    <span class="type">int</span> <span class="built_in">array</span>[] = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">    <span class="type">int</span> *p = &amp;<span class="built_in">array</span>[<span class="number">-1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, p[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 强转数组类型</span></span><br><span class="line">    <span class="comment">// int a[2] = &#123;123, 234&#125;;</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (int[][1][1][1])&#123;1,2&#125;[0][0][0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (int[][1][1][1])&#123;1&#125;[0][0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (int[][1][1][1])&#123;1&#125;[0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (int[][1][1][1])&#123;1&#125;);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (int[])&#123;1&#125;);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, a);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (void* [])&#123;a&#125;[0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (void* [])&#123;a&#125;[1]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%p\n&quot;, (void* [])&#123;a&#125;[2]);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// printf(&quot;%d\n&quot;, (int* [])&#123;a&#125;[0][0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%d\n&quot;, (int* [])&#123;a&#125;[0][1] - (int* [])&#123;a&#125;[0][0]);</span></span><br><span class="line">    <span class="comment">// printf(&quot;%d\n&quot;, *(int *)(void* [])&#123;a&#125;[0] - ((int* [])&#123;a, a&#125;[1])[1]);</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/3623a4d9/</id>
    <link href="https://cosmical-container.github.io/posts/3623a4d9/"/>
    <published>2024-08-22T12:12:00.000Z</published>
    <summary>
      <![CDATA[<p>这次分享几个数组的冷门玩法：柔性数组、负数下标和复合字面量。</p>
<h2 id="一、柔性数组：通信数据包神器"><a href="#一、柔性数组：通信数据包神器" class="headerlink" title="一、柔性数组：通信数据包神器"></a>一、柔性数组]]>
    </summary>
    <title>C语言数组冷知识</title>
    <updated>2024-08-22T12:12:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>寰宇体</name>
    </author>
    <category term="C语言" scheme="https://cosmical-container.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
    <category term="C++" scheme="https://cosmical-container.github.io/tags/C/"/>
    <category term="Win32" scheme="https://cosmical-container.github.io/tags/Win32/"/>
    <content>
      <![CDATA[<p>总有些场合，文字得靠键盘一个字一个字”敲”进去——目标输入框不吃复制粘贴，或者你只是想让朋友亲眼看着一段话”自己”出现在聊天框里。AutoWrite 就是干这个的：你把文本交给它，它替你把每个键按下去。项目做了两版，正好是 Windows 下模拟输入的两条技术路线——<strong>第一版把自己变成键盘，第二版直接给窗口发消息</strong>。第一版整理后传上了 GitHub：<a href="https://github.com/COSMICAL-CONTAINER/AutoWrite">AutoWrite</a>（MIT 协议）。</p><h2 id="第一版思路：把自己变成键盘"><a href="#第一版思路：把自己变成键盘" class="headerlink" title="第一版思路：把自己变成键盘"></a>第一版思路：把自己变成键盘</h2><p>最早的版本（V0.0.1，2023 年 4 月）核心只有一个念头：程序不碰目标窗口，只负责<strong>假装自己是键盘</strong>。Windows 提供的 <code>keybd_event</code> 就是干这个的——给它一个虚拟键码（VK code），系统就当作这个键被按了一下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_word</span><span class="params">(<span class="type">int</span> vk)</span><span class="comment">//输入数字、字母</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">keybd_event</span>(vk,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);<span class="comment">// 按下</span></span><br><span class="line"><span class="built_in">keybd_event</span>(vk,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);<span class="comment">// 弹起</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>按下、弹起，两个事件，一个键就”敲”完了。字母数字都好办，麻烦的是符号——它们和主键区不是一对一的关系。比如 <code>*</code> <code>+</code> 这类键，键盘上根本没有独立的”*键”，它们住在数字小键盘上，虚拟键码是另一套：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">WriteCharacter</span><span class="params">(<span class="type">int</span> vk)</span><span class="comment">//输入符号</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">switch</span>(vk)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">case</span> <span class="number">42</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">106</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);<span class="comment">// &#x27;*&#x27; → 小键盘 VK_MULTIPLY</span></span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">106</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">43</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">107</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);<span class="comment">// &#x27;+&#x27; → 小键盘 VK_ADD</span></span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">107</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">44</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">108</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);<span class="comment">// &#x27;,&#x27; → 小键盘 VK_SEPARATOR</span></span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">108</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(VK_OEM_MINUS,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line"><span class="built_in">keybd_event</span>(VK_OEM_MINUS,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="string">&#x27;.&#x27;</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(VK_OEM_PERIOD,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line"><span class="built_in">keybd_event</span>(VK_OEM_PERIOD,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">47</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">111</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);<span class="comment">// &#x27;/&#x27; → 小键盘 VK_DIVIDE</span></span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">111</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27; &#x27;</span>:</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">0x20</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">0x20</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ASCII 码 42（<code>*</code>）要翻译成小键盘的 106（<code>VK_MULTIPLY</code>），45（<code>-</code>）对应的是 <code>VK_OEM_MINUS</code>——这张”ASCII 到虚拟键码”的对照表，就是自动打字机的第一块地基。</p><p>大小写是另一课。键盘上字母键只有一把，大小写靠的是 <strong>Shift 这个修饰键</strong>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">ShiftDown</span><span class="params">()</span><span class="comment">//按下shift</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">16</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ShiftUp</span><span class="params">()</span><span class="comment">//弹起shift</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">keybd_event</span>(<span class="number">16</span>,<span class="number">0</span>,KEYEVENTF_KEYUP,<span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>AutoWrite</code> 主循环把整套逻辑串起来：遍历字符串，按字符类别分发到上面三套机制：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">AutoWrite</span><span class="params">(std::string word)</span><span class="comment">//输入一串句子</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;word.<span class="built_in">length</span>();i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> zm=word.<span class="built_in">at</span>(i);</span><br><span class="line"><span class="keyword">if</span>((zm &gt;= <span class="string">&#x27;a&#x27;</span> &amp;&amp; zm &lt;= <span class="string">&#x27;z&#x27;</span>))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">write_word</span>(zm - <span class="number">32</span>);<span class="comment">// &#x27;a&#x27; 发 &#x27;A&#x27; 的键码，不带 Shift，出来的就是小写</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>( zm &gt;= <span class="string">&#x27;A&#x27;</span> &amp;&amp; zm &lt;= <span class="string">&#x27;Z&#x27;</span> )</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">ShiftDown</span>();</span><br><span class="line"><span class="built_in">write_word</span>(zm);</span><br><span class="line"><span class="built_in">ShiftUp</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(zm&gt;<span class="number">41</span> &amp;&amp; zm&lt;<span class="number">48</span> || zm == <span class="string">&#x27; &#x27;</span> || zm == <span class="string">&#x27;.&#x27;</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">WriteCharacter</span>(zm);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">Sleep</span>(<span class="number">10</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里藏着自动打字机的第一课：<code>write_word(zm - 32)</code> 把 <code>&#39;a&#39;</code> 换成 <code>&#39;A&#39;</code> 的键码发出去——<strong>不带 Shift 按字母键，出来的就是小写</strong>。键盘事件的世界里没有”小写键码”，只有”字母键码 + Shift 状态”的组合；真正需要 Shift 陪跑的反而是大写字母。符号分支的守卫条件 <code>zm&gt;41 &amp;&amp; zm&lt;48</code> 用的还是 ASCII 码区间——42 到 47 正是 <code>* + , - . /</code> 一族。</p><p>每敲一个键 <code>Sleep(10)</code>——太快了有些程序的消息循环跟不上，会丢字。这个”每个键之间留一口气”的节奏问题，后面还会回来找两次麻烦。当时的 main 也特别直白：两秒倒计时之后，打 10 遍 <code>nihao</code>，每遍跟一个回车，纯粹的开机自检。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::cout&lt;&lt;<span class="string">&quot;程序开始!&quot;</span>&lt;&lt;std::endl;</span><br><span class="line"><span class="built_in">Sleep</span>(<span class="number">2000</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">AutoWrite</span>(<span class="string">&quot;nihao&quot;</span>);</span><br><span class="line"><span class="built_in">Enter</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="升级成正经程序：GUI-版"><a href="#升级成正经程序：GUI-版" class="headerlink" title="升级成正经程序：GUI 版"></a>升级成正经程序：GUI 版</h2><p>命令行版能用，但每次都得先点开目标窗口再切回终端回车，手忙脚乱。于是到了 V0.0.x 的后期，它长成了一个大方的 Win32 程序：<strong>窗口里一个多行文本框，把想打的字粘进去，点一下”点我开始”，然后你有两秒钟把光标移到真正的目标输入框里</strong>，剩下的事交给程序。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> WM_COMMAND:</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">LOWORD</span>(wParam) == <span class="number">1</span>)<span class="comment">// &quot;点我开始&quot;按钮</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> n = <span class="built_in">GetWindowTextLength</span>(hEditName);<span class="comment">// 获取文本框中的文本长度</span></span><br><span class="line"><span class="keyword">if</span> (n &gt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">TCHAR *szBuffer = (TCHAR *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (n + <span class="number">1</span>));<span class="comment">// 缓冲区</span></span><br><span class="line"><span class="built_in">GetWindowText</span>(hEditName, szBuffer, n + <span class="number">1</span>);<span class="comment">// 获取文本</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 校验字符是否都在支持范围内，然后：</span></span><br><span class="line"><span class="built_in">MessageBox</span>(<span class="literal">NULL</span>, <span class="string">&quot;请在关闭我之后2s内将输入指针移动到想要输入文字的框中&quot;</span>, <span class="string">&quot;提示&quot;</span>, MB_OK | MB_ICONINFORMATION);</span><br><span class="line"><span class="built_in">Sleep</span>(<span class="number">2000</span>);</span><br><span class="line"><span class="built_in">AutoWrite</span>(szBuffer);</span><br><span class="line"><span class="built_in">free</span>(szBuffer);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><p>这里有个容易忽略的细节：拿文本框内容不能直接读，得<strong>先问长度、再按长度开缓冲区</strong>——<code>GetWindowTextLength</code> + <code>malloc(n+1)</code> + <code>GetWindowText</code> 三连。早期的版本这里是个写死的 2048 字节数组（更新记录 V0.0.1 里”增加 buffer 区设置为 2048”就是它），超长文本直接爆，V0.0.2 改成了动态申请，才有了这段标准三连。</p><p>按键的核心也从 <code>keybd_event</code> 换成了新一代的 <code>SendInput</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_word</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> vk)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// keybd_event(vk,0,0,0);</span></span><br><span class="line"><span class="comment">// keybd_event(vk,0,KEYEVENTF_KEYUP,0);</span></span><br><span class="line">INPUT inputs = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">inputs.type = INPUT_KEYBOARD;</span><br><span class="line">inputs.ki.wVk = vk;</span><br><span class="line"></span><br><span class="line">UINT uSent = <span class="built_in">SendInput</span>(<span class="number">1</span>, &amp;inputs, <span class="keyword">sizeof</span>(INPUT));</span><br><span class="line"><span class="keyword">if</span> (uSent != <span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line">cout &lt;&lt; <span class="string">L&quot;SendInput failed: &quot;</span> &lt;&lt; <span class="built_in">HRESULT_FROM_WIN32</span>(<span class="built_in">GetLastError</span>()) &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意注释里保留的老写法——<code>keybd_event</code> 和 <code>SendInput</code> 干的是同一件事，但 <code>SendInput</code> 把一批按键事件<strong>打包成原子操作</strong>一次性注入，中间不会被真实的键鼠输入插队；<code>keybd_event</code> 是一次一个事件，两个调用之间你的手如果真的碰了键盘，序列就被污染了。对自动打字机来说这个区别是实质性的，所以换代，老代码留着当注释当纪念。</p><p>这一版的完整代码在仓库里：</p><figure class="highlight cpp"><figcaption><span>AutoWrite.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VERSION <span class="string">&quot;0.0.5&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IDI_MYICON 101</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 微软虚拟键代码</span></span><br><span class="line"><span class="comment">// https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes</span></span><br><span class="line"><span class="comment">// 输入数字、字母</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_word</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> vk)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// keybd_event(vk,0,0,0);</span></span><br><span class="line">    <span class="comment">// keybd_event(vk,0,KEYEVENTF_KEYUP,0);</span></span><br><span class="line">    INPUT inputs = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">    inputs.type = INPUT_KEYBOARD;</span><br><span class="line">    inputs.ki.wVk = vk;</span><br><span class="line"></span><br><span class="line">    UINT uSent = <span class="built_in">SendInput</span>(<span class="number">1</span>, &amp;inputs, <span class="keyword">sizeof</span>(INPUT));</span><br><span class="line">    <span class="keyword">if</span> (uSent != <span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">L&quot;SendInput failed: &quot;</span> &lt;&lt; <span class="built_in">HRESULT_FROM_WIN32</span>(<span class="built_in">GetLastError</span>()) &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输入符号</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">WriteCharacter</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> vk)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (vk)</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_MINUS, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_MINUS, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;.&#x27;</span>:</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_PERIOD, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_PERIOD, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27; &#x27;</span>:</span><br><span class="line">        <span class="built_in">keybd_event</span>(<span class="number">0x20</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">keybd_event</span>(<span class="number">0x20</span>, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;\&#x27;&#x27;</span>:</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_7, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">keybd_event</span>(VK_OEM_7, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Enter</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">13</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">13</span>, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ShiftDown</span><span class="params">()</span> <span class="comment">// 按下shift</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">16</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ShiftUp</span><span class="params">()</span> <span class="comment">// 弹起shift</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">16</span>, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">CtrlV</span><span class="params">()</span> <span class="comment">// 粘贴</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">162</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">write_word</span>(<span class="string">&#x27;V&#x27;</span>);</span><br><span class="line">    <span class="built_in">keybd_event</span>(<span class="number">162</span>, <span class="number">0</span>, KEYEVENTF_KEYUP, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">AutoWrite</span><span class="params">(string word)</span> <span class="comment">// 输入一串句子</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">size_t</span> n = word.<span class="built_in">length</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">char</span> zm = word.<span class="built_in">at</span>(i);</span><br><span class="line">        <span class="keyword">if</span> ((zm &gt;= <span class="string">&#x27;a&#x27;</span> &amp;&amp; zm &lt;= <span class="string">&#x27;z&#x27;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">write_word</span>(zm - <span class="number">32</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (zm &gt;= <span class="string">&#x27;A&#x27;</span> &amp;&amp; zm &lt;= <span class="string">&#x27;Z&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">ShiftDown</span>();</span><br><span class="line">            <span class="built_in">write_word</span>(zm);</span><br><span class="line">            <span class="built_in">ShiftUp</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((zm &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; zm &lt;= <span class="string">&#x27;9&#x27;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">write_word</span>(zm);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (zm == <span class="string">&#x27; &#x27;</span> || zm == <span class="string">&#x27;.&#x27;</span> || zm == <span class="string">&#x27;-&#x27;</span> || zm == <span class="string">&#x27;\&#x27;&#x27;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">WriteCharacter</span>(zm);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">Sleep</span>(<span class="number">10</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">LRESULT CALLBACK <span class="title">WndProc</span><span class="params">(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">static</span> HWND hEditName;</span><br><span class="line">    <span class="keyword">switch</span> (msg)</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="keyword">case</span> WM_CREATE:</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 创建文本框</span></span><br><span class="line">        hEditName = <span class="built_in">CreateWindow</span>(</span><br><span class="line">            <span class="built_in">TEXT</span>(<span class="string">&quot;Edit&quot;</span>),</span><br><span class="line">            <span class="built_in">TEXT</span>(<span class="string">&quot;&quot;</span>),</span><br><span class="line">            WS_CHILD | WS_VISIBLE | WS_BORDER <span class="comment">/*边框*/</span> | WS_TABSTOP <span class="comment">/*允许使用TAB选中*/</span> | ES_MULTILINE,</span><br><span class="line">            <span class="number">10</span>, <span class="number">10</span>, <span class="number">765</span>, <span class="number">500</span>,</span><br><span class="line">            hwnd,</span><br><span class="line">            (HMENU)<span class="number">2</span>,</span><br><span class="line">            (HINSTANCE)(LONG_PTR)<span class="built_in">GetWindowLong</span>(hwnd, GWLP_HINSTANCE),</span><br><span class="line">            <span class="literal">NULL</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">case</span> WM_DESTROY:</span><br><span class="line">        <span class="built_in">PostQuitMessage</span>(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> WM_COMMAND:</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">LOWORD</span>(wParam) == <span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">bool</span> isrightstr = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> n = <span class="built_in">GetWindowTextLength</span>(hEditName); <span class="comment">// 获取文本框中的文本长度 描述：获取文本框中的文本长度;</span></span><br><span class="line">            <span class="keyword">if</span> (n &gt; <span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                TCHAR *szBuffer = (TCHAR *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>) * (n + <span class="number">1</span>)); <span class="comment">// 缓冲区</span></span><br><span class="line">                <span class="built_in">GetWindowText</span>(hEditName, szBuffer, n + <span class="number">1</span>);                 <span class="comment">// 获取文本</span></span><br><span class="line">                szBuffer[n + <span class="number">1</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">for</span> (<span class="type">char</span> c = szBuffer[<span class="number">0</span>]; c != <span class="string">&#x27;\0&#x27;</span>; c = szBuffer[++n])</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">// 检查字符串是否符合要求</span></span><br><span class="line">                    <span class="keyword">if</span> (!((c &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;9&#x27;</span>) || (c &gt;= <span class="string">&#x27;a&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;z&#x27;</span>) || (c &gt;= <span class="string">&#x27;A&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;Z&#x27;</span>) || c == <span class="string">&#x27; &#x27;</span> || c == <span class="string">&#x27;.&#x27;</span> || c == <span class="string">&#x27;\&#x27;&#x27;</span>))</span><br><span class="line">                    &#123;</span><br><span class="line">                        isrightstr = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (isrightstr)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">MessageBox</span>(<span class="literal">NULL</span>, <span class="string">&quot;请在关闭我之后2s内将输入指针移动到想要输入文字的框中&quot;</span>, <span class="string">&quot;提示&quot;</span>, MB_OK | MB_ICONINFORMATION);</span><br><span class="line">                    <span class="built_in">Sleep</span>(<span class="number">2000</span>);</span><br><span class="line">                    <span class="built_in">AutoWrite</span>(szBuffer);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">MessageBox</span>(<span class="literal">NULL</span>, <span class="string">&quot;不是有效的字符串&quot;</span>, <span class="string">&quot;提示&quot;</span>, MB_OK | MB_ICONINFORMATION);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="built_in">free</span>(szBuffer);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">MessageBox</span>(<span class="literal">NULL</span>, <span class="string">&quot;你TM倒是输入啊&quot;</span>, <span class="string">&quot;提示&quot;</span>, MB_OK | MB_ICONWARNING);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">DefWindowProc</span>(hwnd, msg, wParam, lParam);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> WINAPI <span class="title">WinMain</span><span class="params">(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, <span class="type">int</span> nCmdShow)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">ShowWindow</span>(<span class="built_in">GetConsoleWindow</span>(), SW_HIDE);</span><br><span class="line"></span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> CLASS_NAME[] = <span class="string">&quot;Hello, World!&quot;</span>;</span><br><span class="line"></span><br><span class="line">    WNDCLASS wc = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">    wc.lpfnWndProc = WndProc;</span><br><span class="line">    wc.hInstance = hInstance;</span><br><span class="line">    wc.lpszClassName = CLASS_NAME;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// HICON hIcon = (HICON)LoadImage(NULL, &quot;MYICON.ico&quot;, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);</span></span><br><span class="line">    HICON hIcon = <span class="built_in">LoadIcon</span>(<span class="built_in">GetModuleHandle</span>(<span class="literal">NULL</span>), <span class="built_in">MAKEINTRESOURCE</span>(IDI_MYICON));</span><br><span class="line"></span><br><span class="line">    wc.hIcon = hIcon;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 其他窗口类成员的初始化</span></span><br><span class="line">    <span class="built_in">RegisterClass</span>(&amp;wc);</span><br><span class="line"></span><br><span class="line">    HWND hwnd = <span class="built_in">CreateWindowEx</span>(</span><br><span class="line">        <span class="number">0</span>,                                                                   <span class="comment">// Optional window styles.</span></span><br><span class="line">        CLASS_NAME,                                                          <span class="comment">// Window class</span></span><br><span class="line">        (<span class="string">&quot;AutoWrite - 自动打字机 作者：寰宇体 V&quot;</span> + <span class="built_in">string</span>(VERSION)).<span class="built_in">c_str</span>(), <span class="comment">// Window text</span></span><br><span class="line">        WS_OVERLAPPEDWINDOW,                                                 <span class="comment">// Window style</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// Size and position</span></span><br><span class="line">        <span class="number">100</span>, <span class="number">100</span>, <span class="number">800</span>, <span class="number">600</span>,</span><br><span class="line"></span><br><span class="line">        <span class="literal">NULL</span>,      <span class="comment">// Parent window</span></span><br><span class="line">        <span class="literal">NULL</span>,      <span class="comment">// Menu</span></span><br><span class="line">        hInstance, <span class="comment">// Instance handle</span></span><br><span class="line">        <span class="literal">NULL</span>       <span class="comment">// Additional application data</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (hwnd == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">SendMessage</span>(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Add button</span></span><br><span class="line">    <span class="built_in">CreateWindow</span>(</span><br><span class="line">        <span class="string">&quot;BUTTON&quot;</span>,                                              <span class="comment">// Predefined class; Unicode assumed</span></span><br><span class="line">        <span class="string">&quot;点我开始&quot;</span>,                                            <span class="comment">// Button text</span></span><br><span class="line">        WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, <span class="comment">// Styles</span></span><br><span class="line">        <span class="number">350</span>,                                                   <span class="comment">// x position</span></span><br><span class="line">        <span class="number">525</span>,                                                   <span class="comment">// y position</span></span><br><span class="line">        <span class="number">100</span>,                                                   <span class="comment">// Button width</span></span><br><span class="line">        <span class="number">30</span>,                                                    <span class="comment">// Button height</span></span><br><span class="line">        hwnd,                                                  <span class="comment">// Parent window</span></span><br><span class="line">        (HMENU)<span class="number">1</span>,                                              <span class="comment">// Button identifier.</span></span><br><span class="line">        (HINSTANCE)(LONG_PTR)<span class="built_in">GetWindowLong</span>(hwnd, GWLP_HINSTANCE),</span><br><span class="line">        <span class="literal">NULL</span>); <span class="comment">// Pointer not needed.</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">ShowWindow</span>(hwnd, nCmdShow);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">MessageBox</span>(hwnd, (<span class="string">&quot;本代码由寰宇体制作 建议不要输入太多字符 版本&quot;</span> + <span class="built_in">string</span>(VERSION)).<span class="built_in">c_str</span>(), <span class="string">&quot;提示&quot;</span>, MB_OK);</span><br><span class="line"></span><br><span class="line">    MSG msg = &#123;&#125;;</span><br><span class="line">    <span class="keyword">while</span> (<span class="built_in">GetMessage</span>(&amp;msg, <span class="literal">NULL</span>, <span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">TranslateMessage</span>(&amp;msg);</span><br><span class="line">        <span class="built_in">DispatchMessage</span>(&amp;msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这一版还有两个工程化的小升级值得记一笔。<strong>图标不再从文件加载</strong>：早期注释里的写法是运行时 <code>LoadImage</code> 去 读 <code>MYICON.ico</code>，图标文件和 exe 分家，发给别人就丢图标；后来改成 <code>.rc</code> 资源脚本把 ico 编进 exe，<code>LoadIcon(MAKEINTRESOURCE(IDI_MYICON))</code> 直接从模块里取。<strong>控制台窗口被藏起来</strong>：<code>ShowWindow(GetConsoleWindow(), SW_HIDE)</code>，编译出的程序是个纯粹的窗口应用，背后不带一个黑框。</p><h2 id="更新记录：五个版本号，五个-bug"><a href="#更新记录：五个版本号，五个-bug" class="headerlink" title="更新记录：五个版本号，五个 bug"></a>更新记录：五个版本号，五个 bug</h2><p>这个项目的文件夹里一直躺着一份手写的更新记录，是最朴素但也最真实的开发日志：</p><figure class="highlight text"><figcaption><span>更新记录.txt</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">AutoWrite V0.0.1</span><br><span class="line">0.建立新工程</span><br><span class="line">1.完成基本功能的书写</span><br><span class="line">2.在增加buffer区设置为2048</span><br><span class="line"></span><br><span class="line">AutoWrite V0.0.2</span><br><span class="line">0.修复个别电脑缺少dll文件的bug，使用静态编译dll办法</span><br><span class="line">1.移除buffer大小限制，使用动态申请内存建立buffer区</span><br><span class="line">2.增加打字前的提示窗口，可以将窗口拖拽到对应的文本控件上</span><br><span class="line"></span><br><span class="line">AutoWrite V0.0.3</span><br><span class="line">0.修复不能输入数字的bug</span><br><span class="line"></span><br><span class="line">AutoWrite V0.0.4</span><br><span class="line">0.修复最后一个字母不会打印的bug</span><br><span class="line"></span><br><span class="line">AutoWrite V0.0.5</span><br><span class="line">0.修复不能输入&#x27;\&#x27;&#x27;字符的bug</span><br></pre></td></tr></table></figure><p>五条记录里有三条是”某类字符打不出来”：数字、最后一个字母、单引号。这正是模拟键盘路线的宿命——<strong>每一种字符都得有人替它安排好按哪个键</strong>。数字忘了接、循环边界少走一步、引号这种藏在 <code>VK_OEM_7</code> 里的冷门键，谁没安排谁就打不出来。V0.0.5 把单引号补进 <code>WriteCharacter</code> 的 switch 之后，这个字符集就冻结了：字母、数字、空格、<code>-</code>、<code>.</code>、<code>&#39;</code>，GUI 里的合法性校验也是按这张表来的。</p><p>V0.0.2 的”静态编译 dll 办法”值得单独说：程序在自己电脑上跑得好好的，发到别人电脑上弹”缺少 xxx.dll”——动态链接的运行库别人机器上没有。<code>-static-libgcc -static-libstdc++</code> 把运行库直接打进 exe，体积大一点，换来双击就能跑。</p><h2 id="第二版：换掉整个输入模型"><a href="#第二版：换掉整个输入模型" class="headerlink" title="第二版：换掉整个输入模型"></a>第二版：换掉整个输入模型</h2><p>键盘模拟用了一年多，2024 年年中重写时我决定换掉整个模型。模拟键盘的本质是<strong>往系统级输入队列里插键</strong>，字打给”当前焦点”——它快不了（每键之间要 Sleep），也精准不了（焦点飘了就打错地方）。更优雅的思路是：<strong>拿到目标窗口的句柄，直接把字符消息塞进它的消息队列</strong>，绕过键盘状态，定点投递。</p><p>定点投递的第一步是拿到”真正有输入焦点”的那个控件句柄。直接 <code>GetForegroundWindow</code> 拿到的是主窗口，而光标闪动的往往是里面的子控件（比如聊天输入框）。这需要一个不太出名的 API 三连：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获得当前激活的窗口句柄</span></span><br><span class="line">wnd = GetForegroundWindow();</span><br><span class="line"><span class="comment">// 获取本身的线程ID</span></span><br><span class="line">DWORD SelfThreadId = GetCurrentThreadId();</span><br><span class="line"><span class="comment">// 根据窗口句柄获取线程ID</span></span><br><span class="line">DWORD ForeThreadId = GetWindowThreadProcessId(wnd, <span class="literal">NULL</span>);</span><br><span class="line"><span class="comment">// 附加线程</span></span><br><span class="line">AttachThreadInput(ForeThreadId, SelfThreadId, <span class="number">1</span>);</span><br><span class="line"><span class="comment">// 获取具有输入焦点的窗口句柄</span></span><br><span class="line">wnd = GetFocus();</span><br><span class="line"><span class="comment">// 取消附加的线程</span></span><br><span class="line">AttachThreadInput(ForeThreadId, SelfThreadId, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p><code>GetFocus</code> 只能拿到<strong>自己线程内</strong>的焦点窗口，对别的线程无能为力。<code>AttachThreadInput</code> 把自己的线程暂时”挂”到目标窗口的输入线程上，共享同一份输入状态——这时 <code>GetFocus</code> 就能拿到对方线程里真正闪着光标的控件了。用完立刻解除附加。这是 Win32 时代留下的老技巧，MSDN 对它的警告一大堆，但”拿别家程序的焦点控件句柄”这一件事，至今没有更体面的办法。</p><p>拿到句柄之后，字符就不用”按键”了，直接 <code>PostMessage</code> 投递：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; <span class="built_in">strlen</span>(s); )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>((s[j] &amp; <span class="number">0x80</span>) &gt;&gt; <span class="number">7</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PostMessageA(wnd, WM_IME_CHAR, (WPARAM)transmit(&amp;s[j]), <span class="number">0</span>);</span><br><span class="line">        j += <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        PostMessageA(wnd, WM_CHAR, (WPARAM)s[j], <span class="number">0</span>);</span><br><span class="line">        j += <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Sleep(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ASCII 字符走 <code>WM_CHAR</code>，一个字节一步；<code>(s[j] &amp; 0x80) &gt;&gt; 7</code> 判断最高位是不是 1——GBK 编码下中文每个字节的最高位都是 1，这是区分中英文最朴素也最好用的办法。是中文就走 <code>WM_IME_CHAR</code>，一次投递<strong>两个字节</strong>。</p><p>而这两个字节的组装方式，是整个重写版里最有趣的一段：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> <span class="type">int</span> <span class="title function_">transmit</span><span class="params">(<span class="type">char</span>* s)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> (((*(<span class="type">int</span> *)s) &amp; <span class="number">0x0000FF00</span>) &gt;&gt; <span class="number">8</span>) + (((*(<span class="type">int</span> *)s) &amp; <span class="number">0x000000FF</span>) &lt;&lt; <span class="number">8</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一行看着像乱码的位运算，干的事是<strong>字节对调</strong>。拆开看：GBK 编码的”测”是 <code>0xB2 0xE2</code>，在内存里按顺序躺在 <code>s[0]</code>、<code>s[1]</code>。小端机器把这两个字节读进 int，低地址在低位——<code>&amp; 0xFF</code> 取出来的是 <code>s[0]</code>，<code>&amp; 0xFF00 &gt;&gt; 8</code> 取出来的是 <code>s[1]</code>。<code>WM_IME_CHAR</code> 的约定是 <strong>wParam 高 8 位放首字节（lead byte），低 8 位放次字节</strong>，而内存顺序恰好相反，所以要把两个字节换个位置再拼起来：<code>s[1]</code> 原地取高八位腾地方，<code>s[0]</code> 左移八位坐进去，<code>0xB2E2</code> 完成对调。没有这步对调，目标窗口收到的每个”中文”都是乱码。</p><p>这个模型的字符串节奏也和键盘版完全不同：<code>Sleep(1)</code> 甚至可以更激进，因为消息投递没有键盘缓冲区排队的压力；回车单独用 <code>SendInput</code> 模拟——字符走定点投递，而”回车”往往要触发的是对话框层面的默认按钮（比如聊天窗口的”发送”），那是主窗口的消息循环在处理，模拟一个真实的回车键最保险。</p><h2 id="字库：把要打的字和打字机分开"><a href="#字库：把要打的字和打字机分开" class="headerlink" title="字库：把要打的字和打字机分开"></a>字库：把要打的字和打字机分开</h2><p>重写版还有个工程上的小进步：<strong>要打的文本不再写死在 main 里，而是集中放到一个”字库”头文件</strong> <code>AutoWriteFont.h</code> 里，用字符串数组组织，一行一句，空字符串结尾：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> *guisuishou[] = &#123;</span><br><span class="line">                      <span class="string">&quot;龟虽寿&quot;</span>,</span><br><span class="line">                <span class="string">&quot;东汉末年/三国·曹操&quot;</span> ,</span><br><span class="line">                <span class="string">&quot;神龟虽寿,犹有竟时；&quot;</span>,</span><br><span class="line">                <span class="string">&quot;腾蛇乘雾,终为土灰。&quot;</span>,</span><br><span class="line">                <span class="string">&quot;老骥伏枥,志在千里；&quot;</span>,</span><br><span class="line">                <span class="string">&quot;烈士暮年,壮心不已。&quot;</span>,</span><br><span class="line">                <span class="string">&quot;&quot;</span>,</span><br><span class="line">            &#125;;</span><br></pre></td></tr></table></figure><p>诗、段子、长文本各占一个数组，<code>main</code> 里想打哪段就传哪段。字库和打字机分离之后，加新内容不用碰任何逻辑代码。</p><p>长文本还有个实际问题：一次性灌进去，中间的标点不该停顿吗？重写版自己写了一个 <code>my_strtok</code>——标准库 <code>strtok</code> 只认单字节分隔符，全角的”，！。？”在 GBK 里是两个字节，硬切会把汉字腰斩。自研版在比较时多加一条：<strong>两边都是全角标点区间（<code>0xFF01</code>~&#96;0xFF5E&#96;）的字符也算匹配</strong>，然后把长文按”，！。？”切成句子，一句一句投递。这个函数是先单独扔进 <code>test.c</code> 里测通了，才搬回主程序的——先写测试再集成，对这种字符串切片函数来说性价比极高。</p><h2 id="两条路线，各自的位置"><a href="#两条路线，各自的位置" class="headerlink" title="两条路线，各自的位置"></a>两条路线，各自的位置</h2><table><thead><tr><th></th><th>第一版：模拟键盘</th><th>第二版：消息投递</th></tr></thead><tbody><tr><td>原理</td><td><code>SendInput</code> 往系统输入队列插键</td><td><code>PostMessage</code> 直达控件消息队列</td></tr><tr><td>打给谁</td><td>当前焦点窗口（谁有焦点打谁）</td><td>指定句柄（指哪打哪）</td></tr><tr><td>中文</td><td>要先切输入法，靠 IME 逐字上屏</td><td><code>WM_IME_CHAR</code> 直接投双字节</td></tr><tr><td>速度</td><td>每键 <code>Sleep(10)</code> 起步</td><td><code>Sleep(1)</code> 都算奢侈</td></tr><tr><td>盲区</td><td>无——任何程序都当真键盘</td><td>只对标准 Edit 类控件生效</td></tr></tbody></table><p>模拟键盘是”万能的笨办法”：慢，但所有程序都把它当真键盘，自绘输入框、游戏、甚至 BIOS 级别的界面都吃这一套。消息投递是”聪明的小路”：快、准、能打中文，但离开标准控件就失灵。AutoWrite 两版都留着——工具箱里多一把枪，永远不亏。</p><p>第二版的完整代码和字库目前躺在本地工程里，仓库里的是第一版的定稿。如果这个从”假装键盘”进化到”直接写信”的小东西对你有启发，源码在 <a href="https://github.com/COSMICAL-CONTAINER/AutoWrite">AutoWrite</a>（MIT），随便玩。</p>]]>
    </content>
    <id>https://cosmical-container.github.io/posts/5dde1648/</id>
    <link href="https://cosmical-container.github.io/posts/5dde1648/"/>
    <published>2024-08-19T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>总有些场合，文字得靠键盘一个字一个字”敲”进去——目标输入框不吃复制粘贴，或者你只是想让朋友亲眼看着一段话”自己”出现在聊天框里。AutoWrite 就是干这个的：你把文本交给它，它替你把每个键按下去。项目做了两版，正好是 Windows 下模拟输入的两条技术路线——<st]]>
    </summary>
    <title>AutoWrite 自动打字机开发记：从模拟按键到发送消息</title>
    <updated>2024-08-19T13:30:00.000Z</updated>
  </entry>
</feed>
