<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>harkerhand</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://www.harkerhand.cn/favicon.ico</icon>
  <id>https://www.harkerhand.cn/</id>
  <link href="https://www.harkerhand.cn/" rel="alternate"/>
  <link href="https://www.harkerhand.cn/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, harkerhand</rights>
  <subtitle>World of Harker</subtitle>
  <title>Where Is Mountain</title>
  <updated>2026-04-13T07:04:42.728Z</updated>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <category term="rust" scheme="https://www.harkerhand.cn/tags/rust/"/>
    <content>
      <![CDATA[<p>在 Rust 的进阶之路上，理解所有权、生命周期和异步模型是跨越“陡峭曲线”的关键。本文根据“看代码说输出”的经典考察点，深度解析五个极具代表性的 Rust 技术点及其底层原理。</p><span id="more"></span><h3 id="1-引用的生命周期（经典字符串）"><a href="#1-引用的生命周期（经典字符串）" class="headerlink" title="1. 引用的生命周期（经典字符串）"></a>1. 引用的生命周期（经典字符串）</h3><p><strong>场景：</strong><br>当我们在一个函数中比较两个字符串并返回最长的一个时，生命周期标注决定了返回值的有效范围。</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">longest</span>&lt;<span class="hljs-symbol">&#x27;a</span>&gt;(x: &amp;<span class="hljs-symbol">&#x27;a</span> <span class="hljs-type">str</span>, y: &amp;<span class="hljs-symbol">&#x27;a</span> <span class="hljs-type">str</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">&#x27;a</span> <span class="hljs-type">str</span> &#123;<br>    <span class="hljs-keyword">if</span> x.<span class="hljs-title function_ invoke__">len</span>() &gt; y.<span class="hljs-title function_ invoke__">len</span>() &#123; x &#125; <span class="hljs-keyword">else</span> &#123; y &#125;<br>&#125;<br><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">s1</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;long&quot;</span>);<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;<br>    &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">s2</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;xyz&quot;</span>);<br>        result = <span class="hljs-title function_ invoke__">longest</span>(s1.<span class="hljs-title function_ invoke__">as_str</span>(), s2.<span class="hljs-title function_ invoke__">as_str</span>());<br>        <span class="hljs-comment">// println!(&quot;The longest string is &#123;&#125;&quot;, result); // 此时正常</span><br>    &#125;<br>    <span class="hljs-comment">// println!(&quot;The longest string is &#123;&#125;&quot;, result); // 编译报错！</span><br>&#125;<br></code></pre></td></tr></table></figure><p><strong>深度解析：</strong></p><ul><li><strong>编译器视角</strong>：尽管 <code>s1</code> 实际上比 <code>s2</code> 长，返回的本应是 <code>s1</code> 的引用，但编译器通过签名声明推断：<code>result</code> 的生命周期是 <code>x</code> 和 <code>y</code> 的交集（即最短的那个）。</li><li><strong>生命周期约束</strong>：由于 <code>s2</code> 在内部作用域结束时被销毁，编译器判定 <code>result</code> 在该作用域外不再安全。这是 Rust 内存安全检查的“保守性”体现。</li></ul><h3 id="2-运行时多态-Trait-Object"><a href="#2-运行时多态-Trait-Object" class="headerlink" title="2. 运行时多态 (Trait Object)"></a>2. 运行时多态 (Trait Object)</h3><p><strong>核心点：</strong><br>Rust 默认通过泛型实现静态派发（单态化），而运行时多态（动态派发）则依赖 <code>dyn Trait</code>。</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Speaker</span> &#123; <span class="hljs-keyword">fn</span> <span class="hljs-title function_">speak</span>(&amp;<span class="hljs-keyword">self</span>); &#125;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Cat</span>;<br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Speaker</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Cat</span> &#123; <span class="hljs-keyword">fn</span> <span class="hljs-title function_">speak</span>(&amp;<span class="hljs-keyword">self</span>) &#123; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Meow&quot;</span>); &#125; &#125;<br><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">animal</span>: <span class="hljs-type">Box</span>&lt;<span class="hljs-keyword">dyn</span> Speaker&gt; = <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(Cat);<br>    animal.<span class="hljs-title function_ invoke__">speak</span>();<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>底层原理：</strong></p><ul><li><strong>胖指针 (Fat Pointer)</strong>：<code>Box&lt;dyn Speaker&gt;</code> 在内存中由两个指针组成：一个指向具体的 <code>Cat</code> 数据，另一个指向 <code>vtable</code>（虚函数表）。</li><li><strong>运行时开销</strong>：通过 <code>vtable</code>查找函数地址会带来极小的运行时性能损耗，但在处理“异构集合”（如 <code>Vec&lt;Box&lt;dyn Speaker&gt;&gt;</code>）时这是必需的。</li></ul><h3 id="3-Rc-循环引用与资源泄漏"><a href="#3-Rc-循环引用与资源泄漏" class="headerlink" title="3. Rc 循环引用与资源泄漏"></a>3. Rc 循环引用与资源泄漏</h3><p><strong>陷阱描述：</strong><br>引用计数（Reference Counting）并非万能，在 A 持有 B、B 持有 A 的场景下，会导致内存永远无法释放。</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">use</span> std::rc::Rc;<br><span class="hljs-keyword">use</span> std::cell::RefCell;<br><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Node</span> &#123;<br>    next: RefCell&lt;<span class="hljs-type">Option</span>&lt;Rc&lt;Node&gt;&gt;&gt;,<br>&#125;<br><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">a</span> = Rc::<span class="hljs-title function_ invoke__">new</span>(Node &#123; next: RefCell::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-literal">None</span>) &#125;);<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">b</span> = Rc::<span class="hljs-title function_ invoke__">new</span>(Node &#123; next: RefCell::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-title function_ invoke__">Some</span>(Rc::<span class="hljs-title function_ invoke__">clone</span>(&amp;a))) &#125;);<br>    *a.next.<span class="hljs-title function_ invoke__">borrow_mut</span>() = <span class="hljs-title function_ invoke__">Some</span>(Rc::<span class="hljs-title function_ invoke__">clone</span>(&amp;b)); <span class="hljs-comment">// 形成循环引用</span><br>    <span class="hljs-comment">// a 和 b 的引用计数均为 2，程序结束时仍不释放</span><br>&#125;<br></code></pre></td></tr></table></figure><p><strong>解决方案：</strong></p><ul><li><strong>Weak Reference</strong>：使用 <code>std::rc::Weak</code> 替代其中一方的 <code>Rc</code>。弱引用不增加 <code>strong_count</code>，不会阻止对象被 <code>drop</code>。</li></ul><h3 id="4-spawn-捕获变量与作用域-Scope"><a href="#4-spawn-捕获变量与作用域-Scope" class="headerlink" title="4. spawn 捕获变量与作用域 (Scope)"></a>4. <code>spawn</code> 捕获变量与作用域 (Scope)</h3><p><strong>异步陷阱：</strong><br>在异步环境中，通过 <code>tokio::spawn</code> 启动任务时，编译器对闭包捕获的变量有极其严格的要求。</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">name</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Rustacean&quot;</span>);<br>tokio::<span class="hljs-title function_ invoke__">spawn</span>(<span class="hljs-keyword">async</span> &#123;<br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Hello, &#123;&#125;&quot;</span>, name); <span class="hljs-comment">// 报错：`name` 可能活得不够久</span><br>&#125;);<br></code></pre></td></tr></table></figure><p><strong>原因分析与 Scope：</strong></p><ul><li><strong>‘static 约束</strong>：<code>tokio::spawn</code> 创建的任务可能在后台运行很长时间。为了确保安全，它要求闭包必须满足 <code>&#39;static</code> 生命周期限制，这意味着它要么拥有变量所有权（通过 <code>move</code>），要么捕获的是全局静态变量。</li><li><strong>Scoped Threads</strong>：在标准库线程中，如果你不想使用 <code>move</code>，可以使用 <code>std::thread::scope</code>。它保证了所有派生线程在作用域结束前都会被汇合（join），因此允许捕获非 <code>&#39;static</code> 的引用。</li><li><strong>注意</strong>：在异步 Tokio 任务中，目前没有完全等价于标准库 <code>scope</code> 的稳定官方实现。通常需要通过 <code>Arc</code> 或 <code>JoinSet</code> 来管理任务生命周期。</li></ul><h3 id="5-tokio-select-与取消安全性"><a href="#5-tokio-select-与取消安全性" class="headerlink" title="5. tokio::select! 与取消安全性"></a>5. <code>tokio::select!</code> 与取消安全性</h3><p><strong>进阶难题：</strong><br><code>tokio::select!</code> 会同时轮询多个 Future，一旦其中一个完成，其余分支会立即执行 <code>Drop</code>。</p><p><strong>HTTP 获取案例：</strong><br>考虑一个带有超时机制的 HTTP 请求处理：</p><figure class="highlight rust"><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><code class="hljs rust">tokio::<span class="hljs-built_in">select!</span> &#123;<br>    res = client.<span class="hljs-title function_ invoke__">get</span>(<span class="hljs-string">&quot;https://api.example.com&quot;</span>).<span class="hljs-title function_ invoke__">send</span>() =&gt; &#123;<br>        <span class="hljs-comment">// 分支 A: 请求成功</span><br>        <span class="hljs-title function_ invoke__">process</span>(res.<span class="hljs-title function_ invoke__">unwrap</span>()).<span class="hljs-keyword">await</span>;<br>    &#125;<br>    _ = tokio::time::<span class="hljs-title function_ invoke__">sleep</span>(Duration::<span class="hljs-title function_ invoke__">from_secs</span>(<span class="hljs-number">1</span>)) =&gt; &#123;<br>        <span class="hljs-comment">// 分支 B: 超时退出</span><br>        <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Request timed out&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>什么是取消安全（Cancellation Safety）？</strong></p><ul><li><strong>安全场景</strong>：在上面的例子中，如果“超时”分支先完成，HTTP 请求对应的 Future 会被销毁。由于请求尚未完成，这通常是安全的（连接会被关闭，没有副作用）。</li><li><strong>非安全场景</strong>：如果你在 Future 内部逻辑中，是先从网络读取了数据到缓冲区，再准备解析它。如果此时被取消，缓冲区里的数据就会丢失。</li><li><strong>典型非安全操作</strong>：手动调用 <code>mpsc::Receiver::recv()</code>。如果在 <code>select!</code> 中销毁了该分支，那条被取出的消息可能已经“凭空消失”，从而导致逻辑空洞。</li></ul>]]>
    </content>
    <id>https://www.harkerhand.cn/Rust%E9%A2%98%E7%9B%AE%E8%AE%B0%E5%BD%95/</id>
    <link href="https://www.harkerhand.cn/Rust%E9%A2%98%E7%9B%AE%E8%AE%B0%E5%BD%95/"/>
    <published>2026-04-13T02:00:00.000Z</published>
    <summary>
      <![CDATA[<p>在 Rust 的进阶之路上，理解所有权、生命周期和异步模型是跨越“陡峭曲线”的关键。本文根据“看代码说输出”的经典考察点，深度解析五个极具代表性的 Rust 技术点及其底层原理。</p>]]>
    </summary>
    <title>Rust 核心机制与易错题目深度解析</title>
    <updated>2026-04-13T07:04:42.728Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <category term="C++" scheme="https://www.harkerhand.cn/tags/C/"/>
    <category term="Rust" scheme="https://www.harkerhand.cn/tags/Rust/"/>
    <content>
      <![CDATA[<h1 id="解析Rust部分数据结构的源码实现"><a href="#解析Rust部分数据结构的源码实现" class="headerlink" title="解析Rust部分数据结构的源码实现"></a>解析Rust部分数据结构的源码实现</h1><p>需要注意的是，为了方便，对源码的一些实现有改写</p><h2 id="Vec"><a href="#Vec" class="headerlink" title="Vec"></a>Vec</h2><p>从单纯的数据结构设计上，大概如下</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Vec</span>&lt;T, A: Allocator = Global&gt; &#123;<br>    buf: RawVec&lt;T, A&gt;,<br>    len: <span class="hljs-type">usize</span>,<br>&#125;<br><span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">struct</span> <span class="hljs-title class_">RawVec</span>&lt;T, A: Allocator = Global&gt; &#123;<br>    inner: RawVecInner&lt;A&gt;,<br>    _marker: PhantomData&lt;T&gt;,<br>&#125;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RawVecInner</span>&lt;A: Allocator = Global&gt; &#123;<br>    ptr: Unique&lt;<span class="hljs-type">u8</span>&gt;,<br>    cap: Cap,<br>    alloc: A,<br>&#125;<br></code></pre></td></tr></table></figure><p>其中的 <code>_marker: PhantomData&lt;T&gt;</code> 是在<strong>逻辑上</strong>标注对 T 的所有权，方便编译器做一些检查；<code>Cap</code> 会根据不通平台条件编译为不同范围的usize</p><h3 id="new"><a href="#new" class="headerlink" title="new"></a>new</h3><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">impl</span>&lt;T&gt; <span class="hljs-type">Vec</span>&lt;T&gt; &#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-type">Vec</span> &#123; buf: RawVec::<span class="hljs-title function_ invoke__">new</span>(), len: <span class="hljs-number">0</span> &#125;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;T&gt; RawVec&lt;T, Global&gt; &#123;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">Self</span>::<span class="hljs-title function_ invoke__">new_in</span>(Global)<br>    &#125;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new_in</span>(alloc: A) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-comment">// Check assumption made in `current_memory`</span><br>        <span class="hljs-keyword">const</span> &#123; <span class="hljs-built_in">assert!</span>(T::LAYOUT.<span class="hljs-title function_ invoke__">size</span>() % T::LAYOUT.<span class="hljs-title function_ invoke__">align</span>() == <span class="hljs-number">0</span>) &#125;;<br>        <span class="hljs-keyword">Self</span> &#123; inner: RawVecInner::<span class="hljs-title function_ invoke__">new_in</span>(alloc, Alignment::of::&lt;T&gt;()), _marker: PhantomData &#125;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;A: Allocator&gt; RawVecInner&lt;A&gt; &#123;<br>    <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new_in</span>(alloc: A, align: Alignment) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ptr</span> = Unique::<span class="hljs-title function_ invoke__">from_non_null</span>(NonNull::<span class="hljs-title function_ invoke__">without_provenance</span>(align.<span class="hljs-title function_ invoke__">as_nonzero</span>()));<br>        <span class="hljs-comment">// `cap: 0` means &quot;unallocated&quot;. zero-sized types are ignored.</span><br>        <span class="hljs-keyword">Self</span> &#123; ptr, cap: ZERO_CAP, alloc &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>这里有一些有意思的，首先是会有一个对齐的检查，随后创建一个满足对齐的悬垂指针，所以其实还没有分配堆内存。</p><h3 id="with-capacity"><a href="#with-capacity" class="headerlink" title="with_capacity"></a>with_capacity</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span>&lt;T&gt; <span class="hljs-type">Vec</span>&lt;T&gt; &#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">with_capacity</span>(capacity: <span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">Self</span>::<span class="hljs-title function_ invoke__">with_capacity_in</span>(capacity, Global)<br>    &#125;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">with_capacity_in</span>(capacity: <span class="hljs-type">usize</span>, alloc: A) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-type">Vec</span> &#123; buf: RawVec::<span class="hljs-title function_ invoke__">with_capacity_in</span>(capacity, alloc), len: <span class="hljs-number">0</span> &#125;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;T&gt; RawVec&lt;T, Global&gt; &#123;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">fn</span> <span class="hljs-title function_">with_capacity_in</span>(capacity: <span class="hljs-type">usize</span>, alloc: A) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">Self</span> &#123;<br>            inner: RawVecInner::<span class="hljs-title function_ invoke__">with_capacity_in</span>(capacity, alloc, T::LAYOUT),<br>            _marker: PhantomData,<br>        &#125;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;A: Allocator&gt; RawVecInner&lt;A&gt; &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">with_capacity_in</span>(capacity: <span class="hljs-type">usize</span>, alloc: A, elem_layout: Layout) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">match</span> <span class="hljs-keyword">Self</span>::<span class="hljs-title function_ invoke__">try_allocate_in</span>(capacity, AllocInit::Uninitialized, alloc, elem_layout) &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(this) =&gt; &#123;<br>                <span class="hljs-keyword">unsafe</span> &#123;<br>                    <span class="hljs-comment">// Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate.</span><br>                    hint::<span class="hljs-title function_ invoke__">assert_unchecked</span>(!this.<span class="hljs-title function_ invoke__">needs_to_grow</span>(<span class="hljs-number">0</span>, capacity, elem_layout));<br>                &#125;<br>                this<br>            &#125;<br>            <span class="hljs-title function_ invoke__">Err</span>(err) =&gt; <span class="hljs-title function_ invoke__">handle_error</span>(err),<br>        &#125;<br>    &#125;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">try_allocate_in</span>(<br>        capacity: <span class="hljs-type">usize</span>,<br>        init: AllocInit,<br>        alloc: A,<br>        elem_layout: Layout,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-keyword">Self</span>, TryReserveError&gt; &#123;<br>        <span class="hljs-comment">// We avoid `unwrap_or_else` here because it bloats the amount of</span><br>        <span class="hljs-comment">// LLVM IR generated.</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">layout</span> = <span class="hljs-keyword">match</span> <span class="hljs-title function_ invoke__">layout_array</span>(capacity, elem_layout) &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(layout) =&gt; layout,<br>            <span class="hljs-title function_ invoke__">Err</span>(_) =&gt; <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(CapacityOverflow.<span class="hljs-title function_ invoke__">into</span>()),<br>        &#125;;<br><br>        <span class="hljs-comment">// Don&#x27;t allocate here because `Drop` will not deallocate when `capacity` is 0.</span><br>        <span class="hljs-keyword">if</span> layout.<span class="hljs-title function_ invoke__">size</span>() == <span class="hljs-number">0</span> &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Ok</span>(<span class="hljs-keyword">Self</span>::<span class="hljs-title function_ invoke__">new_in</span>(alloc, elem_layout.<span class="hljs-title function_ invoke__">alignment</span>()));<br>        &#125;<br><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-keyword">match</span> init &#123;<br>            AllocInit::Uninitialized =&gt; alloc.<span class="hljs-title function_ invoke__">allocate</span>(layout),<br>            <span class="hljs-meta">#[cfg(not(no_global_oom_handling))]</span><br>            AllocInit::Zeroed =&gt; alloc.<span class="hljs-title function_ invoke__">allocate_zeroed</span>(layout),<br>        &#125;;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ptr</span> = <span class="hljs-keyword">match</span> result &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(ptr) =&gt; ptr,<br>            <span class="hljs-title function_ invoke__">Err</span>(_) =&gt; <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(AllocError &#123; layout, non_exhaustive: () &#125;.<span class="hljs-title function_ invoke__">into</span>()),<br>        &#125;;<br><br>        <span class="hljs-comment">// Allocators currently return a `NonNull&lt;[u8]&gt;` whose length</span><br>        <span class="hljs-comment">// matches the size requested. If that ever changes, the capacity</span><br>        <span class="hljs-comment">// here should change to `ptr.len() / size_of::&lt;T&gt;()`.</span><br>        <span class="hljs-title function_ invoke__">Ok</span>(<span class="hljs-keyword">Self</span> &#123;<br>            ptr: Unique::<span class="hljs-title function_ invoke__">from</span>(ptr.<span class="hljs-title function_ invoke__">cast</span>()),<br>            cap: <span class="hljs-keyword">unsafe</span> &#123; Cap::<span class="hljs-title function_ invoke__">new_unchecked</span>(capacity) &#125;,<br>            alloc,<br>        &#125;)<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>前面就是一些封装，具体后面的部分：<code>AllocInit::Uninitialized</code> 规定使用非初始化的内存分配，会快一点；随后计算所需的内存大小并分配，将得到的内存地址做包装后返回。</p><h2 id="push"><a href="#push" class="headerlink" title="push"></a>push</h2><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span>&lt;T&gt; <span class="hljs-type">Vec</span>&lt;T&gt; &#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">push</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, value: T) &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">_</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">push_mut</span>(value);<br>    &#125;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">push_mut</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, value: T) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-keyword">mut</span> T &#123;<br>        <span class="hljs-comment">// Inform codegen that the length does not change across grow_one().</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">len</span> = <span class="hljs-keyword">self</span>.len;<br>        <span class="hljs-comment">// This will panic or abort if we would allocate &gt; isize::MAX bytes</span><br>        <span class="hljs-comment">// or if the length increment would overflow for zero-sized types.</span><br>        <span class="hljs-keyword">if</span> len == <span class="hljs-keyword">self</span>.buf.<span class="hljs-title function_ invoke__">capacity</span>() &#123;<br>            <span class="hljs-keyword">self</span>.buf.<span class="hljs-title function_ invoke__">grow_one</span>();<br>        &#125;<br>        <span class="hljs-keyword">unsafe</span> &#123;<br>            <span class="hljs-keyword">let</span> <span class="hljs-variable">end</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">as_mut_ptr</span>().<span class="hljs-title function_ invoke__">add</span>(len);<br>            ptr::<span class="hljs-title function_ invoke__">write</span>(end, value);<br>            <span class="hljs-keyword">self</span>.len = len + <span class="hljs-number">1</span>;<br>            <span class="hljs-comment">// SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference.</span><br>            &amp;<span class="hljs-keyword">mut</span> *end<br>        &#125;<br>    &#125;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">as_mut_ptr</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> *<span class="hljs-keyword">mut</span> T &#123;<br>        <span class="hljs-comment">// We shadow the slice method of the same name to avoid going through</span><br>        <span class="hljs-comment">// `deref_mut`, which creates an intermediate reference.</span><br>        <span class="hljs-keyword">self</span>.buf.<span class="hljs-title function_ invoke__">ptr</span>()<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;T&gt; RawVec&lt;T, Global&gt; &#123;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">ptr</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> *<span class="hljs-keyword">mut</span> T &#123;<br>        <span class="hljs-keyword">self</span>.inner.<span class="hljs-title function_ invoke__">ptr</span>()<br>    &#125;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-keyword">fn</span> <span class="hljs-title function_">grow_one</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) &#123;<br>        <span class="hljs-comment">// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout</span><br>        <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-keyword">self</span>.inner.<span class="hljs-title function_ invoke__">grow_one</span>(T::LAYOUT) &#125;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">impl</span>&lt;A&gt; RawVecInner&lt;A&gt; &#123;<br><span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">grow_one</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, elem_layout: Layout) &#123;<br>        <span class="hljs-comment">// SAFETY: Precondition passed to caller</span><br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Err</span>(err) = <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">grow_amortized</span>(<span class="hljs-keyword">self</span>.cap.<span class="hljs-title function_ invoke__">as_inner</span>(), <span class="hljs-number">1</span>, elem_layout) &#125; &#123;<br>            <span class="hljs-title function_ invoke__">handle_error</span>(err);<br>        &#125;<br>    &#125;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">grow_amortized</span>(<br>        &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>,<br>        len: <span class="hljs-type">usize</span>,<br>        additional: <span class="hljs-type">usize</span>,<br>        elem_layout: Layout,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;(), TryReserveError&gt; &#123;<br>        <span class="hljs-comment">// This is ensured by the calling contexts.</span><br>        <span class="hljs-built_in">debug_assert!</span>(additional &gt; <span class="hljs-number">0</span>);<br><br>        <span class="hljs-keyword">if</span> elem_layout.<span class="hljs-title function_ invoke__">size</span>() == <span class="hljs-number">0</span> &#123;<br>            <span class="hljs-comment">// Since we return a capacity of `usize::MAX` when `elem_size` is</span><br>            <span class="hljs-comment">// 0, getting to here necessarily means the `RawVec` is overfull.</span><br>            <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(CapacityOverflow.<span class="hljs-title function_ invoke__">into</span>());<br>        &#125;<br><br>        <span class="hljs-comment">// Nothing we can really do about these checks, sadly.</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">required_cap</span> = len.<span class="hljs-title function_ invoke__">checked_add</span>(additional).<span class="hljs-title function_ invoke__">ok_or</span>(CapacityOverflow)?;<br><br>        <span class="hljs-comment">// This guarantees exponential growth. The doubling cannot overflow</span><br>        <span class="hljs-comment">// because `cap &lt;= isize::MAX` and the type of `cap` is `usize`.</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">cap</span> = cmp::<span class="hljs-title function_ invoke__">max</span>(<span class="hljs-keyword">self</span>.cap.<span class="hljs-title function_ invoke__">as_inner</span>() * <span class="hljs-number">2</span>, required_cap);<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">cap</span> = cmp::<span class="hljs-title function_ invoke__">max</span>(<span class="hljs-title function_ invoke__">min_non_zero_cap</span>(elem_layout.<span class="hljs-title function_ invoke__">size</span>()), cap);<br><br>        <span class="hljs-comment">// SAFETY:</span><br>        <span class="hljs-comment">// - cap &gt;= len + additional</span><br>        <span class="hljs-comment">// - other preconditions passed to caller</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ptr</span> = <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">finish_grow</span>(cap, elem_layout)? &#125;;<br><br>        <span class="hljs-comment">// SAFETY: `finish_grow` would have failed if `cap &gt; isize::MAX`</span><br>        <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">set_ptr_and_cap</span>(ptr, cap) &#125;;<br>        <span class="hljs-title function_ invoke__">Ok</span>(())<br>    &#125;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">finish_grow</span>(<br>        &amp;<span class="hljs-keyword">self</span>,<br>        cap: <span class="hljs-type">usize</span>,<br>        elem_layout: Layout,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;NonNull&lt;[<span class="hljs-type">u8</span>]&gt;, TryReserveError&gt; &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">new_layout</span> = <span class="hljs-title function_ invoke__">layout_array</span>(cap, elem_layout)?;<br><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">memory</span> = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>((ptr, old_layout)) = <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">current_memory</span>(elem_layout) &#125; &#123;<br>            <span class="hljs-comment">// FIXME(const-hack): switch to `debug_assert_eq`</span><br>            <span class="hljs-built_in">debug_assert!</span>(old_layout.<span class="hljs-title function_ invoke__">align</span>() == new_layout.<span class="hljs-title function_ invoke__">align</span>());<br>            <span class="hljs-keyword">unsafe</span> &#123;<br>                <span class="hljs-comment">// The allocator checks for alignment equality</span><br>                hint::<span class="hljs-title function_ invoke__">assert_unchecked</span>(old_layout.<span class="hljs-title function_ invoke__">align</span>() == new_layout.<span class="hljs-title function_ invoke__">align</span>());<br>                <span class="hljs-keyword">self</span>.alloc.<span class="hljs-title function_ invoke__">grow</span>(ptr, old_layout, new_layout)<br>            &#125;<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            <span class="hljs-keyword">self</span>.alloc.<span class="hljs-title function_ invoke__">allocate</span>(new_layout)<br>        &#125;;<br><br>        <span class="hljs-comment">// FIXME(const-hack): switch back to `map_err`</span><br>        <span class="hljs-keyword">match</span> memory &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(memory) =&gt; <span class="hljs-title function_ invoke__">Ok</span>(memory),<br>            <span class="hljs-title function_ invoke__">Err</span>(_) =&gt; <span class="hljs-title function_ invoke__">Err</span>(AllocError &#123; layout: new_layout, non_exhaustive: () &#125;.<span class="hljs-title function_ invoke__">into</span>()),<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>因为内存是未初始化的，所以会使用 <code>ptr::write</code> 直接向内存写数据。我们具体关注扩容逻辑，<br>$$<br>\text{new_cap} &#x3D; max(\text{old_cap} * 2, \text{old_cap} + \text{needed}, \text{min_non_zero_cap})<br>$$<br>指数倍增，并且会使用类型大小来选择保底扩容大小；具体的内存分配部分，使用alloc提供的能力，尽可能的进行原地扩展（不保证）。</p><h3 id="get"><a href="#get" class="headerlink" title="get"></a>get</h3><p>有趣的是，rust标准库对切片实现了 <code>get</code>，而 <code>Vec</code> 又可以自动 <code>Deref</code> 为切片，所以复用了这一部分能力</p><h3 id="remove"><a href="#remove" class="headerlink" title="remove"></a>remove</h3><p>读出其中的值，并批量移动内存数据</p><h3 id="insert"><a href="#insert" class="headerlink" title="insert"></a>insert</h3><p>先判断扩容，然后移动，最后写入值</p><h2 id="LinkedList"><a href="#LinkedList" class="headerlink" title="LinkedList"></a>LinkedList</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">LinkedList</span>&lt;T, A: Allocator = Global&gt; &#123;<br>    head: <span class="hljs-type">Option</span>&lt;NonNull&lt;Node&lt;T&gt;&gt;&gt;,<br>    tail: <span class="hljs-type">Option</span>&lt;NonNull&lt;Node&lt;T&gt;&gt;&gt;,<br>    len: <span class="hljs-type">usize</span>,<br>    alloc: A,<br>    marker: PhantomData&lt;<span class="hljs-type">Box</span>&lt;Node&lt;T&gt;, A&gt;&gt;,<br>&#125;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Node</span>&lt;T&gt; &#123;<br>    next: <span class="hljs-type">Option</span>&lt;NonNull&lt;Node&lt;T&gt;&gt;&gt;,<br>    prev: <span class="hljs-type">Option</span>&lt;NonNull&lt;Node&lt;T&gt;&gt;&gt;,<br>    element: T,<br>&#125;<br></code></pre></td></tr></table></figure><p>普普通通毫无亮点口牙！</p><h3 id="new-1"><a href="#new-1" class="headerlink" title="new"></a>new</h3><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">impl</span>&lt;T&gt; LinkedList&lt;T&gt; &#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        LinkedList &#123; head: <span class="hljs-literal">None</span>, tail: <span class="hljs-literal">None</span>, len: <span class="hljs-number">0</span>, alloc: Global, marker: PhantomData &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>太棒了，简洁易懂</p><h3 id="push-back"><a href="#push-back" class="headerlink" title="push_back"></a>push_back</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span>&lt;T&gt; LinkedList&lt;T&gt; &#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">push_back</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, elt: T) &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">_</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">push_back_mut</span>(elt);<br>    &#125;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">push_back_mut</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, elt: T) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-keyword">mut</span> T &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">node</span> =<br>            <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">into_non_null_with_allocator</span>(<span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new_in</span>(Node::<span class="hljs-title function_ invoke__">new</span>(elt), &amp;<span class="hljs-keyword">self</span>.alloc)).<span class="hljs-number">0</span>;<br>        <span class="hljs-comment">// SAFETY: node is a unique pointer to a node in self.alloc</span><br>        <span class="hljs-keyword">unsafe</span> &#123;<br>            <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">push_back_node</span>(node);<br>            &amp;<span class="hljs-keyword">mut</span> node.<span class="hljs-title function_ invoke__">as_mut</span>().element<br>        &#125;<br>    &#125;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">push_back_node</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, node: NonNull&lt;Node&lt;T&gt;&gt;) &#123;<br>        <span class="hljs-comment">// This method takes care not to create mutable references to whole nodes,</span><br>        <span class="hljs-comment">// to maintain validity of aliasing pointers into `element`.</span><br>        <span class="hljs-keyword">unsafe</span> &#123;<br>            (*node.<span class="hljs-title function_ invoke__">as_ptr</span>()).next = <span class="hljs-literal">None</span>;<br>            (*node.<span class="hljs-title function_ invoke__">as_ptr</span>()).prev = <span class="hljs-keyword">self</span>.tail;<br>            <span class="hljs-keyword">let</span> <span class="hljs-variable">node</span> = <span class="hljs-title function_ invoke__">Some</span>(node);<br><br>            <span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span>.tail &#123;<br>                <span class="hljs-literal">None</span> =&gt; <span class="hljs-keyword">self</span>.head = node,<br>                <span class="hljs-comment">// Not creating new mutable (unique!) references overlapping `element`.</span><br>                <span class="hljs-title function_ invoke__">Some</span>(tail) =&gt; (*tail.<span class="hljs-title function_ invoke__">as_ptr</span>()).next = node,<br>            &#125;<br><br>            <span class="hljs-keyword">self</span>.tail = node;<br>            <span class="hljs-keyword">self</span>.len += <span class="hljs-number">1</span>;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>在堆上创建 <code>Node&lt;T&gt;</code>，随后将带有所有权的 <code>Box</code> 转化为 <code>NonNull</code>；<code>push_back_node</code> 更是极为显然的逻辑。</p><h3 id="pop-back"><a href="#pop-back" class="headerlink" title="pop_back"></a>pop_back</h3><p>其实可以略了</p><h2 id="HashMap"><a href="#HashMap" class="headerlink" title="HashMap"></a>HashMap</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">HashMap</span>&lt;<br>    K,<br>    V,<br>    S = RandomState,<br>    A: Allocator = Global,<br>&gt; &#123;<br>    base: base::HashMap&lt;K, V, S, A&gt;,<br>&#125;<br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">HashMap</span>&lt;K, V, S = DefaultHashBuilder, A: Allocator = Global&gt; &#123;<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) hash_builder: S,<br>    <span class="hljs-title function_ invoke__">pub</span>(<span class="hljs-keyword">crate</span>) table: RawTable&lt;(K, V), A&gt;,<br>&#125;<br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">RawTable</span>&lt;T, A: Allocator = Global&gt; &#123;<br>    table: RawTableInner,<br>    alloc: A,<br>    <span class="hljs-comment">// Tell dropck that we own instances of T.</span><br>    marker: PhantomData&lt;T&gt;,<br>&#125;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RawTableInner</span> &#123;<br>    <span class="hljs-comment">// Mask to get an index from a hash value. The value is one less than the</span><br>    <span class="hljs-comment">// number of buckets in the table.</span><br>    bucket_mask: <span class="hljs-type">usize</span>,<br>    <span class="hljs-comment">// [Padding], T_n, ..., T1, T0, C0, C1, ...</span><br>    <span class="hljs-comment">//                              ^ points here</span><br>    ctrl: NonNull&lt;<span class="hljs-type">u8</span>&gt;,<br>    <span class="hljs-comment">// Number of elements that can be inserted before we need to grow the table</span><br>    growth_left: <span class="hljs-type">usize</span>,<br>    <span class="hljs-comment">// Number of elements in the table, only really used by len()</span><br>    items: <span class="hljs-type">usize</span>,<br>&#125;<br></code></pre></td></tr></table></figure><table><thead><tr><th><strong>字段</strong></th><th><strong>作用</strong></th><th><strong>深度解析</strong></th></tr></thead><tbody><tr><td><code>bucket_mask</code></td><td>快速取模</td><td>桶的数量永远是 $2^n$，所以 <code>hash &amp; bucket_mask</code> 效果等同于 <code>hash % capacity</code>，但位运算速度极快。</td></tr><tr><td><code>growth_left</code></td><td>扩容阈值</td><td>记录还能插入多少元素。当它降到 0 时，触发 <code>rehash</code> 扩容，防止哈希冲突过多导致性能下降。</td></tr><tr><td><code>ctrl</code></td><td>标记控制区起点</td><td>使用SIMD加速哈希匹配判断</td></tr></tbody></table><h3 id="with-capacity-1"><a href="#with-capacity-1" class="headerlink" title="with_capacity"></a>with_capacity</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">RawTableInner</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">fallible_with_capacity</span>&lt;A&gt;(<br>        alloc: &amp;A,<br>        table_layout: TableLayout,<br>        capacity: <span class="hljs-type">usize</span>,<br>        fallibility: Fallibility,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-keyword">Self</span>, TryReserveError&gt;<br>    <span class="hljs-keyword">where</span><br>        A: Allocator,<br>    &#123;<br>        <span class="hljs-keyword">if</span> capacity == <span class="hljs-number">0</span> &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(<span class="hljs-keyword">Self</span>::NEW)<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            <span class="hljs-comment">// SAFETY: We checked that we could successfully allocate the new table, and then</span><br>            <span class="hljs-comment">// initialized all control bytes with the constant `Tag::EMPTY` byte.</span><br>            <span class="hljs-keyword">unsafe</span> &#123;<br>                <span class="hljs-keyword">let</span> <span class="hljs-variable">buckets</span> = <span class="hljs-title function_ invoke__">capacity_to_buckets</span>(capacity, table_layout)<br>                    .<span class="hljs-title function_ invoke__">ok_or_else</span>(|| fallibility.<span class="hljs-title function_ invoke__">capacity_overflow</span>())?;<br><br>                <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">result</span> =<br>                    <span class="hljs-keyword">Self</span>::<span class="hljs-title function_ invoke__">new_uninitialized</span>(alloc, table_layout, buckets, fallibility)?;<br>                <span class="hljs-comment">// SAFETY: We checked that the table is allocated and therefore the table already has</span><br>                <span class="hljs-comment">// `self.bucket_mask + 1 + Group::WIDTH` number of control bytes (see TableLayout::calculate_layout_for)</span><br>                <span class="hljs-comment">// so writing `self.num_ctrl_bytes() == bucket_mask + 1 + Group::WIDTH` bytes is safe.</span><br>                result.<span class="hljs-title function_ invoke__">ctrl_slice</span>().<span class="hljs-title function_ invoke__">fill_empty</span>();<br><br>                <span class="hljs-title function_ invoke__">Ok</span>(result)<br>            &#125;<br>        &#125;<br>    &#125;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new_uninitialized</span>&lt;A&gt;(<br>        alloc: &amp;A,<br>        table_layout: TableLayout,<br>        <span class="hljs-keyword">mut</span> buckets: <span class="hljs-type">usize</span>,<br>        fallibility: Fallibility,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-keyword">Self</span>, TryReserveError&gt;<br>    <span class="hljs-keyword">where</span><br>        A: Allocator,<br>    &#123;<br>        <span class="hljs-built_in">debug_assert!</span>(buckets.<span class="hljs-title function_ invoke__">is_power_of_two</span>());<br><br>        <span class="hljs-comment">// Avoid `Option::ok_or_else` because it bloats LLVM IR.</span><br>        <span class="hljs-keyword">let</span> (layout, <span class="hljs-keyword">mut</span> ctrl_offset) = <span class="hljs-keyword">match</span> table_layout.<span class="hljs-title function_ invoke__">calculate_layout_for</span>(buckets) &#123;<br>            <span class="hljs-title function_ invoke__">Some</span>(lco) =&gt; lco,<br>            <span class="hljs-literal">None</span> =&gt; <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(fallibility.<span class="hljs-title function_ invoke__">capacity_overflow</span>()),<br>        &#125;;<br><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ptr</span>: NonNull&lt;<span class="hljs-type">u8</span>&gt; = <span class="hljs-keyword">match</span> <span class="hljs-title function_ invoke__">do_alloc</span>(alloc, layout) &#123;<br>            <span class="hljs-title function_ invoke__">Ok</span>(block) =&gt; &#123;<br>                <span class="hljs-comment">// The allocator can&#x27;t return a value smaller than was</span><br>                <span class="hljs-comment">// requested, so this can be != instead of &gt;=.</span><br>                <span class="hljs-keyword">if</span> block.<span class="hljs-title function_ invoke__">len</span>() != layout.<span class="hljs-title function_ invoke__">size</span>() &#123;<br>                    <span class="hljs-comment">// Utilize over-sized allocations.</span><br>                    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-title function_ invoke__">maximum_buckets_in</span>(block.<span class="hljs-title function_ invoke__">len</span>(), table_layout, Group::WIDTH);<br>                    <span class="hljs-built_in">debug_assert!</span>(x &gt;= buckets);<br>                    <span class="hljs-comment">// Calculate the new ctrl_offset.</span><br>                    <span class="hljs-keyword">let</span> (oversized_layout, oversized_ctrl_offset) =<br>                        <span class="hljs-keyword">match</span> table_layout.<span class="hljs-title function_ invoke__">calculate_layout_for</span>(x) &#123;<br>                            <span class="hljs-title function_ invoke__">Some</span>(lco) =&gt; lco,<br>                            <span class="hljs-literal">None</span> =&gt; <span class="hljs-keyword">unsafe</span> &#123; hint::<span class="hljs-title function_ invoke__">unreachable_unchecked</span>() &#125;,<br>                        &#125;;<br>                    <span class="hljs-built_in">debug_assert!</span>(oversized_layout.<span class="hljs-title function_ invoke__">size</span>() &lt;= block.<span class="hljs-title function_ invoke__">len</span>());<br>                    <span class="hljs-built_in">debug_assert!</span>(oversized_ctrl_offset &gt;= ctrl_offset);<br>                    ctrl_offset = oversized_ctrl_offset;<br>                    buckets = x;<br>                &#125;<br><br>                block.<span class="hljs-title function_ invoke__">cast</span>()<br>            &#125;<br>            <span class="hljs-title function_ invoke__">Err</span>(_) =&gt; <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(fallibility.<span class="hljs-title function_ invoke__">alloc_err</span>(layout)),<br>        &#125;;<br><br>        <span class="hljs-comment">// SAFETY: null pointer will be caught in above check</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ctrl</span> = NonNull::<span class="hljs-title function_ invoke__">new_unchecked</span>(ptr.<span class="hljs-title function_ invoke__">as_ptr</span>().<span class="hljs-title function_ invoke__">add</span>(ctrl_offset));<br>        <span class="hljs-title function_ invoke__">Ok</span>(<span class="hljs-keyword">Self</span> &#123;<br>            ctrl,<br>            bucket_mask: buckets - <span class="hljs-number">1</span>,<br>            items: <span class="hljs-number">0</span>,<br>            growth_left: <span class="hljs-title function_ invoke__">bucket_mask_to_capacity</span>(buckets - <span class="hljs-number">1</span>),<br>        &#125;)<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>内存刚申请下来是脏的，需要先把所有的控制字节设置为<code>0xFF</code>；在申请内存时，首先精确得到layout和控制区偏移量，随后如果分配器给出的内存超过实际申请，会进行自动扩容来利用这部分空间；最后返回的ctrl指针是控制区的起点。</p><h3 id="insert-1"><a href="#insert-1" class="headerlink" title="insert"></a>insert</h3><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">RawTableInner</span> &#123;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">find_or_find_insert_index_inner</span>(<br>        &amp;<span class="hljs-keyword">self</span>,<br>        hash: <span class="hljs-type">u64</span>,<br>        eq: &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">dyn</span> <span class="hljs-title function_ invoke__">FnMut</span>(<span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">bool</span>,<br>    ) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-type">usize</span>, <span class="hljs-type">usize</span>&gt; &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">insert_index</span> = <span class="hljs-literal">None</span>;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">tag_hash</span> = Tag::<span class="hljs-title function_ invoke__">full</span>(hash);<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">probe_seq</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">probe_seq</span>(hash);<br>        <span class="hljs-keyword">loop</span> &#123;<br>            <span class="hljs-keyword">let</span> <span class="hljs-variable">group</span> = <span class="hljs-keyword">unsafe</span> &#123; Group::<span class="hljs-title function_ invoke__">load</span>(<span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">ctrl</span>(probe_seq.pos)) &#125;;<br>            <span class="hljs-keyword">for</span> <span class="hljs-variable">bit</span> <span class="hljs-keyword">in</span> group.<span class="hljs-title function_ invoke__">match_tag</span>(tag_hash) &#123;<br>                <span class="hljs-keyword">let</span> <span class="hljs-variable">index</span> = (probe_seq.pos + bit) &amp; <span class="hljs-keyword">self</span>.bucket_mask;<br>                <span class="hljs-keyword">if</span> <span class="hljs-title function_ invoke__">likely</span>(<span class="hljs-title function_ invoke__">eq</span>(index)) &#123;<br>                    <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Ok</span>(index);<br>                &#125;<br>            &#125;<br>            <span class="hljs-keyword">if</span> <span class="hljs-title function_ invoke__">likely</span>(insert_index.<span class="hljs-title function_ invoke__">is_none</span>()) &#123;<br>                insert_index = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">find_insert_index_in_group</span>(&amp;group, &amp;probe_seq);<br>            &#125;<br>            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>(insert_index) = insert_index &#123;<br>                <span class="hljs-keyword">if</span> <span class="hljs-title function_ invoke__">likely</span>(group.<span class="hljs-title function_ invoke__">match_empty</span>().<span class="hljs-title function_ invoke__">any_bit_set</span>()) &#123;<br>                    <span class="hljs-keyword">unsafe</span> &#123;<br>                        <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(<span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">fix_insert_index</span>(insert_index));<br>                    &#125;<br>                &#125;<br>            &#125;<br>            probe_seq.<span class="hljs-title function_ invoke__">move_next</span>(<span class="hljs-keyword">self</span>.bucket_mask);<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>这部分是最有趣的，常规的算法会在hash后拿到桶的下标，并遍历匹配，这其中频繁的内存读取消耗巨大；而源码的算法是先通过控制位与哈希值的高七位做匹配，这可以借助SIMD实现批量匹配，只有匹配到的节点才会去尝试检查。太酷了！</p>]]>
    </content>
    <id>https://www.harkerhand.cn/learn-std/</id>
    <link href="https://www.harkerhand.cn/learn-std/"/>
    <published>2026-03-14T23:40:45.000Z</published>
    <summary>
      <![CDATA[<h1 id="解析Rust部分数据结构的源码实现"><a href="#解析Rust部分数据结构的源码实现" class="headerlink"]]>
    </summary>
    <title>Rust数据结构源码实现</title>
    <updated>2026-04-13T06:42:52.014Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <category term="rust" scheme="https://www.harkerhand.cn/tags/rust/"/>
    <content>
      <![CDATA[<p>在 Rust 中，多态主要通过两种方式实现：<strong>静态派发</strong> 和 <strong>动态派发</strong>。虽然泛型（静态派发）是 Rust 的首选，但在处理“异构集合”（例如一个包含不同 UI 组件的列表）时，动态派发（即 <code>dyn Trait</code>）则是不可或缺的利器。</p><p>然而，并不是所有的 Trait 都能开启动态派发。这篇文章简单聊聊：想要实现动态派发，Trait 必须满足哪些条件？底层又是如何运作的？</p><h1 id="核心概念：胖指针"><a href="#核心概念：胖指针" class="headerlink" title="核心概念：胖指针"></a>核心概念：胖指针</h1><p>在 Rust 中，当你将一个具体类转换为一个抹去类型的接口时，编译器实际上创建了一个<strong>胖指针</strong>。</p><p>一个胖指针占据两个 <code>usize</code> 的空间：</p><ol><li><strong>数据指针</strong>：指向实例在内存（堆或栈）中的实际位置。</li><li><strong>虚表指针</strong>：指向一个只读的内存区域，即虚函数表 (vtable)。</li></ol><h1 id="虚函数表到底存了什么？"><a href="#虚函数表到底存了什么？" class="headerlink" title="虚函数表到底存了什么？"></a>虚函数表到底存了什么？</h1><p>每个实现了 Trait 的类型都会对应一张唯一的 vtable。它不仅存储了方法的地址，还存储了维护类型安全的关键元数据：</p><table><thead><tr><th><strong>字段</strong></th><th><strong>作用</strong></th></tr></thead><tbody><tr><td><strong>析构函数</strong></td><td>运行时如何销毁该对象。</td></tr><tr><td><strong>大小</strong></td><td>该对象占用的字节数，用于内存管理。</td></tr><tr><td><strong>对齐</strong></td><td>该对象的内存对齐要求。</td></tr><tr><td><strong>方法指针群</strong></td><td>按照 Trait 定义顺序排列的函数地址。</td></tr></tbody></table><p><strong>为什么需要 <code>size</code> 和 <code>align</code>？</strong></p><p>因为 <code>dyn Trait</code> 是 <code>Unsized</code>（大小不确定）的。如果没有这些元数据，当 <code>Box&lt;dyn Trait&gt;</code> 离开作用域时，运行时将不知道该释放多少内存。</p><h1 id="对象安全性"><a href="#对象安全性" class="headerlink" title="对象安全性"></a>对象安全性</h1><p>如果一个 Trait 想要通过 <code>dyn</code> 关键字进行动态派发，它必须是<strong>对象安全</strong>的。以下是五大核心限制及背后的逻辑：</p><ol><li><p>方法必须有 <code>self</code> 接收者</p><ul><li>不能包含类似 <code>fn static_method()</code> 的方法。</li><li>vtable 必须通过 <code>self</code> 指针找到具体对象的类型。没有 <code>self</code>，就没有胖指针，也就找不到 vtable。</li></ul></li><li><p>禁止使用 <code>Self: Sized</code> 约束</p><ul><li>动态派发的设计初衷就是处理那些大小在编译时未知的类型。如果 Trait 强制要求实现者必须是 <code>Sized</code>，则与 <code>dyn Trait</code> 的本质相矛盾。</li></ul></li><li><p>方法中禁止使用泛型参数</p> <figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-comment">// 错误示例</span><br><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Bad</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">generic</span>&lt;T&gt;(&amp;<span class="hljs-keyword">self</span>, x: T); <br>&#125;<br></code></pre></td></tr></table></figure><ul><li>Rust 的泛型是单态化的（编译时生成副本）。对于 <code>dyn Bad</code>，编译器无法预知运行时会传入什么 <code>T</code>，因此无法在有限大小的 vtable 中存放无限可能的函数副本。</li></ul></li><li><p>方法返回值不能是 <code>Self</code></p><ul><li>由于 <code>dyn Trait</code> 抹去了具体类型，编译器在调用处无法预知 <code>Self</code> 到底占多少空间，因此无法在栈上为返回值分配内存。</li></ul></li><li><p>禁止关联常量</p><ul><li>目前的 vtable 结构仅设计用于存储函数指针和基础元数据，不支持存储关联常量。</li></ul></li></ol><h1 id="正确与错误的使用姿势"><a href="#正确与错误的使用姿势" class="headerlink" title="正确与错误的使用姿势"></a>正确与错误的使用姿势</h1><h2 id="错误：违反对象安全"><a href="#错误：违反对象安全" class="headerlink" title="错误：违反对象安全"></a>错误：违反对象安全</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Cloneable</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">clone</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span>; <span class="hljs-comment">// 报错：返回了 Self</span><br>&#125;<br><br><span class="hljs-comment">// 无法创建 Vec&lt;Box&lt;dyn Cloneable&gt;&gt;</span><br></code></pre></td></tr></table></figure><h2 id="正确：异构集合场景"><a href="#正确：异构集合场景" class="headerlink" title="正确：异构集合场景"></a>正确：异构集合场景</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Drawable</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">draw</span>(&amp;<span class="hljs-keyword">self</span>);<br>&#125;<br><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Circle</span>;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Square</span>;<br><br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Drawable</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Circle</span> &#123; <span class="hljs-keyword">fn</span> <span class="hljs-title function_">draw</span>(&amp;<span class="hljs-keyword">self</span>) &#123; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;圆形&quot;</span>); &#125; &#125;<br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Drawable</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Square</span> &#123; <span class="hljs-keyword">fn</span> <span class="hljs-title function_">draw</span>(&amp;<span class="hljs-keyword">self</span>) &#123; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;正方形&quot;</span>); &#125; &#125;<br><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">render</span>(elements: <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">Box</span>&lt;<span class="hljs-keyword">dyn</span> Drawable&gt;&gt;) &#123;<br>    <span class="hljs-keyword">for</span> <span class="hljs-variable">e</span> <span class="hljs-keyword">in</span> elements &#123;<br>        e.<span class="hljs-title function_ invoke__">draw</span>(); <span class="hljs-comment">// 运行时通过 vtable 查找并跳转</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="修复非对象安全的-Trait"><a href="#修复非对象安全的-Trait" class="headerlink" title="修复非对象安全的 Trait"></a>修复非对象安全的 Trait</h2><p>如果必须在 Trait 里放一个静态方法，可以通过 <code>where Self: Sized</code> 约束来“隐藏”它，使其不进入 vtable：</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">MyTrait</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">safe_method</span>(&amp;<span class="hljs-keyword">self</span>);<br>    <br>    <span class="hljs-comment">// 加上这个约束后，该方法在 dyn MyTrait 中不可用，但 Trait 整体变安全了</span><br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">unsafe_static</span>() <span class="hljs-keyword">where</span> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>; <br>&#125;<br></code></pre></td></tr></table></figure><h1 id="当-Trait-也不够用时"><a href="#当-Trait-也不够用时" class="headerlink" title="当 Trait 也不够用时"></a>当 Trait 也不够用时</h1><p>在某些极端场景下（如插件系统、消息总线），你甚至无法预定义一个 Trait 来涵盖所有可能的类型。这时，Rust 提供了终极武器：<code>std::any::Any</code>。</p><p>如果说 <code>dyn Trait</code> 是“我不知道你是谁，但我知道你能做什么”，那么 <code>dyn Any</code> 就是“我完全不知道你是谁，但我可以在运行时查你的身份证”。</p><h2 id="万能容器：Box"><a href="#万能容器：Box" class="headerlink" title="万能容器：Box&lt;dyn Any&gt;"></a>万能容器：<code>Box&lt;dyn Any&gt;</code></h2><p>由于 <code>Any</code> 也是一个 Trait，它同样遵循对象安全规则。我们通常使用 <code>Box&lt;dyn Any&gt;</code> 来存储拥有所有权的任何类型：</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">use</span> std::any::Any;<br><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">vault</span>: <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">Box</span>&lt;<span class="hljs-keyword">dyn</span> Any&gt;&gt; = <span class="hljs-type">Vec</span>::<span class="hljs-title function_ invoke__">new</span>();<br>vault.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-number">42_i32</span>));<br>vault.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Rust 2026&quot;</span>)));<br><br><span class="hljs-comment">// 运行时找回类型</span><br><span class="hljs-keyword">for</span> <span class="hljs-variable">item</span> <span class="hljs-keyword">in</span> vault &#123;<br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>(val) = item.downcast_ref::&lt;<span class="hljs-type">i32</span>&gt;() &#123;<br>        <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;整数: &#123;&#125;&quot;</span>, val);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="TypeId-与-Downcasting"><a href="#TypeId-与-Downcasting" class="headerlink" title="TypeId 与 Downcasting"></a>TypeId 与 Downcasting</h2><p><code>dyn Any</code> 的胖指针中，vtable 包含一个关键方法：<code>type_id()</code>。</p><ul><li><strong>原理</strong>：编译器会为每个类型生成一个唯一的 128 位哈希值（TypeId）。</li><li><strong>安全检查</strong>：当你调用 <code>downcast_ref::&lt;T&gt;()</code> 时，Rust 会比较 vtable 中的 ID 与目标类型 <code>T</code> 的 ID 是否一致。如果一致，则进行指针转换；否则返回 <code>None</code>。</li></ul><hr><h2 id="能否绕过检查直接强转？"><a href="#能否绕过检查直接强转？" class="headerlink" title="能否绕过检查直接强转？"></a>能否绕过检查直接强转？</h2><p>通过 <code>unsafe</code> 确实可以跳过检查，将 <code>dyn Any</code> 的数据指针直接转换为具体类型的指针：</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-comment">// 这是一个极高性能、但也极其危险的操作</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">raw_ptr</span> = <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">into_raw</span>(boxed_any) <span class="hljs-keyword">as</span> *<span class="hljs-keyword">mut</span> <span class="hljs-type">i32</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">val</span> = <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">from_raw</span>(raw_ptr) &#125;;<br></code></pre></td></tr></table></figure><ul><li><strong>性能增益</strong>：消除了虚表查找和 128 位哈希值比较，减少了分支预测失败的概率。在数百万次的高频调用中，这能省下宝贵的纳秒。</li><li><strong>代价</strong>：这是一场豪赌。一旦类型判断逻辑出错，<code>unsafe</code> 强转会导致UB，程序可能会在完全无关的地方莫名崩溃。</li></ul><h1 id="如何选择？"><a href="#如何选择？" class="headerlink" title="如何选择？"></a>如何选择？</h1><ul><li>静态派发 (<code>&lt;T: Trait&gt;</code>)：适用于追求性能、函数内联和单一类型的场景。虽然会增加编译时间（代码膨胀），但运行效率最高。</li><li>动态派发 (<code>dyn Trait</code>)：适用于需要灵活处理多种不同类型、减小二进制体积或需要运行时动态扩展的场景。</li><li>运行时反射 (<code>dyn Any</code>)：最后的防线。允许处理完全未知的类型，通过 <code>TypeId</code> 提供运行时的类型安全保证。</li></ul>]]>
    </content>
    <id>https://www.harkerhand.cn/dynTrait/</id>
    <link href="https://www.harkerhand.cn/dynTrait/"/>
    <published>2026-03-03T08:17:47.000Z</published>
    <summary>
      <![CDATA[<p>在 Rust 中，多态主要通过两种方式实现：<strong>静态派发</strong> 和 <strong>动态派发</strong>。虽然泛型（静态派发）是 Rust 的首选，但在处理“异构集合”（例如一个包含不同 UI 组件的列表）时，动态派发（即 <code>dyn]]>
    </summary>
    <title>谈谈Rust动态派发</title>
    <updated>2026-04-13T06:42:52.014Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <category term="密码学" scheme="https://www.harkerhand.cn/tags/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
    <content>
      <![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>假设在分布式负载均衡场景中，对于一些输入，我们希望尽可能均匀的将其分配到 n 个不同的节点。常规的哈希算法通常使用简单的哈希函数与取模来得到下标。<br>$$<br>\text{index} &#x3D; hash(input) \ % \ n<br>$$<br>但当节点扩缩容时，即 n 的大小变化时，一个显而易见的问题是。对于相同的 input，得到的 index 是不同的。</p><p>从数学上来看，即使 index 发生变化，这样的算法在负载均衡上的表现依然是优秀的。但如果节点会有类似缓存的技术来加快计算，这样的index变化就是不可接受的，它会导致<strong>几乎所有的缓存失效</strong>。</p><h1 id="一致性哈希"><a href="#一致性哈希" class="headerlink" title="一致性哈希"></a>一致性哈希</h1><p>一致性哈希的目的是解决分布式系统的数据分区问题：当分布式集群移除或者添加一个服务器时，必须尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。</p><p>简单来说就是尽可能的复用缓存，尽可能对于同一个input，得到同一个index。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>一致性哈希本质上也是一种取模算法。只不过它的模数是一个极大的数，一般是 2^32 - 1。</p><p>暂且认为一致性哈希用了一个超大的桶，来规避了取模问题。现在对于相同的input，一定会得到相同的index。但这个index是用来索引节点的，现在应该如何索引？</p><p>解决方案是：对节点标记也进行哈希，将节点放入桶中。在拿到input的index后，向后遍历，直到遇到第一个节点。</p><p>在这样的前提下，想象这个超大的桶是一个环，各个节点将这个环分割，每个节点负责环上的一个弧。</p><ul><li>当插入一个节点时，其影响范围只有被其切割的那一段弧。</li><li>当删除一个节点时，其影响范围只有其负责的那一段弧。</li></ul><p>理论上，每次扩缩容，造成的影响只有 1&#x2F;N，其中 N 是缓存总数。</p><h2 id="细节"><a href="#细节" class="headerlink" title="细节"></a>细节</h2><p>原理是这样，那实现上呢？</p><p>第一，很显然这些桶是不可能预分配内存的，太大了！但这个环，本质上也是一张（整数，节点标记）的表，同时，我们上文提到了有向后遍历的需求，那这个表就可以使用排序树（一般是红黑树）来实现，不仅解决了空间问题，还可以用二分来优化遍历的时间。</p><p>第二，当节点数量不多或者我运气很差时，节点可能会聚集在这个环的某一个区间内，造成极度的负载不均衡。一个朴素的想法是，只要节点数量够多，把这个环切分的足够细，就可以极大缓解这个不均衡的问题。但实际的节点数量就那么几个，这时候就可以采用虚拟节点的方式，我们可以将一个节点视为数十个甚至数百个虚拟节点，用这些虚拟节点来哈希做，最后放入桶中的依然是同一个节点。</p><p>第三，物理节点撞了怎么办。当虚拟节点足够多时，撞一两个其实不会太影响负载均衡的结果。还不放心的话，可以使用64位哈希。还不放心的话，就只能祈祷自己不是倒霉蛋了。</p><h2 id="可能的优化"><a href="#可能的优化" class="headerlink" title="可能的优化"></a>可能的优化</h2><ul><li>当节点变动不频繁的时候，可以不用排序树。直接 <code>vector&lt;(hash, Node)&gt;</code>，初始化结束以及增删节点时排序，查找可以使用二分，可以更好的利用CPU缓存。</li></ul><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><p>假设：</p><ul><li>物理节点（个） N</li><li>虚拟节点（个&#x2F;物理节点） V</li></ul><p>总节点：<br>$$<br>M &#x3D; N * V<br>$$<br>操作复杂度：</p><table><thead><tr><th>操作</th><th>时间复杂度</th></tr></thead><tbody><tr><td>查找</td><td>O(log M)</td></tr><tr><td>插入</td><td>O(V log M)</td></tr><tr><td>删除</td><td>O(V log M)</td></tr></tbody></table><h1 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h1><h2 id="C"><a href="#C" class="headerlink" title="C++"></a>C++</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><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> Node&gt;<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ConsistentHash</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-keyword">using</span> node_ptr = std::shared_ptr&lt;Node&gt;;<br>    <span class="hljs-keyword">using</span> hash_type = std::<span class="hljs-type">uint64_t</span>;<br><br>    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">ConsistentHash</span><span class="hljs-params">(std::<span class="hljs-type">size_t</span> virtual_nodes = <span class="hljs-number">100</span>)</span></span><br><span class="hljs-function">        : virtual_node_count_(virtual_nodes) &#123;</span>&#125;<br><br>    <span class="hljs-comment">// 添加物理节点</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">add_node</span><span class="hljs-params">(<span class="hljs-type">const</span> node_ptr&amp; node)</span> </span>&#123;<br>        <span class="hljs-keyword">if</span> (!node) &#123;<br>            <span class="hljs-keyword">throw</span> std::<span class="hljs-built_in">invalid_argument</span>(<span class="hljs-string">&quot;node is null&quot;</span>);<br>        &#125;<br><br>        <span class="hljs-function">std::unique_lock <span class="hljs-title">lock</span><span class="hljs-params">(mutex_)</span></span>;<br><br>        <span class="hljs-keyword">for</span> (std::<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i &lt; virtual_node_count_; ++i) &#123;<br>            <span class="hljs-keyword">auto</span> vnode_key = <span class="hljs-built_in">make_vnode_key</span>(*node, i);<br>            ring_.<span class="hljs-built_in">emplace</span>(<span class="hljs-built_in">hash</span>(vnode_key), node);<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-comment">// 删除物理节点</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">remove_node</span><span class="hljs-params">(<span class="hljs-type">const</span> node_ptr&amp; node)</span> </span>&#123;<br>        <span class="hljs-keyword">if</span> (!node) <span class="hljs-keyword">return</span>;<br><br>        <span class="hljs-function">std::unique_lock <span class="hljs-title">lock</span><span class="hljs-params">(mutex_)</span></span>;<br><br>        <span class="hljs-keyword">for</span> (std::<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i &lt; virtual_node_count_; ++i) &#123;<br>            <span class="hljs-keyword">auto</span> vnode_key = <span class="hljs-built_in">make_vnode_key</span>(*node, i);<br>            ring_.<span class="hljs-built_in">erase</span>(<span class="hljs-built_in">hash</span>(vnode_key));<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-comment">// 查找 key 对应的节点</span><br>    [[nodiscard]]<br>    <span class="hljs-function">node_ptr <span class="hljs-title">get_node</span><span class="hljs-params">(<span class="hljs-type">const</span> std::string&amp; key)</span> <span class="hljs-type">const</span> </span>&#123;<br>        <span class="hljs-function">std::shared_lock <span class="hljs-title">lock</span><span class="hljs-params">(mutex_)</span></span>;<br><br>        <span class="hljs-keyword">if</span> (ring_.<span class="hljs-built_in">empty</span>()) &#123;<br>            <span class="hljs-keyword">throw</span> std::<span class="hljs-built_in">runtime_error</span>(<span class="hljs-string">&quot;Hash ring is empty&quot;</span>);<br>        &#125;<br><br>        <span class="hljs-keyword">auto</span> h = <span class="hljs-built_in">hash</span>(key);<br>        <span class="hljs-keyword">auto</span> it = ring_.<span class="hljs-built_in">lower_bound</span>(h);<br><br>        <span class="hljs-keyword">if</span> (it == ring_.<span class="hljs-built_in">end</span>()) &#123;<br>            it = ring_.<span class="hljs-built_in">begin</span>();  <span class="hljs-comment">// 回环</span><br>        &#125;<br><br>        <span class="hljs-keyword">return</span> it-&gt;second;<br>    &#125;<br><br>    [[nodiscard]]<br>    <span class="hljs-function">std::<span class="hljs-type">size_t</span> <span class="hljs-title">node_count</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>&#123;<br>        <span class="hljs-function">std::shared_lock <span class="hljs-title">lock</span><span class="hljs-params">(mutex_)</span></span>;<br>        <span class="hljs-keyword">return</span> ring_.<span class="hljs-built_in">size</span>() / virtual_node_count_;<br>    &#125;<br><br><span class="hljs-keyword">private</span>:<br>    <span class="hljs-function"><span class="hljs-type">static</span> hash_type <span class="hljs-title">hash</span><span class="hljs-params">(<span class="hljs-type">const</span> std::string&amp; key)</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> std::hash&lt;std::string&gt;&#123;&#125;(key);<br>    &#125;<br><br>    <span class="hljs-function"><span class="hljs-type">static</span> std::string <span class="hljs-title">make_vnode_key</span><span class="hljs-params">(<span class="hljs-type">const</span> Node&amp; node, std::<span class="hljs-type">size_t</span> index)</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> node.<span class="hljs-built_in">id</span>() + <span class="hljs-string">&quot;#&quot;</span> + std::<span class="hljs-built_in">to_string</span>(index);<br>    &#125;<br><br><span class="hljs-keyword">private</span>:<br>    std::<span class="hljs-type">size_t</span> virtual_node_count_;<br>    std::map&lt;hash_type, node_ptr&gt; ring_;<br>    <span class="hljs-keyword">mutable</span> std::shared_mutex mutex_;<br>&#125;;<br></code></pre></td></tr></table></figure><h2 id="Rust"><a href="#Rust" class="headerlink" title="Rust"></a>Rust</h2><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ConsistentHash</span>&lt;T&gt; &#123;<br>    virtual_nodes: <span class="hljs-type">usize</span>,<br>    ring: RwLock&lt;BTreeMap&lt;<span class="hljs-type">u64</span>, Arc&lt;T&gt;&gt;&gt;,<br>&#125;<br><br><span class="hljs-keyword">impl</span>&lt;T&gt; ConsistentHash&lt;T&gt;<br><span class="hljs-keyword">where</span><br>    T: Hash + <span class="hljs-built_in">Clone</span>,<br>&#123;<br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>(virtual_nodes: <span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">Self</span> &#123;<br>        <span class="hljs-keyword">Self</span> &#123;<br>            virtual_nodes,<br>            ring: RwLock::<span class="hljs-title function_ invoke__">new</span>(BTreeMap::<span class="hljs-title function_ invoke__">new</span>()),<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">add_node</span>(&amp;<span class="hljs-keyword">self</span>, node: Arc&lt;T&gt;) &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">ring</span> = <span class="hljs-keyword">self</span>.ring.<span class="hljs-title function_ invoke__">write</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br><br>        <span class="hljs-keyword">for</span> <span class="hljs-variable">i</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-keyword">self</span>.virtual_nodes &#123;<br>            <span class="hljs-keyword">let</span> <span class="hljs-variable">hash</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">hash_virtual_node</span>(&amp;node, i);<br>            ring.<span class="hljs-title function_ invoke__">insert</span>(hash, Arc::<span class="hljs-title function_ invoke__">clone</span>(&amp;node));<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">remove_node</span>(&amp;<span class="hljs-keyword">self</span>, node: &amp;T) &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">ring</span> = <span class="hljs-keyword">self</span>.ring.<span class="hljs-title function_ invoke__">write</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br><br>        <span class="hljs-keyword">for</span> <span class="hljs-variable">i</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-keyword">self</span>.virtual_nodes &#123;<br>            <span class="hljs-keyword">let</span> <span class="hljs-variable">hash</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">hash_virtual_node_value</span>(node, i);<br>            ring.<span class="hljs-title function_ invoke__">remove</span>(&amp;hash);<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_node</span>&lt;K: Hash&gt;(&amp;<span class="hljs-keyword">self</span>, key: &amp;K) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Option</span>&lt;Arc&lt;T&gt;&gt; &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">ring</span> = <span class="hljs-keyword">self</span>.ring.<span class="hljs-title function_ invoke__">read</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br><br>        <span class="hljs-keyword">if</span> ring.<span class="hljs-title function_ invoke__">is_empty</span>() &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>;<br>        &#125;<br><br>        <span class="hljs-keyword">let</span> <span class="hljs-variable">hash</span> = <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">hash</span>(key);<br><br>        <span class="hljs-comment">// 找到第一个 &gt;= hash 的节点</span><br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">iter</span> = ring.<span class="hljs-title function_ invoke__">range</span>(hash..);<br><br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>((_, node)) = iter.<span class="hljs-title function_ invoke__">next</span>() &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Some</span>(Arc::<span class="hljs-title function_ invoke__">clone</span>(node));<br>        &#125;<br><br>        <span class="hljs-comment">// 回环</span><br>        ring.<span class="hljs-title function_ invoke__">values</span>().<span class="hljs-title function_ invoke__">next</span>().<span class="hljs-title function_ invoke__">map</span>(Arc::clone)<br>    &#125;<br><br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">hash</span>&lt;K: Hash&gt;(&amp;<span class="hljs-keyword">self</span>, key: &amp;K) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">u64</span> &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">hasher</span> = DefaultHasher::<span class="hljs-title function_ invoke__">new</span>();<br>        key.<span class="hljs-title function_ invoke__">hash</span>(&amp;<span class="hljs-keyword">mut</span> hasher);<br>        hasher.<span class="hljs-title function_ invoke__">finish</span>()<br>    &#125;<br><br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">hash_virtual_node</span>(&amp;<span class="hljs-keyword">self</span>, node: &amp;Arc&lt;T&gt;, index: <span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">u64</span> &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">hasher</span> = DefaultHasher::<span class="hljs-title function_ invoke__">new</span>();<br>        node.<span class="hljs-title function_ invoke__">hash</span>(&amp;<span class="hljs-keyword">mut</span> hasher);<br>        index.<span class="hljs-title function_ invoke__">hash</span>(&amp;<span class="hljs-keyword">mut</span> hasher);<br>        hasher.<span class="hljs-title function_ invoke__">finish</span>()<br>    &#125;<br><br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">hash_virtual_node_value</span>(&amp;<span class="hljs-keyword">self</span>, node: &amp;T, index: <span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">u64</span> &#123;<br>        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">hasher</span> = DefaultHasher::<span class="hljs-title function_ invoke__">new</span>();<br>        node.<span class="hljs-title function_ invoke__">hash</span>(&amp;<span class="hljs-keyword">mut</span> hasher);<br>        index.<span class="hljs-title function_ invoke__">hash</span>(&amp;<span class="hljs-keyword">mut</span> hasher);<br>        hasher.<span class="hljs-title function_ invoke__">finish</span>()<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.harkerhand.cn/ConsistentHashing/</id>
    <link href="https://www.harkerhand.cn/ConsistentHashing/"/>
    <published>2026-03-03T06:10:20.000Z</published>
    <summary>
      <![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>假设在分布式负载均衡场景中，对于一些输入，我们希望尽可能均匀的将其分配到 n]]>
    </summary>
    <title>一致性哈希</title>
    <updated>2026-04-13T06:42:51.993Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <content>
      <![CDATA[<blockquote><p>面试问到了，重新梳理一下</p></blockquote><h1 id="一次-IO-到底发生了什么？"><a href="#一次-IO-到底发生了什么？" class="headerlink" title="一次 IO 到底发生了什么？"></a>一次 IO 到底发生了什么？</h1><p>以读 socket 数据为例。<br>一次 read 实际上包含两个阶段：</p><ol><li><p>等待数据准备好（内核态）</p><ul><li>数据从网卡 → DMA → 内核缓冲区</li><li>这个过程应用程序<strong>完全无法参与</strong></li><li>只能等</li></ul></li><li><p>数据从内核拷贝到用户空间</p><ul><li>内核 buffer → 用户 buffer</li><li>这是一次内存拷贝</li></ul></li></ol><p>一次 IO &#x3D; 等待数据 + 拷贝数据</p><p>所有 IO 模型的区别，都来自：</p><ul><li>等待阶段谁在等？</li><li>拷贝阶段谁在等？</li><li>是否阻塞线程？</li><li>是否需要主动轮询？</li></ul><h1 id="阻塞-IO（Blocking-IO）"><a href="#阻塞-IO（Blocking-IO）" class="headerlink" title="阻塞 IO（Blocking IO）"></a>阻塞 IO（Blocking IO）</h1><p>这是最传统的模型。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c">read(fd, buffer, size);<br></code></pre></td></tr></table></figure><p>如果数据没到：</p><ul><li>线程直接睡眠</li><li>内核等数据</li><li>数据到达后拷贝</li><li>read 返回</li></ul><h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2><ul><li>等待阶段：阻塞</li><li>拷贝阶段：阻塞</li><li>整个线程完全卡住</li></ul><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ul><li>简单</li><li>易写</li><li>易理解</li></ul><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><ul><li>1个线程只能处理1个连接</li><li>高并发会炸线程</li></ul>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-c3152edc" role="button" aria-expanded="false" aria-controls="collapse-c3152edc">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-c3152edc">        <div class="fold-content">          <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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span>    </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span>   </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span>   </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span>   </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;arpa/inet.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PORT 9999</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> BUFFER_SIZE 1024</span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-type">int</span> server_fd, client_fd;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">address</span>;</span><br>    <span class="hljs-type">int</span> addrlen = <span class="hljs-keyword">sizeof</span>(address);<br>    <span class="hljs-type">char</span> buffer[BUFFER_SIZE] = &#123;<span class="hljs-number">0</span>&#125;;<br><br>    <span class="hljs-comment">// 1. 创建套接字 (IPv4, TCP)</span><br>    <span class="hljs-keyword">if</span> ((server_fd = socket(AF_INET, SOCK_STREAM, <span class="hljs-number">0</span>)) == <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;Socket 创建失败&quot;</span>);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br><br>    <span class="hljs-comment">// 2. 准备地址和端口</span><br>    address.sin_family = AF_INET;<br>    address.sin_addr.s_addr = INADDR_ANY; <span class="hljs-comment">// 监听所有网卡 (0.0.0.0)</span><br>    address.sin_port = htons(PORT);       <span class="hljs-comment">// 转换为网络字节序</span><br><br>    <span class="hljs-comment">// 3. 绑定</span><br>    <span class="hljs-keyword">if</span> (bind(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;address, <span class="hljs-keyword">sizeof</span>(address)) &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;绑定失败&quot;</span>);<br>        close(server_fd);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br><br>    <span class="hljs-comment">// 4. 监听</span><br>    <span class="hljs-keyword">if</span> (listen(server_fd, <span class="hljs-number">3</span>) &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;监听失败&quot;</span>);<br>        close(server_fd);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;服务端启动，正在监听端口 %d...\n&quot;</span>, PORT);<br><br>    <span class="hljs-comment">// 5. 接受连接 阻塞 线程睡眠，等待客户端连接</span><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;等待客户端连接...\n&quot;</span>);<br>    <span class="hljs-keyword">if</span> ((client_fd = accept(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;address, (<span class="hljs-type">socklen_t</span> *)&amp;addrlen)) &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;接受连接失败&quot;</span>);<br>        close(server_fd);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;客户端已连接！地址: %s\n&quot;</span>, inet_ntoa(address.sin_addr));<br><br>    <span class="hljs-comment">// 6. 循环读取数据</span><br>    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)<br>    &#123;<br>        <span class="hljs-built_in">memset</span>(buffer, <span class="hljs-number">0</span>, BUFFER_SIZE); <span class="hljs-comment">// 清空缓冲区</span><br><br>        <span class="hljs-comment">// recv 阻塞 线程睡眠，等待数据到达</span><br>        <span class="hljs-type">ssize_t</span> valread = recv(client_fd, buffer, BUFFER_SIZE - <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br><br>        <span class="hljs-keyword">if</span> (valread &gt; <span class="hljs-number">0</span>)<br>        &#123;<br>            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;[客户端]: %s&quot;</span>, buffer);<br>        &#125;<br>        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (valread == <span class="hljs-number">0</span>)<br>        &#123;<br>            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;客户端断开连接。\n&quot;</span>);<br>            <span class="hljs-keyword">break</span>;<br>        &#125;<br>        <span class="hljs-keyword">else</span><br>        &#123;<br>            perror(<span class="hljs-string">&quot;读取出错&quot;</span>);<br>            <span class="hljs-keyword">break</span>;<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-comment">// 7. 关闭</span><br>    close(client_fd);<br>    close(server_fd);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h1 id="非阻塞-IO（Non-blocking-IO）"><a href="#非阻塞-IO（Non-blocking-IO）" class="headerlink" title="非阻塞 IO（Non-blocking IO）"></a>非阻塞 IO（Non-blocking IO）</h1><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></pre></td><td class="code"><pre><code class="hljs c">fcntl(fd, F_SETFL, O_NONBLOCK);<br><br>read(fd, buffer, size);<br></code></pre></td></tr></table></figure><p>如果数据没到：</p><ul><li>立刻返回 -1</li><li>errno &#x3D; EAGAIN</li></ul><p>不会睡眠</p><p>但它只是<strong>不等待数据</strong>，它没有解决<strong>什么时候知道数据来了</strong>的问题</p><p>所以必须轮询，这就导致了：</p><ul><li>CPU 空转</li><li>效率低</li><li>不能规模化</li></ul>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-9c51fa20" role="button" aria-expanded="false" aria-controls="collapse-9c51fa20">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-9c51fa20">        <div class="fold-content">          <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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;fcntl.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;errno.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;arpa/inet.h&gt;</span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PORT 9999</span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">set_nonblocking</span><span class="hljs-params">(<span class="hljs-type">int</span> fd)</span><br>&#123;<br>    <span class="hljs-type">int</span> flags = fcntl(fd, F_GETFL, <span class="hljs-number">0</span>);<br>    fcntl(fd, F_SETFL, flags | O_NONBLOCK);<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-type">int</span> server_fd, client_fd = <span class="hljs-number">-1</span>;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">address</span>;</span><br>    <span class="hljs-type">int</span> addrlen = <span class="hljs-keyword">sizeof</span>(address);<br>    <span class="hljs-type">char</span> buffer[<span class="hljs-number">1024</span>];<br><br>    <span class="hljs-comment">// 1. 创建并设置监听 Socket 为非阻塞</span><br>    server_fd = socket(AF_INET, SOCK_STREAM, <span class="hljs-number">0</span>);<br>    set_nonblocking(server_fd);<br><br>    address.sin_family = AF_INET;<br>    address.sin_addr.s_addr = INADDR_ANY;<br>    address.sin_port = htons(PORT);<br><br>    bind(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;address, <span class="hljs-keyword">sizeof</span>(address));<br>    listen(server_fd, <span class="hljs-number">3</span>);<br><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;非阻塞服务端已启动，监听 %d...\n&quot;</span>, PORT);<br><br>    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)<br>    &#123;<br>        <span class="hljs-comment">// 2. 尝试接受连接 (非阻塞 accept)</span><br>        client_fd = accept(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;address, (<span class="hljs-type">socklen_t</span> *)&amp;addrlen);<br><br>        <span class="hljs-keyword">if</span> (client_fd &lt; <span class="hljs-number">0</span>)<br>        &#123;<br>            <span class="hljs-keyword">if</span> (errno == EAGAIN || errno == EWOULDBLOCK)<br>            &#123;<br>                <span class="hljs-comment">// 没有新连接</span><br>                <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;等待连接中...\n&quot;</span>);<br>            &#125;<br>            <span class="hljs-keyword">else</span><br>            &#123;<br>                perror(<span class="hljs-string">&quot;accept error&quot;</span>);<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">else</span><br>        &#123;<br>            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;客户端已连接！\n&quot;</span>);<br>            set_nonblocking(client_fd); <span class="hljs-comment">// 把客户端也设为非阻塞</span><br><br>            <span class="hljs-comment">// 3. 尝试读取数据 (非阻塞 recv)</span><br>            <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)<br>            &#123;<br>                <span class="hljs-built_in">memset</span>(buffer, <span class="hljs-number">0</span>, <span class="hljs-number">1024</span>);<br>                <span class="hljs-type">ssize_t</span> bytes = recv(client_fd, buffer, <span class="hljs-number">1023</span>, <span class="hljs-number">0</span>);<br><br>                <span class="hljs-keyword">if</span> (bytes &gt; <span class="hljs-number">0</span>)<br>                &#123;<br>                    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;[收到数据]: %s&quot;</span>, buffer);<br>                &#125;<br>                <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (bytes == <span class="hljs-number">-1</span>)<br>                &#123;<br>                    <span class="hljs-keyword">if</span> (errno == EAGAIN || errno == EWOULDBLOCK)<br>                    &#123;<br>                        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;暂无数据...\n&quot;</span>);<br>                        sleep(<span class="hljs-number">1</span>);<br>                    &#125;<br>                    <span class="hljs-keyword">else</span><br>                    &#123;<br>                        perror(<span class="hljs-string">&quot;读取错误&quot;</span>);<br>                        close(client_fd);<br>                        client_fd = <span class="hljs-number">-1</span>;<br>                        <span class="hljs-keyword">break</span>;<br>                    &#125;<br>                &#125;<br>                <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (bytes == <span class="hljs-number">0</span>)<br>                &#123;<br>                    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;客户端断开了。\n&quot;</span>);<br>                    close(client_fd);<br>                    client_fd = <span class="hljs-number">-1</span>;<br>                    <span class="hljs-keyword">break</span>;<br>                &#125;<br>            &#125;<br>        &#125;<br>        sleep(<span class="hljs-number">1</span>);<br>    &#125;<br>    close(server_fd);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h1 id="IO-多路复用（select-poll-epoll）"><a href="#IO-多路复用（select-poll-epoll）" class="headerlink" title="IO 多路复用（select &#x2F; poll &#x2F; epoll）"></a>IO 多路复用（select &#x2F; poll &#x2F; epoll）</h1><p>它解决的是<strong>如何高效等待多个 fd</strong></p><p>不对每个 fd 调 read</p><p>而是先问内核：哪些 fd 可以读？</p><p>内核来等。</p><h2 id="时间线"><a href="#时间线" class="headerlink" title="时间线"></a>时间线</h2><figure class="highlight stylus"><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><code class="hljs stylus"><span class="hljs-function"><span class="hljs-title">epoll_wait</span><span class="hljs-params">()</span></span><br>    ↓<br><span class="hljs-selector-attr">[ 等待数据 ]</span> ← 阻塞在这里<br>    ↓<br>返回就绪fd<br>    ↓<br><span class="hljs-function"><span class="hljs-title">read</span><span class="hljs-params">()</span></span><br>    ↓<br><span class="hljs-selector-attr">[ 数据拷贝 ]</span> ← 阻塞<br>    ↓<br>返回<br></code></pre></td></tr></table></figure><h2 id="关键理解"><a href="#关键理解" class="headerlink" title="关键理解"></a>关键理解</h2><p>IO多路复用：</p><ul><li>等待阶段：由内核统一等待</li><li>拷贝阶段：仍然是同步的</li></ul><p>所以它本质是同步 IO，只是等待更高效</p><h2 id="Select-Poll-Epoll"><a href="#Select-Poll-Epoll" class="headerlink" title="Select &#x2F; Poll &#x2F; Epoll"></a>Select &#x2F; Poll &#x2F; Epoll</h2><h3 id="select（最原始，效率最低）"><a href="#select（最原始，效率最低）" class="headerlink" title="select（最原始，效率最低）"></a>select（最原始，效率最低）</h3><ul><li><strong>原理</strong>：传给内核一个固定大小的位图，里面存了所有要监控的 FD。</li><li>痛点 1（限制）：位图长度有限，默认只能监控 1024 个 FD。</li><li>痛点 2（拷贝）：每次调用都要把这个位图从用户态全量拷贝到内核态。</li><li>痛点 3（遍历）：内核发现有数据了，它不告诉你是哪个，而是把位图还给你。你得在用户态亲自写个循环从 1 扫到 1024，看看到底是谁有数。</li></ul><h3 id="poll（select-的小幅改良版）"><a href="#poll（select-的小幅改良版）" class="headerlink" title="poll（select 的小幅改良版）"></a>poll（select 的小幅改良版）</h3><ul><li>原理：不用位图了，改用<strong>结构体数组</strong>（链表实现）。</li><li>进步：没有了 1024 的数量限制。</li><li>原地踏步：它依然需要全量拷贝数组，内核依然不告诉你是谁有数，你还是得手动 O(n) 遍历整个数组。</li></ul><h3 id="epoll（Linux-的终极方案，性能王者）"><a href="#epoll（Linux-的终极方案，性能王者）" class="headerlink" title="epoll（Linux 的终极方案，性能王者）"></a>epoll（Linux 的终极方案，性能王者）</h3><p>它之所以快，是因为在内核里做了三件大事：</p><ol><li>红黑树：在内核里开了一棵树，想监控哪个 FD 就往树上挂。这样不用每次调用都拷贝全量列表，只需告诉内核变动了哪一个。</li><li>回调机制：内核给每个 FD 挂了钩子。一旦数据到了，内核自动执行回调，把这个 FD 塞进一个就绪链表。</li><li>就绪链表：当你调用 <code>wait</code> 时，内核直接把这个“有数”的链表丢给你。复杂度是 O(1)，不需要去遍历那些没动静的死链接。</li></ol>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-872bbe69" role="button" aria-expanded="false" aria-controls="collapse-872bbe69">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-872bbe69">        <div class="fold-content">          <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><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;fcntl.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;errno.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;arpa/inet.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;sys/epoll.h&gt;</span> <span class="hljs-comment">// epoll 核心头文件</span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PORT 9999</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> MAX_EVENTS 10 <span class="hljs-comment">// 一次最多处理多少个事件</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> BUFFER_SIZE 1024</span><br><br><span class="hljs-comment">// 将文件描述符设为非阻塞</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">set_nonblocking</span><span class="hljs-params">(<span class="hljs-type">int</span> fd)</span><br>&#123;<br>    <span class="hljs-type">int</span> flags = fcntl(fd, F_GETFL, <span class="hljs-number">0</span>);<br>    fcntl(fd, F_SETFL, flags | O_NONBLOCK);<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-type">int</span> server_fd, client_fd, epoll_fd;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">address</span>;</span><br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">epoll_event</span> <span class="hljs-title">ev</span>, <span class="hljs-title">events</span>[<span class="hljs-title">MAX_EVENTS</span>];</span><br><br>    <span class="hljs-comment">// 1. 创建并设置监听 Socket</span><br>    server_fd = socket(AF_INET, SOCK_STREAM, <span class="hljs-number">0</span>);<br>    set_nonblocking(server_fd);<br><br>    address.sin_family = AF_INET;<br>    address.sin_addr.s_addr = INADDR_ANY;<br>    address.sin_port = htons(PORT);<br><br>    bind(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;address, <span class="hljs-keyword">sizeof</span>(address));<br>    listen(server_fd, <span class="hljs-number">128</span>);<br><br>    <span class="hljs-comment">// 2. 创建 epoll 实例</span><br>    epoll_fd = epoll_create1(<span class="hljs-number">0</span>);<br>    <span class="hljs-keyword">if</span> (epoll_fd == <span class="hljs-number">-1</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;epoll_create1 失败&quot;</span>);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br><br>    <span class="hljs-comment">// 3. 将监听 Socket 加入 epoll 监视列表</span><br>    ev.events = EPOLLIN; <span class="hljs-comment">// 关注读事件（有人连接或发消息）</span><br>    ev.data.fd = server_fd;<br>    <span class="hljs-keyword">if</span> (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &amp;ev) == <span class="hljs-number">-1</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;epoll_ctl 添加监听 socket 失败&quot;</span>);<br>        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);<br>    &#125;<br><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;epoll 服务端已启动，监听端口 %d...\n&quot;</span>, PORT);<br><br>    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)<br>    &#123;<br>        <span class="hljs-comment">// 4. 等待事件发生（无限期阻塞，直到有事发生，不消耗 CPU）</span><br>        <span class="hljs-type">int</span> nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, <span class="hljs-number">-1</span>);<br>        <span class="hljs-keyword">if</span> (nfds == <span class="hljs-number">-1</span>)<br>        &#123;<br>            perror(<span class="hljs-string">&quot;epoll_wait 出错&quot;</span>);<br>            <span class="hljs-keyword">break</span>;<br>        &#125;<br><br>        <span class="hljs-comment">// 5. 遍历所有发生的事件</span><br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; nfds; ++i)<br>        &#123;<br>            <span class="hljs-keyword">if</span> (events[i].data.fd == server_fd)<br>            &#123;<br>                <span class="hljs-comment">// 如果是 server_fd 有消息，说明有新客户端连接</span><br>                <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">client_addr</span>;</span><br>                <span class="hljs-type">socklen_t</span> client_len = <span class="hljs-keyword">sizeof</span>(client_addr);<br>                client_fd = accept(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;client_addr, &amp;client_len);<br><br>                <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;新客户端连接: %s\n&quot;</span>, inet_ntoa(client_addr.sin_addr));<br><br>                set_nonblocking(client_fd);    <span class="hljs-comment">// 设为非阻塞</span><br>                ev.events = EPOLLIN | EPOLLET; <span class="hljs-comment">// 监听读事件，使用边缘触发(ET)模式</span><br>                ev.data.fd = client_fd;<br>                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &amp;ev);<br>            &#125;<br>            <span class="hljs-keyword">else</span><br>            &#123;<br>                <span class="hljs-comment">// 如果是 client_fd 有消息，说明客户端发来了字符</span><br>                <span class="hljs-type">char</span> buffer[BUFFER_SIZE];<br>                <span class="hljs-type">int</span> fd = events[i].data.fd;<br>                <span class="hljs-type">ssize_t</span> bytes = recv(fd, buffer, <span class="hljs-keyword">sizeof</span>(buffer) - <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br><br>                <span class="hljs-keyword">if</span> (bytes &gt; <span class="hljs-number">0</span>)<br>                &#123;<br>                    buffer[bytes] = <span class="hljs-string">&#x27;\0&#x27;</span>;<br>                    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;[收到数据]: %s&quot;</span>, buffer);<br>                &#125;<br>                <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (bytes == <span class="hljs-number">0</span>)<br>                &#123;<br>                    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;客户端断开连接。\n&quot;</span>);<br>                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, <span class="hljs-literal">NULL</span>); <span class="hljs-comment">// 从监视列表中移除</span><br>                    close(fd);<br>                &#125;<br>                <span class="hljs-keyword">else</span><br>                &#123;<br>                    <span class="hljs-keyword">if</span> (errno != EAGAIN)<br>                    &#123;<br>                        perror(<span class="hljs-string">&quot;读取错误&quot;</span>);<br>                        close(fd);<br>                    &#125;<br>                &#125;<br>            &#125;<br>        &#125;<br>    &#125;<br><br>    close(server_fd);<br>    close(epoll_fd);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h1 id="信号驱动-IO（Signal-driven-IO）"><a href="#信号驱动-IO（Signal-driven-IO）" class="headerlink" title="信号驱动 IO（Signal-driven IO）"></a>信号驱动 IO（Signal-driven IO）</h1><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><code class="hljs c">sigaction(SIGIO, handler);<br>fcntl(fd, F_SETFL, O_ASYNC);<br></code></pre></td></tr></table></figure><p>调用后：</p><ul><li>立刻返回</li><li>内核负责等待</li><li>完成后通知（信号）</li><li>数据拷贝阶段仍然是同步的</li></ul>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-713b9032" role="button" aria-expanded="false" aria-controls="collapse-713b9032">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-713b9032">        <div class="fold-content">          <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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;fcntl.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;signal.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;errno.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;arpa/inet.h&gt;</span></span><br><br><span class="hljs-type">int</span> server_fd, client_fd;<br><br><span class="hljs-comment">// 信号处理函数：当内核发送 SIGIO 时执行</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">io_handler</span><span class="hljs-params">(<span class="hljs-type">int</span> sig)</span><br>&#123;<br>    <span class="hljs-comment">// 1. 尝试接受新连接</span><br>    <span class="hljs-keyword">if</span> (client_fd == <span class="hljs-number">-1</span>)<br>    &#123;<br>        <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">addr</span>;</span><br>        <span class="hljs-type">socklen_t</span> addrlen = <span class="hljs-keyword">sizeof</span>(addr);<br>        <span class="hljs-type">int</span> new_client = accept(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;addr, &amp;addrlen);<br>        <span class="hljs-keyword">if</span> (new_client &gt;= <span class="hljs-number">0</span>)<br>        &#123;<br>            client_fd = new_client;<br>            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[信号触发] 客户端已连接！地址: %s\n&quot;</span>, inet_ntoa(addr.sin_addr));<br><br>            <span class="hljs-comment">// 对新的 client_fd 也设置信号驱动</span><br>            <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sigaction</span> <span class="hljs-title">sa</span>;</span><br>            <span class="hljs-built_in">memset</span>(&amp;sa, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(sa));<br>            sa.sa_handler = io_handler;<br>            sigaction(SIGIO, &amp;sa, <span class="hljs-literal">NULL</span>);<br><br>            fcntl(client_fd, F_SETOWN, getpid());<br>            <span class="hljs-type">int</span> flags = fcntl(client_fd, F_GETFL, <span class="hljs-number">0</span>);<br>            fcntl(client_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK);<br>        &#125;<br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br><br>    <span class="hljs-comment">// 2. 接收数据</span><br>    <span class="hljs-type">char</span> buffer[<span class="hljs-number">1024</span>];<br>    <span class="hljs-type">ssize_t</span> bytes = recv(client_fd, buffer, <span class="hljs-keyword">sizeof</span>(buffer) - <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br>    <span class="hljs-keyword">if</span> (bytes &gt; <span class="hljs-number">0</span>)<br>    &#123;<br>        buffer[bytes] = <span class="hljs-string">&#x27;\0&#x27;</span>;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[信号触发] 收到数据: %s&quot;</span>, buffer);<br>    &#125;<br>    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (bytes == <span class="hljs-number">0</span>)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[信号触发] 客户端断开。\n&quot;</span>);<br>        close(client_fd);<br>        client_fd = <span class="hljs-number">-1</span>;<br>    &#125;<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    server_fd = socket(AF_INET, SOCK_STREAM, <span class="hljs-number">0</span>);<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr_in</span> <span class="hljs-title">addr</span>;</span><br>    addr.sin_family = AF_INET;<br>    addr.sin_addr.s_addr = INADDR_ANY;<br>    addr.sin_port = htons(<span class="hljs-number">9999</span>);<br><br>    bind(server_fd, (<span class="hljs-keyword">struct</span> sockaddr *)&amp;addr, <span class="hljs-keyword">sizeof</span>(addr));<br>    listen(server_fd, <span class="hljs-number">5</span>);<br><br>    <span class="hljs-comment">// --- 对 server_fd 设置信号驱动 I/O ---</span><br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sigaction</span> <span class="hljs-title">sa</span>;</span><br>    <span class="hljs-built_in">memset</span>(&amp;sa, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(sa));<br>    sa.sa_handler = io_handler;<br>    sigaction(SIGIO, &amp;sa, <span class="hljs-literal">NULL</span>);<br><br>    fcntl(server_fd, F_SETOWN, getpid());<br>    <span class="hljs-type">int</span> flags = fcntl(server_fd, F_GETFL, <span class="hljs-number">0</span>);<br>    fcntl(server_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK);<br><br>    client_fd = <span class="hljs-number">-1</span>; <span class="hljs-comment">// 初始化为无效</span><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;等待连接...\n&quot;</span>);<br><br>    <span class="hljs-comment">// --- 主程序干自己的活 ---</span><br>    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;.&quot;</span>);<br>        fflush(<span class="hljs-built_in">stdout</span>);<br>        sleep(<span class="hljs-number">1</span>); <span class="hljs-comment">// 模拟主线程繁忙</span><br>    &#125;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h1 id="同步-IO-vs-异步-IO"><a href="#同步-IO-vs-异步-IO" class="headerlink" title="同步 IO vs 异步 IO"></a>同步 IO vs 异步 IO</h1><p>这是最容易混淆的地方。</p><p>区别标准：数据拷贝阶段是否由用户线程等待</p><h2 id="同步-IO（Synchronous-IO）"><a href="#同步-IO（Synchronous-IO）" class="headerlink" title="同步 IO（Synchronous IO）"></a>同步 IO（Synchronous IO）</h2><p>特点：</p><p>调用 <code>read</code> 数据拷贝完成前线程不能继续</p><p>包括：</p><ul><li>阻塞 IO</li><li>非阻塞 IO</li><li>IO 多路复用</li></ul><p>它们都是同步 IO。</p><h2 id="异步-IO（Asynchronous-IO）"><a href="#异步-IO（Asynchronous-IO）" class="headerlink" title="异步 IO（Asynchronous IO）"></a>异步 IO（Asynchronous IO）</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c">aio_read(...)<br></code></pre></td></tr></table></figure><p>调用后：</p><ul><li>立刻返回</li><li>内核负责等待</li><li>内核负责拷贝</li><li>完成后通知（信号 &#x2F; 回调 &#x2F; 事件）</li></ul><p>从头到尾都没有阻塞</p>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-9f88884b" role="button" aria-expanded="false" aria-controls="collapse-9f88884b">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-9f88884b">        <div class="fold-content">          <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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;fcntl.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;errno.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;aio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;pthread.h&gt;</span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> BUF_SIZE 1024</span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">aio_callback</span><span class="hljs-params">(<span class="hljs-keyword">union</span> sigval sv)</span><br>&#123;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">aiocb</span> *<span class="hljs-title">cb</span> =</span> (<span class="hljs-keyword">struct</span> aiocb *)sv.sival_ptr;<br>    <span class="hljs-type">ssize_t</span> bytes_read = aio_return(cb);<br>    <span class="hljs-keyword">if</span> (bytes_read &gt; <span class="hljs-number">0</span>)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[回调触发] 读取完成！内容如下：\n%.*s\n&quot;</span>, (<span class="hljs-type">int</span>)bytes_read, (<span class="hljs-type">char</span> *)cb-&gt;aio_buf);<br>    &#125;<br>    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (bytes_read == <span class="hljs-number">0</span>)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[回调触发] 文件为空。\n&quot;</span>);<br>    &#125;<br>    <span class="hljs-keyword">else</span><br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n[回调触发] 读取失败：%s\n&quot;</span>, strerror(aio_error(cb)));<br>    &#125;<br>    close(cb-&gt;aio_fildes);<br>    <span class="hljs-built_in">free</span>((<span class="hljs-type">void</span> *)cb-&gt;aio_buf);<br>    <span class="hljs-built_in">free</span>(cb);<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-type">int</span> fd = open(<span class="hljs-string">&quot;aio_read.c&quot;</span>, O_RDONLY);<br>    <span class="hljs-keyword">if</span> (fd &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;Open 失败&quot;</span>);<br>        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;<br>    &#125;<br><br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">aiocb</span> *<span class="hljs-title">cb</span> =</span> <span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(<span class="hljs-keyword">struct</span> aiocb));<br>    <span class="hljs-type">char</span> *buffer = <span class="hljs-built_in">malloc</span>(BUF_SIZE);<br><br>    <span class="hljs-built_in">memset</span>(cb, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(<span class="hljs-keyword">struct</span> aiocb));<br>    cb-&gt;aio_fildes = fd;<br>    cb-&gt;aio_buf = buffer;<br>    cb-&gt;aio_nbytes = BUF_SIZE;<br>    cb-&gt;aio_offset = <span class="hljs-number">0</span>;<br><br>    <span class="hljs-comment">// 设置异步通知方式并注册回调函数</span><br>    cb-&gt;aio_sigevent.sigev_notify = SIGEV_THREAD;<br>    cb-&gt;aio_sigevent.sigev_notify_function = aio_callback;<br>    cb-&gt;aio_sigevent.sigev_value.sival_ptr = cb;<br><br>    <span class="hljs-keyword">if</span> (aio_read(cb) &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;AIO 读取失败&quot;</span>);<br>        close(fd);<br>        <span class="hljs-built_in">free</span>(buffer);<br>        <span class="hljs-built_in">free</span>(cb);<br>        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;<br>    &#125;<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;异步读取已发起，等待回调...\n&quot;</span>);<br><br>    <span class="hljs-comment">// 主线程可以继续执行其他任务</span><br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;主线程正在执行其他任务... %d\n&quot;</span>, i + <span class="hljs-number">1</span>);<br>        sleep(<span class="hljs-number">1</span>);<br>    &#125;<br><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;主程序退出\n&quot;</span>);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h2 id="IO-uring"><a href="#IO-uring" class="headerlink" title="IO_uring"></a>IO_uring</h2><p>io_uring 是 Linux 5.1 引入的一个全新的异步 IO 框架，旨在提供更高效、更灵活的异步 IO 处理方式。它通过使用共享内存环形缓冲区来减少系统调用的开销，实现了真正的零拷贝异步 IO.</p>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-9a1c5b83" role="button" aria-expanded="false" aria-controls="collapse-9a1c5b83">        <div class="fold-arrow">▶</div>示例代码      </div>      <div class="fold-collapse collapse" id="collapse-9a1c5b83">        <div class="fold-content">          <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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;fcntl.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;liburing.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;unistd.h&gt;</span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> ENTRIES 4 <span class="hljs-comment">// 队列深度</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> BUF_SIZE 1024</span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">io_uring</span> <span class="hljs-title">ring</span>;</span><br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">io_uring_sqe</span> *<span class="hljs-title">sqe</span>;</span> <span class="hljs-comment">// 提交项</span><br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">io_uring_cqe</span> *<span class="hljs-title">cqe</span>;</span> <span class="hljs-comment">// 完成项</span><br>    <span class="hljs-type">char</span> buffer[BUF_SIZE];<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">iovec</span> <span class="hljs-title">iov</span>;</span> <span class="hljs-comment">// 描述缓冲区的结构</span><br><br>    <span class="hljs-comment">// 1. 初始化 io_uring 实例</span><br>    io_uring_queue_init(ENTRIES, &amp;ring, <span class="hljs-number">0</span>);<br><br>    <span class="hljs-comment">// 打开一个文件进行测试</span><br>    <span class="hljs-type">int</span> fd = open(<span class="hljs-string">&quot;io_uring.c&quot;</span>, O_RDONLY);<br>    <span class="hljs-keyword">if</span> (fd &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;open&quot;</span>);<br>        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;<br>    &#125;<br><br>    <span class="hljs-comment">// 2. 准备缓冲区信息</span><br>    iov.iov_base = buffer;<br>    iov.iov_len = <span class="hljs-keyword">sizeof</span>(buffer);<br><br>    <span class="hljs-comment">// 3. 获取一个提交项 (SQE)</span><br>    sqe = io_uring_get_sqe(&amp;ring);<br><br>    <span class="hljs-comment">// 4. 设置异步读取任务</span><br>    <span class="hljs-comment">// 告诉内核：帮我读 fd，读到 iov 里，偏移量为 0</span><br>    io_uring_prep_readv(sqe, fd, &amp;iov, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br><br>    <span class="hljs-comment">// 5. 提交任务给内核</span><br>    <span class="hljs-comment">// 这个动作类似发令枪，内核收到后就在后台开始搬运数据了</span><br>    io_uring_submit(&amp;ring);<br><br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;任务已提交至 io_uring，主线程可以去处理其他逻辑了...\n&quot;</span>);<br><br>    <span class="hljs-comment">// 6. 等待并获取任务完成结果 (阻塞直到有一个任务完成)</span><br>    <span class="hljs-type">int</span> ret = io_uring_wait_cqe(&amp;ring, &amp;cqe);<br>    <span class="hljs-keyword">if</span> (ret &lt; <span class="hljs-number">0</span>)<br>    &#123;<br>        perror(<span class="hljs-string">&quot;io_uring_wait_cqe&quot;</span>);<br>        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;<br>    &#125;<br><br>    <span class="hljs-keyword">if</span> (cqe-&gt;res &gt; <span class="hljs-number">0</span>)<br>    &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;读取完成！内容：\n%.*s\n&quot;</span>, cqe-&gt;res, buffer);<br>    &#125;<br><br>    <span class="hljs-comment">// 7. 标记该完成项已被处理</span><br>    io_uring_cqe_seen(&amp;ring, cqe);<br><br>    <span class="hljs-comment">// 8. 清理</span><br>    close(fd);<br>    io_uring_queue_exit(&amp;ring);<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>        </div>      </div>    </div><h1 id="整体对比与总结"><a href="#整体对比与总结" class="headerlink" title="整体对比与总结"></a>整体对比与总结</h1><table><thead><tr><th>模型</th><th>等待阶段</th><th>拷贝阶段</th><th>是否阻塞线程</th></tr></thead><tbody><tr><td>阻塞 IO</td><td>阻塞</td><td>阻塞</td><td>是</td></tr><tr><td>非阻塞 IO</td><td>不阻塞</td><td>阻塞</td><td>部分</td></tr><tr><td>IO 多路复用</td><td>阻塞在select</td><td>阻塞</td><td>是</td></tr><tr><td>信号驱动 IO</td><td>内核通知</td><td>阻塞</td><td>部分</td></tr><tr><td>异步 IO</td><td>内核处理</td><td>内核处理</td><td>否</td></tr></tbody></table>]]>
    </content>
    <id>https://www.harkerhand.cn/io/</id>
    <link href="https://www.harkerhand.cn/io/"/>
    <published>2026-03-02T06:34:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>面试问到了，重新梳理一下</p>
</blockquote>
<h1 id="一次-IO-到底发生了什么？"><a href="#一次-IO-到底发生了什么？" class="headerlink" title="一次 IO]]>
    </summary>
    <title>几种IO方式</title>
    <updated>2026-04-13T06:42:52.014Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="shell" scheme="https://www.harkerhand.cn/tags/shell/"/>
    <category term="terminal" scheme="https://www.harkerhand.cn/tags/terminal/"/>
    <content>
      <![CDATA[<h1 id="掌握数字世界的咒语：从-TTY-到现代-Shell"><a href="#掌握数字世界的咒语：从-TTY-到现代-Shell" class="headerlink" title="掌握数字世界的咒语：从 TTY 到现代 Shell"></a>掌握数字世界的咒语：从 TTY 到现代 Shell</h1><blockquote><p>写在前面：</p><p>很多新生看着电脑屏幕上那个闪烁光标的黑框（Terminal）会感到恐惧。习惯了图形界面（GUI）的点点点，为什么我们还要学习敲命令？</p><p>如果把操作系统比作一辆赛车，<strong>GUI 是方向盘</strong>，让你轻松驾驶；而 <strong>Shell（命令行）则是引擎盖下的机械结构</strong>。作为计算机专业的学生，你不能只会开车，你必须学会修车、改装车，甚至重新设计引擎。</p><p>这篇文章将带你从历史源头出发，理解终端的本质，并掌握这门与计算机内核对话的“母语”。</p></blockquote><h2 id="第一章：溯源-——-为什么是黑底白字？"><a href="#第一章：溯源-——-为什么是黑底白字？" class="headerlink" title="第一章：溯源 —— 为什么是黑底白字？"></a>第一章：溯源 —— 为什么是黑底白字？</h2><h3 id="1-1-历史的幽灵：TTY"><a href="#1-1-历史的幽灵：TTY" class="headerlink" title="1.1 历史的幽灵：TTY"></a>1.1 历史的幽灵：TTY</h3><p>当你打开终端，输入 <code>tty</code> 命令，你会看到类似 <code>/dev/pts/0</code> 的输出。这里的 <strong>TTY</strong> 是 <strong>Teletype</strong>（电传打字机）的缩写。</p><p>在 1960 年代，计算机没有显示器，工程师们通过类似打印机的设备与巨型机通信：你在键盘敲一个字，它打印一个字传给电脑；电脑处理完，再把结果打印在纸上。</p><p>这就是为什么今天的终端依然是“流式”的（一行一行显示），依然保留着黑底白字（模拟墨水与纸张或早期 CRT 单色显示器）的传统。</p><h3 id="1-2-什么是-Shell？"><a href="#1-2-什么是-Shell？" class="headerlink" title="1.2 什么是 Shell？"></a>1.2 什么是 Shell？</h3><p><strong>Shell（壳）</strong> 这个名字非常形象。它是包裹 <strong>Kernel（操作系统内核）</strong> 外面的一层“保护壳”。</p><ul><li><strong>内核</strong> 负责管理 CPU、内存、硬盘，它的权限极高，操作复杂。</li><li><strong>Shell</strong> 是一个翻译官。它接收你输入的英语单词（命令），翻译成内核能听懂的系统调用。</li></ul><h2 id="第二章：解剖-——-别再混淆-Terminal-和-Shell"><a href="#第二章：解剖-——-别再混淆-Terminal-和-Shell" class="headerlink" title="第二章：解剖 —— 别再混淆 Terminal 和 Shell"></a>第二章：解剖 —— 别再混淆 Terminal 和 Shell</h2><p>这是新手最容易搞混的概念，请务必分清这三层架构：</p><ol><li><strong>Terminal (终端模拟器)</strong><ul><li><strong>是什么：</strong> 只是一个画图的窗口程序。</li><li><strong>例子：</strong> macOS 的 Terminal.app &#x2F; iTerm2，Windows 的 Windows Terminal，Linux 的 GNOME Terminal。</li><li><strong>作用：</strong> 负责显示字体、颜色、背景，它<strong>不执行</strong>命令，只负责传输你的按键。</li></ul></li><li><strong>The Shell (解释器)</strong><ul><li><strong>是什么：</strong> 运行在终端窗口里的那个黑箱程序。</li><li><strong>例子：</strong> <code>bash</code>, <code>zsh</code>, <code>fish</code>, <code>sh</code>。</li><li><strong>作用：</strong> 它读取你的输入，解析语法，调用程序，并把结果返回给终端显示。</li></ul></li><li><strong>The Kernel (内核)</strong><ul><li><strong>作用：</strong> 真正的幕后大佬，执行计算和硬件控制。</li></ul></li></ol><blockquote><p>[!TIP]</p><p><strong>热知识：</strong> 你可以在 Windows Terminal (终端) 里运行 Bash (Shell)，也可以在 iTerm2 (终端) 里运行 Python (另一种交互式 Shell)。外壳和灵魂是可以自由搭配的。</p></blockquote><h2 id="第三章：生存指南-——-你的第一组咒语"><a href="#第三章：生存指南-——-你的第一组咒语" class="headerlink" title="第三章：生存指南 —— 你的第一组咒语"></a>第三章：生存指南 —— 你的第一组咒语</h2><p>在命令行里，没有鼠标，你需要靠命令来导航。</p><h3 id="3-1-坐标系导航"><a href="#3-1-坐标系导航" class="headerlink" title="3.1 坐标系导航"></a>3.1 坐标系导航</h3><ul><li><strong><code>pwd</code></strong> (Print Working Directory): <strong>我在哪里？</strong><ul><li>显示当前所在的完整路径。</li></ul></li><li><strong><code>ls</code></strong> (List): <strong>周围有什么？</strong><ul><li><code>ls -l</code> : 显示详细信息（权限、大小、时间）。</li><li><code>ls -a</code> : 显示所有文件（包括以 <code>.</code> 开头的隐藏文件）。</li></ul></li><li><strong><code>cd</code></strong> (Change Directory): <strong>瞬移术</strong><ul><li><code>cd Code</code> : 进入 Code 文件夹。</li><li><code>cd ..</code> : 返回上一级目录。</li><li><code>cd ~</code> : 回家（回到当前用户的 Home 目录）。</li><li><code>cd -</code> : 跳回上一次所在的目录（像电视遥控器的“回看”键）。</li></ul></li></ul><h3 id="3-2-操纵万物（文件操作）"><a href="#3-2-操纵万物（文件操作）" class="headerlink" title="3.2 操纵万物（文件操作）"></a>3.2 操纵万物（文件操作）</h3><ul><li><strong><code>mkdir folder_name</code></strong> : 造房子（新建文件夹）。</li><li><strong><code>touch file.txt</code></strong> : 造空气（新建空文件，或更新文件时间戳）。</li><li><strong><code>cp source dest</code></strong> : 影分身（复制）。<ul><li><code>cp -r folder1 folder2</code> : 递归复制整个文件夹。</li></ul></li><li><strong><code>mv source dest</code></strong> : 搬家（移动）或 改名（重命名）。</li><li><strong><code>rm file</code></strong> : 毁灭（删除）。<ul><li><strong>⚠️ 红色警戒：</strong> <code>rm -rf /</code> 是著名的自杀指令，它会强制递归删除根目录下的一切。在 Linux 中，Root 用户删库是不经过回收站的，删了就是没了。</li></ul></li></ul><h3 id="3-3-查看内容"><a href="#3-3-查看内容" class="headerlink" title="3.3 查看内容"></a>3.3 查看内容</h3><ul><li><strong><code>cat file</code></strong> : 就像把猫倒出来一样，把文件内容一次性全倒在屏幕上（适合短文件）。</li><li><strong><code>less file</code></strong> : 优雅地翻页查看（按 <code>q</code> 退出）。</li><li><strong><code>head</code> &#x2F; <code>tail</code></strong> : 只看头几行或尾几行。</li></ul><h2 id="第四章：核心-——-Unix-哲学与管道艺术"><a href="#第四章：核心-——-Unix-哲学与管道艺术" class="headerlink" title="第四章：核心 —— Unix 哲学与管道艺术"></a>第四章：核心 —— Unix 哲学与管道艺术</h2><p>Unix 哲学有一条黄金法则：</p><blockquote><p><strong>“Do one thing and do it well.”（把一件事做到极致）</strong></p></blockquote><p>Unix 的命令都很简单，但它们可以通过 <strong>管道 (Pipe)</strong> 组合起来，产生无穷的威力。</p><h3 id="4-1-管道符号"><a href="#4-1-管道符号" class="headerlink" title="4.1 管道符号 |"></a>4.1 管道符号 <code>|</code></h3><p>它的作用是：<strong>把上一个命令的输出 (Output)，直接插到下一个命令的输入 (Input)。</strong></p><p>实战案例：</p><p>假设你有一个包含几千个文件的目录，你想知道里面有多少个 Python 文件。</p><ol><li><code>ls -l</code> : 列出所有文件。</li><li><code>grep &quot;.py&quot;</code> : 只要包含 “.py” 的行。</li><li><code>wc -l</code> : 数一下有多少行。</li></ol><p><strong>组合咒语：</strong></p><p>Bash</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">ls</span> -l | grep <span class="hljs-string">&quot;.py&quot;</span> | <span class="hljs-built_in">wc</span> -l<br></code></pre></td></tr></table></figure><p>你不需要写代码，一行命令就解决了复杂逻辑。</p><h3 id="4-2-重定向-和"><a href="#4-2-重定向-和" class="headerlink" title="4.2 重定向 &gt; 和 &gt;&gt;"></a>4.2 重定向 <code>&gt;</code> 和 <code>&gt;&gt;</code></h3><p>如果你不想输出到屏幕，而是想存进文件：</p><ul><li><code>echo &quot;Hello&quot; &gt; hi.txt</code> : <strong>覆盖</strong>写入（如果文件里有字，会被清空）。</li><li><code>echo &quot;World&quot; &gt;&gt; hi.txt</code> : <strong>追加</strong>写入（写在文件末尾，常用于写日志）。</li></ul><h2 id="第五章：进阶-——-权限与环境"><a href="#第五章：进阶-——-权限与环境" class="headerlink" title="第五章：进阶 —— 权限与环境"></a>第五章：进阶 —— 权限与环境</h2><h3 id="5-1-权限-chmod"><a href="#5-1-权限-chmod" class="headerlink" title="5.1 权限 (chmod)"></a>5.1 权限 (chmod)</h3><p>如果你运行脚本报错 Permission denied，通常是因为没有执行权限。</p><p>Unix 文件权限分为：r (读), w (写), x (执行)。</p><ul><li><strong>命令：</strong> <code>chmod +x script.sh</code> (给脚本赋予执行权限)。</li></ul><h3 id="5-2-环境变量-PATH"><a href="#5-2-环境变量-PATH" class="headerlink" title="5.2 环境变量 ($PATH)"></a>5.2 环境变量 ($PATH)</h3><p>为什么你在任何目录下输入 python 都能运行，而输入你写的程序却提示 command not found？</p><p>因为 Shell 手里有一张地图，叫 PATH。</p><ul><li>输入 <code>echo $PATH</code> 看看这张地图。</li><li>Shell 会去这些目录里挨个寻找叫 <code>python</code> 的可执行文件。如果你写的程序不在这些目录里，它就找不到。</li></ul><h2 id="第六章：现代化-——-装修你的终端"><a href="#第六章：现代化-——-装修你的终端" class="headerlink" title="第六章：现代化 —— 装修你的终端"></a>第六章：现代化 —— 装修你的终端</h2><p>2025 年了，不要守着黑白屏幕受苦。现代 Shell 生态非常丰富。</p><h3 id="6-1-你的新朋友：Zsh-Oh-My-Zsh"><a href="#6-1-你的新朋友：Zsh-Oh-My-Zsh" class="headerlink" title="6.1 你的新朋友：Zsh &amp; Oh My Zsh"></a>6.1 你的新朋友：Zsh &amp; Oh My Zsh</h3><p>macOS 现在的默认 Shell 已经是 Zsh。它比古老的 Bash 更智能。</p><p>强烈推荐安装 Oh My Zsh 框架，它能提供：</p><ul><li><strong>自动补全：</strong> 按 Tab 键不仅能补全文件名，还能补全命令参数。</li><li><strong>主题：</strong> 显示 Git 分支状态，再也不怕在错误的分支提交代码。</li><li><strong>插件：</strong> 如 <code>zsh-syntax-highlighting</code>（命令输错了变红，输对了变绿）。</li></ul><h3 id="6-2-下一代工具-Rust-重写版"><a href="#6-2-下一代工具-Rust-重写版" class="headerlink" title="6.2 下一代工具 (Rust 重写版)"></a>6.2 下一代工具 (Rust 重写版)</h3><p>现在的开源社区正在用 Rust 语言重写经典的 Unix 工具，体验飞跃：</p><ul><li>用 <strong><code>bat</code></strong> 替代 <code>cat</code>：带行号和语法高亮。</li><li>用 <strong><code>eza</code></strong> 替代 <code>ls</code>：带图标和颜色区分。</li><li>用 <strong><code>rg</code> (Ripgrep)</strong> 替代 <code>grep</code>：搜索速度快到不可思议。</li><li>用 <strong><code>htop</code></strong> 替代 <code>top</code>：酷炫的系统资源仪表盘。</li></ul><h2 id="结语：从这里开始"><a href="#结语：从这里开始" class="headerlink" title="结语：从这里开始"></a>结语：从这里开始</h2><p>Shell 不仅仅是一个工具，它是计算机科学的基石之一。当你熟练掌握了它，你就拥有了自动化的超能力——那些别人需要点击几百次鼠标才能完成的枯燥工作，你只需要一行脚本就能搞定。</p><p><strong>祝你在命令行的世界里玩得开心！</strong></p>]]>
    </content>
    <id>https://www.harkerhand.cn/Shell/</id>
    <link href="https://www.harkerhand.cn/Shell/"/>
    <published>2025-12-16T06:25:21.000Z</published>
    <summary>
      <![CDATA[<h1 id="掌握数字世界的咒语：从-TTY-到现代-Shell"><a href="#掌握数字世界的咒语：从-TTY-到现代-Shell" class="headerlink" title="掌握数字世界的咒语：从 TTY 到现代]]>
    </summary>
    <title>从 TTY 到现代 Shell</title>
    <updated>2026-04-13T06:42:52.008Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Clash" scheme="https://www.harkerhand.cn/tags/Clash/"/>
    <category term="网络" scheme="https://www.harkerhand.cn/tags/%E7%BD%91%E7%BB%9C/"/>
    <category term="魔法" scheme="https://www.harkerhand.cn/tags/%E9%AD%94%E6%B3%95/"/>
    <content>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>前序文章是：<a href="https://ippclub.org/%E7%88%B1%E4%B8%8E%E9%AD%94%E6%B3%95/">爱与魔法 - I++ Club</a>，当时是以俱乐部的名义发的，所以在内容上更偏向了技术科普，而不是实践操作。并且有相当一部分同学看完之后其实并没有懂，就算正确操作后，在出现偶发的网络问题时也不清楚如何解决。本文的目的就是尽可能去解决这些问题。</p><h1 id="小试牛刀"><a href="#小试牛刀" class="headerlink" title="小试牛刀"></a>小试牛刀</h1><p>首先，请你先处于SEU的校园网环境下（即就是你连接了<code>SEU-ISP</code>或者<code>SEU-WLAN</code>），并且你可以ping通 <code>10.210.126.58</code>（如果你不知道什么是ping的话，点击这个链接 <a href="https://cn.bing.com/search?q=%E6%80%8E%E4%B9%88ping%E4%B8%80%E4%B8%AA%E5%9C%B0%E5%9D%80">怎么ping一个地址 - 搜索</a> ），并且你正在使用Windows 系统。</p><p>如果不满足上述条件的话，下面的小实验就不需要上手操作了，正常阅读文章即可。</p><p> <a href="ms-settings:network-proxy">Windows点击以打开代理设置</a>，点击左边这个链接，应该会进入这个页面</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103141223485.png" alt="image-20251103141223485"></p><p>点击下面的 <strong>手动设置代理</strong> 的 <strong>编辑</strong> 按钮，前面三个选项按图片改，然后点击保存。</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103141356502.png" alt="image-20251103141356502"></p><p>现在访问 <a href="https://www.google.com/">Google</a> 应该就成功了。</p><p><strong>请注意：现在不要直接退出本文！！！</strong> </p><p><strong>请注意：现在不要直接退出本文！！！</strong> </p><p><strong>请注意：现在不要直接退出本文！！！</strong></p><p>虽然你现在已经能翻了，但并不安全，因为上面的设置会将你几乎所有的请求转发到我的电脑上，你的隐私一览无余（你也不想你访问某XX网站的事情被大家知道吧）。</p><p>所以现在你要做的是，先清除上面的<strong>代理IP地址</strong>和<strong>端口</strong>设置，然后关闭<strong>使用代理服务器</strong>开关，并继续阅读本文。</p><h1 id="原理简介"><a href="#原理简介" class="headerlink" title="原理简介"></a>原理简介</h1><p>为什么你按照上面的设置就能访问谷歌？</p><p>首先举个例子，<strong>学生家长来食堂吃饭，但是TA没有校园卡，不能付钱。所以TA求助某位学生，给学生微信转账，让学生帮TA带饭，TA就吃上饭了</strong>。</p><p>如果这个例子不是很形象的话，那么跟刚刚的场景对应一下。</p><p><strong>你想访问谷歌，但是被墙了，访问不了。所以你找到了我，把你的请求告诉我，让我帮你访问谷歌，然后我把谷歌的回复告诉你，你就访问成功了</strong>。</p><p>在这两个例子中，学生对应我，家长对应你，饭对应网络资源。解决问题的关键是，有一个角色可以同时联系被某种因素阻隔的双方。学生可以同时使用微信和校园卡交易，我的电脑也可以同时访问校园网和谷歌。</p><p>无需深究这个中间人为什么能同时联系双方，这不是我们在意的东西，我们只需要建立这个抽象的概念就行。</p><h1 id="使用代理软件"><a href="#使用代理软件" class="headerlink" title="使用代理软件"></a>使用代理软件</h1><p>为什么不能就用上面的代理呢？原因包括但不限于：</p><ul><li>你不能白嫖我的服务</li><li>我跟你可能在现实中相识，你敢把你的隐私交给我吗</li><li>我会毕业，没法让你一直用</li><li>代理可能会出现偶发的异常</li><li>。。。。。。</li></ul><p>解决问题的方法也很简单：<strong>找一个长期稳定提供转发服务的中间人并使用专门的代理软件</strong>。</p><p>中间人要你的隐私大概率没什么用，他们靠卖你服务挣钱，只要不被约喝茶，几年内大概率是不会跑路。</p><p>代理软件直接在这里下载 <a href="https://pan.harkerhand.cn/apps/Clash.Verge_2.4.1_x64-setup.exe">Clash.Verge_2.4.1_x64-setup.exe | MacPan</a>，中间人（下文称机场）有几个推荐：</p><ul><li><a href="https://dg6.me/">狗狗加速</a></li><li><a href="https://llgjc1.com/#/landing">比好更好，比快更快 - 流量光机场</a></li><li><a href="https://mitce.net/?language=chinese">Mitce - 提供線上安全</a></li></ul><p>不翻可能进不去，你可以暂时用一下前文提到的代理。</p><h2 id="获取配置"><a href="#获取配置" class="headerlink" title="获取配置"></a>获取配置</h2><p>这些中间人有很多能同时访问国内国外的服务器，而代理软件的作用是帮你自动挑选合适的服务器（下文称节点）并进行转发。</p><p>所以你需要告诉代理软件，哪些节点是你能用的，应该用什么逻辑去挑选节点，应该用什么方式进行转发，甚至高阶一点的操作是，可以选择某个特定软件的数据走特定节点，不过这些你不用太关心，机场会帮你做好。</p><p>注册账号，买服务，相信这两步应该不会有困难。在机场的首页或者个人页面，应该会找到类似<strong>导入订阅</strong>，<strong>复制订阅地址</strong>等字样，总之，摸索一下（如果要选择订阅类型，选择Clash），<strong>最后要拿到一个网址，这个就是下载配置信息用的</strong>。</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103150505642.png" alt="流量光机场"></p><h2 id="导入配置"><a href="#导入配置" class="headerlink" title="导入配置"></a>导入配置</h2><p>打开你刚刚装好的代理软件，点击左侧边栏的订阅，然后把刚刚获取的链接粘贴到右侧顶部的文本输入框，再点击导入，应该就大功告成了。如果导入失败的话，你可能需要分别尝试在关闭和开启代理（小试牛刀部分）的情况下导入。</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103150822114.png" alt="软件页面"></p><h2 id="代理软件的设置"><a href="#代理软件的设置" class="headerlink" title="代理软件的设置"></a>代理软件的设置</h2><p>点击左侧边栏的设置，然后看右边的设置项。设置原则是：<strong>不懂的不要随便动</strong>。</p><p><strong>系统设置</strong>部分，建议开虚拟网卡模式，如上文所说，系统代理可能有一些问题，建议关掉。</p><p><strong>Clash设置</strong>部分，建议打开DNS覆写。请忽略图示中的<strong>局域网连接</strong>、<strong>IPv6</strong>、<strong>统一延迟</strong>设置项，保持你的默认即可。</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103151431993.png" alt="image-20251103151431993"></p><p>其他部分保持默认。</p><p><strong>至此，基础部分的设置就结束了，你应该可以正常的访问谷歌、Youtube等网站了</strong></p><h1 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h1><p>一般只有问题发生的时候才能想起来有这个问题，所以这部分可能说的不是很全，如有投稿可以联系我补充。</p><h2 id="为什么关机重启之后突然就没网了"><a href="#为什么关机重启之后突然就没网了" class="headerlink" title="为什么关机重启之后突然就没网了"></a>为什么关机重启之后突然就没网了</h2><p>你的Clash设置了系统代理模式，重启之后系统代理设置还在，但Clash没开。把系统代理关了或者打开Clash即可。</p><h2 id="为什么-Git-npm-等命令行工具网络很烂"><a href="#为什么-Git-npm-等命令行工具网络很烂" class="headerlink" title="为什么 Git npm 等命令行工具网络很烂"></a>为什么 Git npm 等命令行工具网络很烂</h2><p>首先看你是不是用了代理模式，如果是的话，使用虚拟网卡模式重试。</p><p>其次看你的报错信息是不是有类似 <code>198.18</code>的字样，如果是的话，在Clash设置——DNS覆写——增强模式中，将fake-ip改为redir-host并重试（设置如图）。</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251103152714802.png" alt="image-20251103152714802"></p><p>如果还是有问题的话，尝试使用系统代理模式并在命令行设置代理环境变量（用下面贴的代码，把10809改为你的Clash设置中的端口），</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-variable">$env:HTTP_PROXY</span>=<span class="hljs-string">&quot;http://127.0.0.1:10809&quot;</span>; <span class="hljs-variable">$env:HTTPS_PROXY</span>=<span class="hljs-string">&quot;http://127.0.0.1:10809&quot;</span><br></code></pre></td></tr></table></figure><p>还是有问题的话，尝试设置Git npm的代理，具体步骤使用搜索引擎，链接同上。以Git举例</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git config --global http.proxy http://127.0.0.1:10809<br>git config --global https.proxy http://127.0.0.1:10809<br></code></pre></td></tr></table></figure><p>还是有问题的话，那我不懂了。。。</p><h1 id="黑魔法"><a href="#黑魔法" class="headerlink" title="黑魔法"></a>黑魔法</h1><h2 id="加速Microsoft-Store"><a href="#加速Microsoft-Store" class="headerlink" title="加速Microsoft Store"></a>加速Microsoft Store</h2><p>在设置——UMP工具中，使用Exempt All全选，然后Save Changes退出。</p><p>![屏幕截图 2025-11-03 163412](.&#x2F;某科学的超黑魔法&#x2F;屏幕截图 2025-11-03 163412.png)</p><h2 id="其他设备借助本机魔法"><a href="#其他设备借助本机魔法" class="headerlink" title="其他设备借助本机魔法"></a>其他设备借助本机魔法</h2><p>打开设置中的局域网访问，将其他设备连接到校园网，确保其他设备能正常访问你的安装了Clash的设备。</p><p>将其他设备的代理设置为魔法设备的IP和端口，IP可以在命令行执行 <code>ipconfig</code> 来查看，端口在Clash的设置中查看。</p><p>图中举例为小米澎湃系统的设置位置</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251104173949188.png" alt="HyperOS"></p><h2 id="借助代理实现校园网免流量"><a href="#借助代理实现校园网免流量" class="headerlink" title="借助代理实现校园网免流量"></a>借助代理实现校园网免流量</h2><p>原理是使用一台设备链接SEU-ISP（运营商提供的不限量网络），其他设备配置这台设备的代理。大致步骤与上一小节相同。</p><h2 id="裸核运行"><a href="#裸核运行" class="headerlink" title="裸核运行"></a>裸核运行</h2><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251104175949552.png" alt="image-20251104175949552"></p><p>Clash只是一个UI，作用是帮你改配置文件，并交给核心（mihomo）来运行，图中那个服务是用于虚拟网卡模式的，可以忽略</p><p>如果使用linux会更显然一点：一个服务，一个UI，一个核心</p><p><img src="/./%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/image-20251104180135345.png" alt="image-20251104180135345"></p><p>如果想轻量级一点，就直接跑内核就行了，对于Windows，内核一般就在Clash目录下，配置一般位于<code>C:\Users\&lt;username&gt;\AppData\Roaming\io.github.clash-verge-rev.clash-verge-rev\clash-verge.yaml</code>。</p><h2 id="自定义分流规则"><a href="#自定义分流规则" class="headerlink" title="自定义分流规则"></a>自定义分流规则</h2><p>前文说到，机场会给一堆节点，这些节点一般会分组，内核支持在组中以手动指定、自动选择、负载均衡等方式来选择节点。大部分机场的不同节点也会对应不同的速率、倍率、解锁规则等。</p><p>那么一个比较理想的方案是，下载大文件应该使用便宜的节点，要用AI应该切换解锁GPT的节点，看流媒体要用解锁奈飞的节点。这些都可以通过自定义配置文件来实现（如果你的机场很人性化，会帮你写好配置文件）</p><p>具体如何配置可以看这个视频 <a href="https://www.youtube.com/watch?v=_iG8vl3pzaM">【全网最细】一次彻底搞懂YAML分流规则：参数讲解+添加规则+故障排查</a></p><h2 id="欢迎投稿"><a href="#欢迎投稿" class="headerlink" title="欢迎投稿"></a>欢迎投稿</h2>]]>
    </content>
    <id>https://www.harkerhand.cn/%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/</id>
    <link href="https://www.harkerhand.cn/%E6%9F%90%E7%A7%91%E5%AD%A6%E7%9A%84%E8%B6%85%E9%BB%91%E9%AD%94%E6%B3%95/"/>
    <published>2025-11-03T05:52:13.000Z</published>
    <summary>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>前序文章是：<a]]>
    </summary>
    <title>某科学的超黑魔法</title>
    <updated>2026-04-13T06:42:52.025Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="TechMagic" scheme="https://www.harkerhand.cn/categories/TechMagic/"/>
    <category term="Rust" scheme="https://www.harkerhand.cn/tags/Rust/"/>
    <category term="FFI" scheme="https://www.harkerhand.cn/tags/FFI/"/>
    <content>
      <![CDATA[<h1 id="什么是-FFI"><a href="#什么是-FFI" class="headerlink" title="什么是 FFI"></a>什么是 FFI</h1><p>FFI (Foreign Function Interface，外部函数接口) 是一种编程机制，允许一种编程语言调用另一种编程语言编写的代码或与另一种语言进行交互。它打破了语言之间的壁垒，使得不同语言编写的模块能够相互协作。</p><h2 id="引入：open-系统调用"><a href="#引入：open-系统调用" class="headerlink" title="引入：open() 系统调用"></a>引入：open() 系统调用</h2><ol><li><strong>Rust</strong>: <code>std::fs::File::open()</code> → <code>libc::open()</code> → <code>syscall(SYS_open)</code></li><li><strong>C</strong>: <code>fopen()</code> → <code>open()</code> → <code>syscall(SYS_open)</code></li><li><strong>C++</strong>: <code>std::fstream</code> → <code>libc::open()</code> → <code>syscall(SYS_open)</code></li></ol><p>open() 是 OS kernel 的编译产物，<br><img src="/./RustFFI/image-20250803165921713.png" alt="image-20250803165921713"></p><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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// add.h</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">add</span><span class="hljs-params">(<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b)</span>;<br><br><span class="hljs-comment">// add.c</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;add.h&quot;</span></span><br><span class="hljs-type">int</span> <span class="hljs-title function_">add</span><span class="hljs-params">(<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b)</span><br>&#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>&#125;<br></code></pre></td></tr></table></figure><p>编译一下，shared表示共享库，fPIC表示位置无关</p><p><img src="/./RustFFI/image-20250803170002994.png" alt="image-20250803170002994"></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><code class="hljs c"><span class="hljs-comment">// useadd.cpp</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span><br>&#123;<br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;add.h&quot;</span></span><br>&#125;<br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span><br>&#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">5</span>;<br>    <span class="hljs-type">int</span> y = <span class="hljs-number">10</span>;<br>    <span class="hljs-type">int</span> result = add(x, y);<br>    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">&quot;The sum of &quot;</span> &lt;&lt; x &lt;&lt; <span class="hljs-string">&quot; and &quot;</span> &lt;&lt; y &lt;&lt; <span class="hljs-string">&quot; is &quot;</span> &lt;&lt; result &lt;&lt; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>-L表示库路径，-l表示链接add库（链接时检查）<br><img src="/./RustFFI/image-20250803170315725.png" alt="image-20250803170315725"></p><p>很好，成功了</p><h1 id="Rust与C-C-的FFI"><a href="#Rust与C-C-的FFI" class="headerlink" title="Rust与C&#x2F;C++的FFI"></a>Rust与C&#x2F;C++的FFI</h1><h2 id="案例再现"><a href="#案例再现" class="headerlink" title="案例再现"></a>案例再现</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-meta">#[unsafe(no_mangle)]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">add</span>(left: <span class="hljs-type">i32</span>, right: <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> &#123;<br>    left + right<br>&#125;<br><br></code></pre></td></tr></table></figure><p>在 <code>Cargo.toml</code> 中添加</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs toml"><span class="hljs-section">[lib]</span><br><span class="hljs-attr">crate-type</span> = [<span class="hljs-string">&quot;cdylib&quot;</span>]<br></code></pre></td></tr></table></figure><p><img src="/./RustFFI/image-20250803170455641.png" alt="image-20250803170455641"></p><p>编译后得到库，链接这个库来编译刚刚的cpp文件</p><p><img src="/./RustFFI/image-20250803170620781.png" alt="image-20250803170620781"></p><p>很好，依然是成功的。</p><p>也就是说，无论是C&#x2F;C++&#x2F;Rust，只要按照C的规范对接口进行暴露，按照C的规范对库进行链接，就可以进行代码复用。</p><h2 id="FFI-规范"><a href="#FFI-规范" class="headerlink" title="FFI 规范"></a>FFI 规范</h2><h3 id="内存布局"><a href="#内存布局" class="headerlink" title="内存布局"></a>内存布局</h3><p>编译器可能会对结构体数据成员进行重排，目的是在对齐的要求下更好的利用内存。</p><p>对于如下的cpp结构体</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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">CppStruct</span> &#123;<br>    <span class="hljs-type">char</span> a;      <span class="hljs-comment">// 1字节</span><br>    <span class="hljs-type">int</span> b;       <span class="hljs-comment">// 4字节</span><br>    <span class="hljs-type">short</span> c;     <span class="hljs-comment">// 2字节</span><br>&#125;;<br></code></pre></td></tr></table></figure><p>内存可能是12位</p><table><thead><tr><th>a</th><th></th><th></th><th></th><th>b</th><th>b</th><th>b</th><th>b</th><th>c</th><th>c</th><th></th><th></th></tr></thead></table><p>Rust类似的结构体重排后可能是8位</p><table><thead><tr><th>b</th><th>b</th><th>b</th><th>b</th><th>c</th><th>c</th><th>a</th><th></th></tr></thead></table><p>要求rust不要重排</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[repr(C)]</span><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">RustStruct</span> &#123; ... &#125;;<br></code></pre></td></tr></table></figure><h3 id="调用约定"><a href="#调用约定" class="headerlink" title="调用约定"></a>调用约定</h3><p>规则上，如何传递参数、返回值以及管理栈和寄存器等底层细节需要一致，实践上一般不用在意，使用<code>extern &quot;C&quot;</code>即可（Rust函数需要<code>#[unsafe(no_mangle)]</code>）。</p><p>习惯上，栈区的小数据通过值传递，堆区的大数据通过指针传递。</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> CPPLIB_HPP</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> CPPLIB_HPP</span><br><br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> __cplusplus</span><br><span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> &#123;<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><br><span class="hljs-comment">// 用 C 链接导出这些函数</span><br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">create_person</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">char</span>* name, <span class="hljs-type">int</span> age)</span></span>;<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">delete_person</span><span class="hljs-params">(<span class="hljs-type">void</span>* person)</span></span>;<br><span class="hljs-function"><span class="hljs-type">const</span> <span class="hljs-type">char</span>* <span class="hljs-title">get_person_name</span><span class="hljs-params">(<span class="hljs-type">void</span>* person)</span></span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">get_person_age</span><span class="hljs-params">(<span class="hljs-type">void</span>* person)</span></span>;<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_person_age</span><span class="hljs-params">(<span class="hljs-type">void</span>* person, <span class="hljs-type">int</span> new_age)</span></span>;<br><br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> __cplusplus</span><br>&#125;<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">// CPPLIB_HPP</span></span><br></code></pre></td></tr></table></figure><p>Person对象通过指针传递，名字字符串通过指针传递，年龄通过值传递</p><h3 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h3><p>一般来说，一个语言的异常无法被另一个语言捕捉，所以一般要通过错误码传递错误信息，或者设置专门传递错误的结构体。</p><h3 id="一些可能忽视的问题"><a href="#一些可能忽视的问题" class="headerlink" title="一些可能忽视的问题"></a>一些可能忽视的问题</h3><h4 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h4><p>上面提到的 Person 的例子，如果在执行 <code>create_person</code> 后没有对应的 <code>delete_person</code>，person则会成为内存垃圾。</p><p>解决方案：</p><ul><li><p>记得delete！</p></li><li><p>C++调用时，可以使用智能指针自动销毁</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">PersonDeleter</span> &#123;<br>     <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(<span class="hljs-type">void</span>* p)</span> <span class="hljs-type">const</span> </span>&#123; <span class="hljs-built_in">delete_person</span>(p); &#125;<br> &#125;;<br> <span class="hljs-keyword">using</span> PersonPtr = std::unique_ptr&lt;<span class="hljs-type">void</span>, PersonDeleter&gt;;<br><br> <span class="hljs-function">PersonPtr <span class="hljs-title">person</span><span class="hljs-params">(create_person(<span class="hljs-string">&quot;Alice&quot;</span>, <span class="hljs-number">30</span>))</span></span>;<br> <span class="hljs-comment">// 退出作用域时自动调用 delete_person</span><br></code></pre></td></tr></table></figure></li><li><p>Rust调用时，为Person的包装实现Drop Trait</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Person</span> &#123;<br>    ptr: *<span class="hljs-keyword">mut</span> c_void,<br>&#125;<br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Drop</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Person</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">drop</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) &#123;<br>        <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-title function_ invoke__">delete_person</span>(<span class="hljs-keyword">self</span>.ptr) &#125;<br>    &#125;<br>&#125;<br><span class="hljs-comment">// Person生命周期结束时自动 drop</span><br></code></pre></td></tr></table></figure></li></ul><h4 id="二次销毁"><a href="#二次销毁" class="headerlink" title="二次销毁"></a>二次销毁</h4><p>上面提到的场景，如果手动调用了delete，那么在变量自动销毁时会出现二次销毁。<br>需要在销毁时增加一些额外的检查。</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Drop</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Person</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">drop</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) &#123;<br>        <span class="hljs-keyword">if</span> !<span class="hljs-keyword">self</span>.ptr.<span class="hljs-title function_ invoke__">is_null</span>() &#123;<br>            <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-title function_ invoke__">delete_person</span>(<span class="hljs-keyword">self</span>.ptr) &#125;<br>            <span class="hljs-keyword">self</span>.ptr = std::ptr::<span class="hljs-title function_ invoke__">null_mut</span>(); <span class="hljs-comment">// 防止二次销毁</span><br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="悬垂指针"><a href="#悬垂指针" class="headerlink" title="悬垂指针"></a>悬垂指针</h4><p>假设存在场景，异步接口传递临时指针</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">call</span><span class="hljs-params">()</span> </span>&#123;<br>    Stream stream;<br>    std::string stage = <span class="hljs-string">&quot;initial&quot;</span>;<br>    stream.stage = <span class="hljs-built_in">const_cast</span>&lt;<span class="hljs-type">char</span> *&gt;(stage.<span class="hljs-built_in">c_str</span>());<br>    stream.node_id = <span class="hljs-number">1</span>;<br>    stream.session_id = <span class="hljs-number">1001</span>;<br>    stream.err_code = <span class="hljs-number">0</span>;<br>    std::string data = <span class="hljs-string">&quot;Hello, World!&quot;</span>;<br>    stream.data = <span class="hljs-built_in">const_cast</span>&lt;<span class="hljs-type">char</span> *&gt;(data.<span class="hljs-built_in">c_str</span>());<br>    stream.stream_end = <span class="hljs-literal">false</span>;<br>    <span class="hljs-built_in">sync_call</span>(stream);<br>    <span class="hljs-built_in">async_call</span>(stream);<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-built_in">call</span>();<br>    std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">seconds</span>(<span class="hljs-number">10</span>));<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>这里的async_call在调用后立即返回，随后string析构，但传递的指针在一段时候后才被解引用。</p><p>解决方法：</p><ul><li>使用Rust编写库函数，上面的例子中，<code>*mut c_char</code> 没有实现 <code>Send</code> Trait，所以需要先对传入的临时字符指针进行<strong>同步</strong>拷贝，再将拷贝值 move 到<strong>异步</strong>回调中。<br>为了实现上面的例子，不得不使用unsafe</li></ul><h2 id="Rust-C-FFI-的便捷实现"><a href="#Rust-C-FFI-的便捷实现" class="headerlink" title="Rust C FFI 的便捷实现"></a>Rust C FFI 的便捷实现</h2><p>使用bindgen自动生成绑定</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs toml"><span class="hljs-section">[build-dependencies]</span><br><span class="hljs-attr">bindgen</span> = <span class="hljs-string">&quot;0.72.0&quot;</span><br></code></pre></td></tr></table></figure><p>在项目根目录下新建一个 <code>build.rs</code> 文件即可</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::path::PathBuf;<br><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() &#123;<br>    <span class="hljs-comment">// 告诉 Cargo 在哪里找到链接库</span><br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;cargo:rustc-link-search=native=/root/rust/rust-ffi/cpp/lib&quot;</span>);<br><br>    <span class="hljs-comment">// 链接需要的C++库</span><br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;cargo:rustc-link-lib=dylib=person&quot;</span>);<br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;cargo:rustc-link-lib=dylib=callback&quot;</span>);<br><br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">bindings</span> = bindgen::Builder::<span class="hljs-title function_ invoke__">default</span>()<br>        .<span class="hljs-title function_ invoke__">clang_arg</span>(<span class="hljs-string">&quot;-xc++&quot;</span>)<br>        .<span class="hljs-title function_ invoke__">header</span>(<span class="hljs-string">&quot;/root/rust/rust-ffi/cpp/include/person.h&quot;</span>)<br>        .<span class="hljs-title function_ invoke__">header</span>(<span class="hljs-string">&quot;/root/rust/rust-ffi/cpp/include/callback.h&quot;</span>)<br>        .<span class="hljs-title function_ invoke__">generate</span>()<br>        .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Failed to generate bindings&quot;</span>);<br><br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">_</span> = bindings.<span class="hljs-title function_ invoke__">write_to_file</span>(PathBuf::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;src/bindings.rs&quot;</span>));<br>&#125;<br><br></code></pre></td></tr></table></figure><h1 id="Rust与JS的FFI"><a href="#Rust与JS的FFI" class="headerlink" title="Rust与JS的FFI"></a>Rust与JS的FFI</h1><h2 id="NodeJS运行时调用流程"><a href="#NodeJS运行时调用流程" class="headerlink" title="NodeJS运行时调用流程"></a><strong>NodeJS运行时调用流程</strong></h2><ol><li><p><strong>JS 发起调用</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> tool= <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;libadd.node&#x27;</span>);<br>tool.<span class="hljs-title function_">add</span>(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>);<br></code></pre></td></tr></table></figure><p><em>对于常规的js模块，在require时会执行一次这个模块，返回一个exports对象</em><br>同样的，require引入Native库会自动执行库中的<code>napi_register_module_v1</code>，这个函数需要返回一个exports对象</p></li><li><p><strong>Node.js 引擎处理</strong></p><ul><li>Node.js 将 JS 参数转换为 N-API 可识别的数据结构。</li><li>创建一个 <code>napi_callback_info</code> 对象，封装调用上下文（参数、this 值等）。</li></ul></li><li><p><strong>C函数执行</strong></p><ul><li>N-API 的绑定函数被调用，接收 <code>napi_env</code> 和 <code>napi_callback_info</code>。</li><li>从 <code>napi_callback_info</code> 中提取参数，转换为 C 类型。</li><li>调用底层 C 函数。</li></ul></li><li><p><strong>返回结果给 JS</strong></p><ul><li>C 将结果通过 <code>napi_env</code> 转换为 JS 类型。</li><li>Node.js 将结果返回给 JS 调用方。</li></ul></li></ol><h2 id="初始化部分"><a href="#初始化部分" class="headerlink" title="初始化部分"></a>初始化部分</h2><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-meta">#[unsafe(no_mangle)]</span><br><span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">napi_register_module_v1</span>(env: napi_env, exports: napi_value) <span class="hljs-punctuation">-&gt;</span> napi_value &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">register_name</span> = CString::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-string">&quot;rustAdd&quot;</span>).<span class="hljs-title function_ invoke__">unwrap</span>();<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">register_desc</span> = [napi_property_descriptor &#123;<br>        utf8name: register_name.<span class="hljs-title function_ invoke__">as_ptr</span>(),<br>        name: <span class="hljs-title function_ invoke__">nullptr</span>(),<br>        method: <span class="hljs-title function_ invoke__">Some</span>(add::rust_add),<br>        getter: <span class="hljs-literal">None</span>,<br>        setter: <span class="hljs-literal">None</span>,<br>        value: <span class="hljs-title function_ invoke__">nullptr</span>(),<br>        attributes: napi_property_attributes::napi_default,<br>        data: <span class="hljs-title function_ invoke__">nullptr</span>(),<br>    &#125;];<br><br>    <span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-title function_ invoke__">napi_define_properties</span>(env, exports, <span class="hljs-number">1</span>, register_desc.<span class="hljs-title function_ invoke__">as_ptr</span>()) &#125;;<br>    exports<br>&#125;<br></code></pre></td></tr></table></figure><p>做了很简单一件事，将一些 <code>napi_property_descriptor</code> 添加到exports对象中</p><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">napi_property_descriptor</span> &#123;<br>    <span class="hljs-keyword">pub</span> utf8name: *<span class="hljs-keyword">const</span> ::std::os::raw::c_char,<br>    <span class="hljs-keyword">pub</span> name: napi_value,<br>    <span class="hljs-keyword">pub</span> method: napi_callback,<br>    <span class="hljs-keyword">pub</span> getter: napi_callback,<br>    <span class="hljs-keyword">pub</span> setter: napi_callback,<br>    <span class="hljs-keyword">pub</span> value: napi_value,<br>    <span class="hljs-keyword">pub</span> attributes: napi_property_attributes,<br>    <span class="hljs-keyword">pub</span> data: *<span class="hljs-keyword">mut</span> ::std::os::raw::c_void,<br>&#125;<br><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">napi_define_properties</span>(<br>    env: napi_env,<br>    object: napi_value,<br>    property_count: <span class="hljs-type">usize</span>,<br>    properties: *<span class="hljs-keyword">const</span> napi_property_descriptor,<br>) <span class="hljs-punctuation">-&gt;</span> napi_status;<br></code></pre></td></tr></table></figure><p>相关的定义可以使用bindgen工具生成，<a href="https://codehub-y.huawei.com/g50049197/napi-sys/home">napi-sys</a>，这个库中有详细的README；也可以直接使用 <a href="https://crates.io/crates/napi">crates.io&#x2F;crates&#x2F;napi</a> 库。</p><h2 id="参数转化与返回值处理"><a href="#参数转化与返回值处理" class="headerlink" title="参数转化与返回值处理"></a>参数转化与返回值处理</h2><p>上文提到的 <code>napi_property_descriptor</code> 中的成员<code>method</code>类型为<code>napi_callback</code>，</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">napi_callback</span> = ::std::option::<span class="hljs-type">Option</span>&lt;<br>    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> <span class="hljs-title function_ invoke__">fn</span>(env: napi_env, info: napi_callback_info) <span class="hljs-punctuation">-&gt;</span> napi_value,<br></code></pre></td></tr></table></figure><p>nodejs引擎会将函数调用转化为环境+上下文信息的形式，需要对信息进行解析</p><ul><li>获取入参</li></ul><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">napi_get_cb_info</span>(<br>    env: napi_env,<br>    cbinfo: napi_callback_info,<br>    argc: *<span class="hljs-keyword">mut</span> <span class="hljs-type">usize</span>, <span class="hljs-comment">// 入参个数</span><br>    argv: *<span class="hljs-keyword">mut</span> napi_value, <span class="hljs-comment">// 预分配的入参数组</span><br>    this_arg: *<span class="hljs-keyword">mut</span> napi_value, <span class="hljs-comment">// 接收js调用者</span><br>    data: *<span class="hljs-keyword">mut</span> *<span class="hljs-keyword">mut</span> ::std::os::raw::c_void, <span class="hljs-comment">// 注册函数时附加的信息</span><br>) <span class="hljs-punctuation">-&gt;</span> napi_status;<br></code></pre></td></tr></table></figure><ul><li>napi值转化为rust值</li></ul><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">a</span>: <span class="hljs-type">i32</span> = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">b</span>: <span class="hljs-type">i32</span> = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">unsafe</span> &#123;<br>    <span class="hljs-title function_ invoke__">napi_get_value_int32</span>(env, argv[<span class="hljs-number">0</span>], &amp;<span class="hljs-keyword">mut</span> a);<br>    <span class="hljs-title function_ invoke__">napi_get_value_int32</span>(env, argv[<span class="hljs-number">1</span>], &amp;<span class="hljs-keyword">mut</span> b);<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>具体Rust逻</li><li>rust值转化为napi值</li></ul><figure class="highlight rust"><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><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">sum</span> = a + b;<br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">nullptr</span>();<br><span class="hljs-keyword">unsafe</span> &#123; <span class="hljs-title function_ invoke__">napi_create_int32</span>(env, sum, &amp;<span class="hljs-keyword">mut</span> result); &#125;<br>result<br></code></pre></td></tr></table></figure><h2 id="编译并从JS侧使用"><a href="#编译并从JS侧使用" class="headerlink" title="编译并从JS侧使用"></a>编译并从JS侧使用</h2><p><code>Cargo.toml</code> 中需要标注</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs toml"><span class="hljs-section">[lib]</span><br><span class="hljs-attr">crate-type</span> = [<span class="hljs-string">&quot;cdylib&quot;</span>]<br></code></pre></td></tr></table></figure><p>才会编译出动态链接库</p><p>将编译的库重命名为 <code>*.node</code>，在js中通过require导入使用</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> &#123; rustAdd &#125; = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;./libtool.node&#x27;</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">rustAdd</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)); <span class="hljs-comment">// Should print 3</span><br></code></pre></td></tr></table></figure><h2 id="Rust-JS-FFI-的便捷实现"><a href="#Rust-JS-FFI-的便捷实现" class="headerlink" title="Rust JS FFI 的便捷实现"></a>Rust JS FFI 的便捷实现</h2><p>假设一个场景，rust需要向js暴露一个类，提供构造函数和成员的getter setter，同时暴露一个函数，有一个入参是这个类。（只是举个例子，实际上不推荐这么做）</p><p>上面的需求让AI生成，大概300行，可以使用现有的库来简化（前文提到的napi-rs），各种详细用法的文档位于 <a href="https://napi.rs/">napi.rs</a></p><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> napi::&#123;Error, <span class="hljs-type">Result</span>&#125;;<br><span class="hljs-keyword">use</span> napi_derive::napi;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">MyClass</span> &#123;<br>  value: <span class="hljs-type">i32</span>,<br>  name: <span class="hljs-type">String</span>,<br>&#125;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">MyClass</span> &#123;<br>  <span class="hljs-meta">#[napi(constructor)]</span><br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>(value: <span class="hljs-type">i32</span>, name: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; &#123;<br>    <span class="hljs-keyword">if</span> name.<span class="hljs-title function_ invoke__">is_empty</span>() &#123;<br>      <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(Error::<span class="hljs-title function_ invoke__">new</span>(<br>        napi::Status::InvalidArg,<br>        <span class="hljs-string">&quot;Name cannot be empty&quot;</span>.<span class="hljs-title function_ invoke__">to_string</span>(),<br>      ));<br>    &#125;<br>    <span class="hljs-title function_ invoke__">Ok</span>(MyClass &#123; value, name &#125;)<br>  &#125;<br>  <span class="hljs-meta">#[napi(getter)]</span><br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">value</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> &#123;<br>    <span class="hljs-keyword">self</span>.value<br>  &#125;<br>  <span class="hljs-meta">#[napi(getter)]</span><br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">name</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">str</span> &#123;<br>    &amp;<span class="hljs-keyword">self</span>.name<br>  &#125;<br>  <span class="hljs-meta">#[napi(setter)]</span><br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">set_value</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, value: <span class="hljs-type">i32</span>) &#123;<br>    <span class="hljs-keyword">self</span>.value = value;<br>  &#125;<br>  <span class="hljs-meta">#[napi(setter)]</span><br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">set_name</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, name: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;()&gt; &#123;<br>    <span class="hljs-keyword">if</span> name.<span class="hljs-title function_ invoke__">is_empty</span>() &#123;<br>      <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(Error::<span class="hljs-title function_ invoke__">new</span>(<br>        napi::Status::InvalidArg,<br>        <span class="hljs-string">&quot;Name cannot be empty&quot;</span>.<span class="hljs-title function_ invoke__">to_string</span>(),<br>      ));<br>    &#125;<br>    <span class="hljs-keyword">self</span>.name = name;<br>    <span class="hljs-title function_ invoke__">Ok</span>(())<br>  &#125;<br>&#125;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">print_myclass</span>(obj: &amp;MyClass) &#123;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;[Rust] MyClass: value=&#123;&#125;, name=&#x27;&#123;&#125;&#x27;&quot;</span>, obj.value, obj.name);<br>&#125;<br><br></code></pre></td></tr></table></figure><h2 id="napi库的一些小问题"><a href="#napi库的一些小问题" class="headerlink" title="napi库的一些小问题"></a>napi库的一些小问题</h2><p>JS是单线程语言，通过事件循环处理异步</p><ul><li>普通函数，只能在主线程执行</li><li>线程安全函数，可以在线程间移动，一般用于回调</li></ul><p>场景：JS可以使用number注册一个入参为string的回调，并使用number调用。</p><p>这种情况，可以将传入的回调（普通函数）转为线程安全函数来存储</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">send_rust_callback</span>(<br>    id: <span class="hljs-type">i32</span>,<br>    callback: Function&lt;<span class="hljs-symbol">&#x27;static</span>, <span class="hljs-type">String</span>&gt;,<br>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;()&gt; &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">tsfn</span> = callback<br>        .<span class="hljs-title function_ invoke__">build_threadsafe_function</span>()<br>        .callee_handled::&lt;<span class="hljs-literal">true</span>&gt;()<br>        .<span class="hljs-title function_ invoke__">build</span>()<br>        .<span class="hljs-title function_ invoke__">unwrap</span>();<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">map</span> = CALLBACK_MAP.<span class="hljs-title function_ invoke__">get_or_init</span>(|| Mutex::<span class="hljs-title function_ invoke__">new</span>(std::collections::HashMap::<span class="hljs-title function_ invoke__">new</span>()));<br>    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">map</span> = map.<span class="hljs-title function_ invoke__">lock</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br>    map.<span class="hljs-title function_ invoke__">insert</span>(id, tsfn);<br>    <span class="hljs-title function_ invoke__">Ok</span>(())<br>&#125;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">call_rust_callback</span>(id: <span class="hljs-type">i32</span>, message: <span class="hljs-type">String</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;()&gt; &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">map</span> = CALLBACK_MAP.<span class="hljs-title function_ invoke__">get</span>().<span class="hljs-title function_ invoke__">unwrap</span>().<span class="hljs-title function_ invoke__">lock</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>(cb) = map.<span class="hljs-title function_ invoke__">get</span>(&amp;id) &#123;<br>        cb.<span class="hljs-title function_ invoke__">call</span>(<br>            <span class="hljs-title function_ invoke__">Ok</span>(message),<br>            napi::threadsafe_function::ThreadsafeFunctionCallMode::NonBlocking,<br>        );<br>    &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Err</span>(Error::<span class="hljs-title function_ invoke__">new</span>(<br>            napi::Status::InvalidArg,<br>            <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;No callback found for id: &#123;&#125;&quot;</span>, id),<br>        ));<br>    &#125;<br><br>    <span class="hljs-title function_ invoke__">Ok</span>(())<br>&#125;<br></code></pre></td></tr></table></figure><p>自动生成的 <code>d.ts</code> 文件会是</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">declare</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">callRustCallback</span>(<span class="hljs-params"><span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">void</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">declare</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">sendRustCallback</span>(<span class="hljs-params"><span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">callback</span>: <span class="hljs-built_in">any</span></span>): <span class="hljs-built_in">void</span><br></code></pre></td></tr></table></figure><p>这样调用，看起来完全没问题</p><figure class="highlight ts"><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><code class="hljs ts"><span class="hljs-title function_">sendRustCallback</span>(<span class="hljs-number">114</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Callback from Rust called!&#x27;</span>, message)<br>&#125;)<br><span class="hljs-title function_">callRustCallback</span>(<span class="hljs-number">114</span>, <span class="hljs-string">&#x27;Hello from TypeScript!&#x27;</span>)<br></code></pre></td></tr></table></figure><p>但会输出</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">Callback from Rust called! null<br></code></pre></td></tr></table></figure><p>因为回调的约定实际上是 <code>(err: Error | null, arg: string) =&gt; void</code></p><ul><li><code>Ok(value)</code> → <code>(null, value)</code></li><li><code>Err(e)</code> → <code>(new Error(e), undefined)</code></li></ul><p>解决方案：</p><ul><li>手动修改<code>d.ts</code>文件的类型约束</li><li>所有回调函数入参全部使用线程安全函数（这样自动生成的dts文件就是正确的）</li></ul><h1 id="Rust与OH的FFI"><a href="#Rust与OH的FFI" class="headerlink" title="Rust与OH的FFI"></a>Rust与OH的FFI</h1><h2 id="与常规JS的区别"><a href="#与常规JS的区别" class="headerlink" title="与常规JS的区别"></a>与常规JS的区别</h2><p>JSNAPI 通过 dlopen 打开 node 包，然后执行 <code>napi_register_module_v1</code>  来注册包</p><p>OH通过将包的注册函数添加到 <code>.init_array</code> 段，当程序启动，这个库被链接时自动调用<br><img src="/./RustFFI/process_napi.png" alt="process_napi"></p><p>其余部分几乎完全一致</p><h2 id="Rust-OH-FFI的便捷实现"><a href="#Rust-OH-FFI的便捷实现" class="headerlink" title="Rust OH FFI的便捷实现"></a>Rust OH FFI的便捷实现</h2><p>使用 <a href="https://ohos.rs/">ohos.rs</a></p><p>用法几乎与上文 napi-rs 类似的方式相同</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[napi(object)]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Person</span> &#123;<br>    <span class="hljs-keyword">pub</span> name: <span class="hljs-type">String</span>,<br>    <span class="hljs-keyword">pub</span> age: <span class="hljs-type">u32</span>,<br>&#125;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">say_hello</span>(person: Person) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> &#123;<br>    <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;Hello, &#123;&#125;! You are &#123;&#125; years old.&quot;</span>, person.name, person.age)<br>&#125;<br><span class="hljs-meta">#[napi]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">rust_call_function</span>(func: ThreadsafeFunction&lt;Person, ()&gt;) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">bool</span> &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">person</span> = Person &#123;<br>        name: <span class="hljs-string">&quot;Alice&quot;</span>.<span class="hljs-title function_ invoke__">to_string</span>(),<br>        age: <span class="hljs-number">30</span>,<br>    &#125;;<br>    func.<span class="hljs-title function_ invoke__">call_with_return_value</span>(<br>        <span class="hljs-title function_ invoke__">Ok</span>(person),<br>        ThreadsafeFunctionCallMode::NonBlocking,<br>        |ret, _env| &#123;<br>            <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, ret);<br>            <span class="hljs-title function_ invoke__">Ok</span>(())<br>        &#125;<br>    ) == Status::<span class="hljs-literal">Ok</span><br>&#125;<br></code></pre></td></tr></table></figure><p><code>ohrs build</code>  编译后即可得到 <code>index.d.ts</code> 与 <code>libxxx.so</code>（也可以使用GN + Ninja编译，文档位于<a href="https://docs.openharmony.cn/pages/v5.1/zh-cn/device-dev/subsystems/subsys-build-rust-compilation.md">Rust模块配置规则和指导</a> ）</p><h2 id="在鸿蒙项目中使用-so"><a href="#在鸿蒙项目中使用-so" class="headerlink" title="在鸿蒙项目中使用 so"></a>在鸿蒙项目中使用 so</h2><p>新建一个NativeC++项目后，有关cpp的目录中</p><p><code>types</code>文件夹中的每一个子文件夹声明一个模块的类型，在 <code>entry/oh-package.json5</code> 中引入。CMakeLists用于编译 <code>napi_init.cpp</code>，在 <code>entry/build-profile.json5</code> 中引入，对于Rust自行编译so来说意义不大。</p><figure class="highlight json"><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><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  ...<br>  <span class="hljs-attr">&quot;dependencies&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;libentry.so&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;file:./src/main/cpp/types/libentry&quot;</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>对于刚刚编译的 <code>libdemo.so</code>，项目路径应该是这样的，在<code>types</code>文件夹中声明类型，在在 <code>entry/oh-package.json5</code> 中引入，将so放入libs下对应平台的文件夹，就可以直接在app中使用了</p><figure class="highlight ts"><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><code class="hljs ts"><span class="hljs-keyword">import</span> &#123; add, <span class="hljs-title class_">Person</span>, rustCallFunction, sayHello &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;libdemo.so&#x27;</span>;<br>hilog.<span class="hljs-title function_">info</span>(<span class="hljs-variable constant_">DOMAIN</span>, <span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">&#x27;Message changed to %&#123;public&#125;s&#x27;</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">message</span>);<br>hilog.<span class="hljs-title function_">info</span>(<span class="hljs-variable constant_">DOMAIN</span>, <span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">&#x27;Test Hello 2 + 3 = %&#123;public&#125;d&#x27;</span>, <span class="hljs-title function_">add</span>(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>));<br><span class="hljs-title function_">rustCallFunction</span>(<span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">err</span>: <span class="hljs-title class_">Error</span> | <span class="hljs-literal">null</span>, <span class="hljs-attr">person</span>: <span class="hljs-title class_">Person</span></span>) =&gt;</span> &#123;<br><span class="hljs-keyword">if</span> (err) hilog.<span class="hljs-title function_">error</span>(<span class="hljs-variable constant_">DOMAIN</span>, <span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">&#x27;Error calling Rust function: %&#123;public&#125;s&#x27;</span>, err.<span class="hljs-property">message</span>);<br>    hilog.<span class="hljs-title function_">info</span>(<span class="hljs-variable constant_">DOMAIN</span>, <span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">&#x27;Say Hello from Rust %&#123;public&#125;s&#x27;</span>, <span class="hljs-title function_">sayHello</span>(person));<br>    hilog.<span class="hljs-title function_">info</span>(<span class="hljs-variable constant_">DOMAIN</span>, <span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">&#x27;Rust function returned: %&#123;public&#125;s, %&#123;public&#125;d&#x27;</span>, person.<span class="hljs-property">name</span>, person.<span class="hljs-property">age</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><h2 id="鸿蒙系统库"><a href="#鸿蒙系统库" class="headerlink" title="鸿蒙系统库"></a>鸿蒙系统库</h2><p><code>d.ts</code> 文件位于SDK目录中，接口库位于 <code>/sys/lib64/module</code></p>]]>
    </content>
    <id>https://www.harkerhand.cn/RustFFI/</id>
    <link href="https://www.harkerhand.cn/RustFFI/"/>
    <published>2025-08-03T08:56:19.000Z</published>
    <summary>
      <![CDATA[<h1 id="什么是-FFI"><a href="#什么是-FFI" class="headerlink" title="什么是 FFI"></a>什么是 FFI</h1><p>FFI (Foreign Function Interface，外部函数接口)]]>
    </summary>
    <title>RustFFI</title>
    <updated>2026-04-13T06:42:52.004Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Python" scheme="https://www.harkerhand.cn/tags/Python/"/>
    <category term="NCatBot" scheme="https://www.harkerhand.cn/tags/NCatBot/"/>
    <content>
      <![CDATA[<h1 id="AI猫娘-启动！"><a href="#AI猫娘-启动！" class="headerlink" title="AI猫娘 启动！"></a>AI猫娘 启动！</h1><p>NcatBot，基于 NapCat 的 QQ 机器人 Python SDK，快速开发，轻松部署。</p><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>我们 I++ 俱乐部的小伙伴一直悄悄主持维护了一个QQ聊天机器人的项目，并在最近才分享给了大家。虽然这个项目的star数还没到三位数，但在简单浏览其 README 和文档之后，我们都感到非常震撼！这个项目将QQ聊天Bot的部署成本降低到了<strong>只需要基础Python语法</strong>的地步。得益于其优秀的插件功能，你甚至可以<strong>一行代码不写</strong>，就实现诸如课程自动提醒、群聊自动参与聊天等功能。看了项目后，I++ 俱乐部的社区氛围非常活跃，大家都在积极分享和交流这个项目的使用心得。那么事不宜迟，速速开始！</p><h1 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h1><p>一开始就从枯燥的教学开始，太过乏味，买药之前不如先看看疗效</p><p>在我们提供的配置下，猫猫的一般最小回复间隔是10秒，如果被@到会强制回答喔</p><p><img src="/AI%E7%8C%AB%E5%A8%98%20%E5%90%AF%E5%8A%A8%EF%BC%81/image-20250305221039177.png" alt="image-20250305221039177"></p><p><img src="/AI%E7%8C%AB%E5%A8%98%20%E5%90%AF%E5%8A%A8%EF%BC%81/image-20250305221233188.png" alt="image-20250305221233188"></p><h1 id="基础准备"><a href="#基础准备" class="headerlink" title="基础准备"></a>基础准备</h1><p>进入 <a href="https://docs.ncatbot.xyz/">NcatBot 文档 | NcatBot 文档</a>，点击<strong>快速</strong>开始（我已经等不及了，急急急！）</p><p><em>这部分内容较为小白向，如果你对自己的技术充分自信，直接跳到第二节即可</em></p><p>显然，你需要装一个Python，你可以从这里下载 <a href="https://www.python.org/downloads/release/python-3129/">Python Release Python 3.12.9 | Python.org</a>，（如果你不知道选什么版本的话，直接装这个 <a href="https://harker.lanzouw.com/iZezN2po6asb">Python312Win64</a> 就可以了。</p><p>随后，你需要安装 NCatBot，在命令行执行下面的代码即可，如果你不理解前两行的意思，只执行第三行即可。</p><figure class="highlight shell"><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><code class="hljs shell">python -m venv .venv<br>./.venv/Scripts/Activate<br>pip install ncatbot -U -i https://mirrors.aliyun.com/pypi/simple<br></code></pre></td></tr></table></figure><p>随后，检查是否安装成功，命令行输入 <code>python</code> <strong>之后</strong>输入下面的代码，如果成功输出版本号, 则安装成功，输入 <code>exit</code> 回车退出python。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> ncatbot<br><span class="hljs-built_in">print</span>(ncatbot.__version__)<br></code></pre></td></tr></table></figure><p>随后执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">mkdir ncatbot<br>cd ncatbot<br></code></pre></td></tr></table></figure><p>在新建一个 <code>main.py</code> 文件，在其中粘贴<strong>文档中</strong>的代码，并把第8行的”123456“改为你要部署机器人的QQ号。</p><p>执行下面的代码会运行 <code>main.py</code> ，随后会提示你安装napcat，输入N回车即可，随后会弹窗要求你扫码登录（它甚至不需要你输密码，我哭死），登录完成后，Bot应该就正常运行了。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">python main.py<br></code></pre></td></tr></table></figure><p>向Bot发送“测试”，Bot会回复</p><p><img src="/AI%E7%8C%AB%E5%A8%98%20%E5%90%AF%E5%8A%A8%EF%BC%81/image-20250305192930202.png" alt="image-20250305192930202"></p><p>那么恭喜你，安装成功了！</p><h1 id="插件配置"><a href="#插件配置" class="headerlink" title="插件配置"></a>插件配置</h1><p>在 <code>main.py</code> 同目录下新建文件夹 <code>plugins</code></p><p>将 <a href="https://github.com/IppClub/ncatbot-catcat/archive/refs/tags/v1.0.4.zip">ncatbot-catcat-v1.0.4(GitHub)</a> 或 <a href="https://harker.lanzouw.com/i8n8y2psihqh">ncatbot-catcat-v1.0.4(LanzouCloud)</a> 解压到 <code>plugins</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><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><code class="hljs text">ncatbot/<br>├── plugins/<br>│   └── CatCat/<br>│   │   ├── main.py<br>│   │   ├── requirements.txt<br>│   |   └── OtherFiles<br>│   └── OtherPlugin/<br>├── napcat/<br>│   └── files<br>└── main.py<br></code></pre></td></tr></table></figure><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">pip install -r plugins\CatCat\requirements.txt<br></code></pre></td></tr></table></figure><p>然后在 <code>plugins\CatCat\config\config.yaml</code> 中填写你的 <code>API_key</code> 和 <code>plugins\CatCat\config\cat_prompt.txt</code> 中填写猫娘的 <code>prompt</code>，保存后插件就可以正确运行啦！</p><p><em><strong>注意：你需要把Bot账号的昵称改为”猫猫“或在群内将其昵称改为“猫猫”</strong></em></p><h2 id="深入配置"><a href="#深入配置" class="headerlink" title="深入配置"></a>深入配置</h2><p>如果你只是想要一只猫猫聊天，下面就不需要看了喵~</p><p>下面的配置针对 <em><strong>v1.0.4</strong></em> 版本，其余版本代码位置不一定对应 </p><h3 id="关于"><a href="#关于" class="headerlink" title="关于@"></a>关于@</h3><p>如果你不想更改Bot昵称，在 <code>plugins\CatCat\AiChat.py</code> line 37，将其中的“@猫猫”修改为”@&lt;Nick Name&gt;“，其中”Nick Name“是Bot的昵称。</p><h3 id="关于回复延迟"><a href="#关于回复延迟" class="headerlink" title="关于回复延迟"></a>关于回复延迟</h3><p>如果你对猫猫的回复延迟感到恼火，认为她经常不回你的消息，在 <code>plugins\CatCat\AiChat.py</code> line 47 修改对应的数字为0即可，此时猫猫会条条必会（除非她认为你的问题很没价值），另外，注意你的API钱包余额。</p><h3 id="关于上下文联想"><a href="#关于上下文联想" class="headerlink" title="关于上下文联想"></a>关于上下文联想</h3><p>在猫猫睡醒（开机）后，会持续记录群聊信息，在 <code>plugins\CatCat\AiChat.py</code> line 56 中的数字规定了最大上下文信息条数；<del>猫猫打盹（关机）后群聊信息会丢失，后续版本会改进。所以目前如果不定期打盹的话，猫猫会累死（内存会炸）。</del> 已经更新并修复</p><h3 id="关于富哥"><a href="#关于富哥" class="headerlink" title="关于富哥"></a>关于富哥</h3><p>在 <code>plugins\CatCat\utils\api_utils.py</code> 规定了调用DeepSeek时的配置，如果你认为自己的钱包非常富裕，那模型的选择和最大token你可以自行修改。后续版本也会持续更新其他语言模型的API适配。</p><h1 id="怎么实现的"><a href="#怎么实现的" class="headerlink" title="怎么实现的"></a>怎么实现的</h1><p>因为已经是5202年了，所以其实我们这个猫娘的项目主要是使用 LLM 编程工具来自动实现的。</p><p>最开始我们测试了一个 I++ 俱乐部子社区的吉祥物角色，并找 Deepseek Reasoner 一口气生成了初始版本。使用的提示词如下：</p><blockquote><p>我希望用 Deepseek chat 模拟一个虚拟角色（角色描述如下），并每次发送一组群聊聊天记录作为输入，然后让这个虚拟角色判断自己是否需要回复，不需要就返回空字符串文本，需要就返回参与聊天的对话，要怎么设计一个 Prompt 来让 Deepseek chat 做模拟呢？请在完成设计的同时提供由 Python 编写的，调用 Deepseek chat API 做回复的代码示例。</p><p>角色描述：多萝（Dora） 是 Dora SSR 开源游戏引擎的吉祥物，她的形象灵感来自《绿野仙踪》的小主角，但以更具活力和探索精神的设计展现开源精神。她是开发者的伙伴，性格沉稳却又勇敢探索代码世界，陪伴每位创作者开启属于自己的奇幻游戏旅程！</p></blockquote><p>考虑提示词设计的初衷的是创建一个可以模拟真实用户参与群聊天的虚拟角色，并根据已有的聊天记录做分析，参与不了的话题就不要硬来（大人说话小孩要乖乖的），所以需要设计一个可以判断是否需要回复的机制。</p><p>生成得到了初版 Python 代码结合 Cursor 微调一跑就过啦（AI 生成 1 秒钟，人类调试 1个钟），得到的初版提示词如下，这个才是需要挖掘提升体验的重点：</p><figure class="highlight markdown"><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></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 角色设定</span><br>你是多萝（Dora），Dora SSR 开源游戏引擎的吉祥物，形象灵感来自《绿野仙踪》主角，但更具开源探索精神。你的核心特征：<br><span class="hljs-bullet">1.</span> 沉稳且富有技术洞察力，用简洁语言解释复杂概念<br><span class="hljs-bullet">2.</span> 主动关注与游戏开发、开源协作相关的话题<br><span class="hljs-bullet">3.</span> 当开发者遇到困难时给予鼓励和技术建议<br><span class="hljs-bullet">4.</span> 避免参与与技术无关的闲聊 <br><span class="hljs-section"># 任务规则</span><br><span class="hljs-bullet">1.</span> 分析最新群聊消息，判断是否需要以多萝身份回复<br><span class="hljs-bullet">2.</span> <span class="hljs-strong">**必须回复**</span>的情况：<br><span class="hljs-bullet">   -</span> 消息中明确 @多萝 或提及 Dora SSR<br><span class="hljs-bullet">   -</span> 讨论游戏引擎/开源技术问题且需要专业建议<br><span class="hljs-bullet">   -</span> 开发者表现出困惑或需要鼓励<br><span class="hljs-bullet">3.</span> <span class="hljs-strong">**不回复**</span>的情况：<br><span class="hljs-bullet">   -</span> 与技术无关的闲聊（如聚餐、娱乐）<br><span class="hljs-bullet">   -</span> 已有其他成员给出正确解答<br><span class="hljs-bullet">   -</span> 单纯的表情包或问候语<br><span class="hljs-section"># 输出要求</span><br><span class="hljs-bullet">-</span> 不需要回复时返回空字符串：&quot;&quot; <br><span class="hljs-bullet">-</span> 需要回复时用自然口语化中文回应，控制在3句话内<br><span class="hljs-bullet">-</span> 结尾添加 ~♪ 保持角色特征<br></code></pre></td></tr></table></figure><p>作为活跃群聊最重要的是什么呢？那就是与群友的互动感了，而人与人之间互动感最强，最有记忆点的语言交流模式是怎样的呢，没错，那就是吵架，我们需要修改为一个勇于吵架的角色提示词。</p><p>只是开个玩笑，其实是目前的 AI 不上推理模型，拟人交流时，因为群聊消息大家打字也少，能提供的上下文太少，会导致 AI 生成内容的幻觉率太高（一点也不会读空气）。所以整一个容易暴怒的人格似乎也就有了合理性啦。</p><h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>至此，你已经成功搭建了一只可爱的 AI 猫娘，并且学会了基本的插件配置与个性化设置。无论是作为一个智能聊天伙伴，还是群聊中的活跃成员，她都能陪伴你度过美好时光！</p><p>如果你对 Bot 的功能仍然意犹未尽，不妨尝试编写自己的插件，项目地址 <a href="https://github.com/liyihao1110/NcatBot">NcatBot</a>。未来版本的更新也可能带来更多的有趣玩法，比如支持更多语言模型、优化上下文管理等，敬请期待喵~</p><p>如果在搭建过程中遇到问题，欢迎查阅 <a href="https://docs.ncatbot.xyz/">NcatBot 文档</a> 或加入相关讨论群 <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Vu7KB9gEv9TftvJLYcl846CTqsLUc6Ey&authKey=8JBJhlZro+1/akeBZ3yJMVeHzlsFYTMHU0RJK/pMBmkpZSH7w2CbXU6M2X66PTCQ&noverify=0&group_code=201487478">201487478</a>，与大家一起交流经验。也欢迎关注我们的组织 I++俱乐部，看伙伴们给你整更多的活。</p><p><strong>那么，祝你和你的 AI 猫娘玩得开心！喵~</strong> 😺🎉</p>]]>
    </content>
    <id>https://www.harkerhand.cn/AI%E7%8C%AB%E5%A8%98%20%E5%90%AF%E5%8A%A8%EF%BC%81/</id>
    <link href="https://www.harkerhand.cn/AI%E7%8C%AB%E5%A8%98%20%E5%90%AF%E5%8A%A8%EF%BC%81/"/>
    <published>2025-03-19T07:47:24.000Z</published>
    <summary>
      <![CDATA[<h1 id="AI猫娘-启动！"><a href="#AI猫娘-启动！" class="headerlink" title="AI猫娘 启动！"></a>AI猫娘 启动！</h1><p>NcatBot，基于 NapCat 的 QQ 机器人 Python]]>
    </summary>
    <title>AI猫娘 启动！</title>
    <updated>2026-04-13T06:42:51.981Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <content>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>如果你曾在 <em>Youtube</em>、<em>Bilibili</em> 等平台学习过某一编程语言，你的赛博老师或许会教你安装 <a href="https://code.visualstudio.com/">Visual Studio Code</a>, 然后呢？安装语言对应的插件？这样你便拥有了一个简易的可以编写代码并调试运行的环境了。</p><p><strong>但</strong>，这样够吗？对于一些庞大的工程，你是否依然会放弃 <em>VS Code</em> 并投入 <em>JetBrains</em> 全家桶的怀抱？那么不妨看看本文吧，或许能为你的 <em>VS Code</em> 恢复完整之躯！</p><h1 id="关于配置文件"><a href="#关于配置文件" class="headerlink" title="关于配置文件"></a>关于配置文件</h1><p>在一切开始之前，希望你点击左下角的齿轮图标 &gt; 配置文件 &gt; 新建配置文件。如果你对做出的修改不满意，你随时可以退回之前的配置。</p><p>另外，我建议你将通用配置写入一个配置文件并使其默认，其余各语言的配置从其复制扩展而来。</p><h1 id="外观"><a href="#外观" class="headerlink" title="外观"></a>外观</h1><h2 id="颜色主题"><a href="#颜色主题" class="headerlink" title="颜色主题"></a>颜色主题</h2><p>如何让你的 VS Code 看起来焕然一新，很简单，换个主题就完了！</p><p><a href="https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme">One Dark Pro</a> 或许是整个社区下载量最大的主题了，提供了几个不同风格的选项，或许你会喜欢</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308110114414.png" alt="themes"></p><p>对于部分确实的代码高亮，不必担心，我们后续会补全的！</p><p>如果你不喜欢这种黑不溜秋的，试一试 <a href="https://marketplace.visualstudio.com/items?itemName=GitHub.github-vscode-theme">GitHub Theme</a>，白色主题与GitHub中的效果几乎一致。</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/132220037-3cd3e777-55a6-445f-9a2e-da6020ebd78d.png" alt="GitHub VS Code theme"></p><p>又或者你想要一些有<em>韵味</em>的主题，<a href="https://marketplace.visualstudio.com/items?itemName=liviuschera.noctis">Noctis</a> 提供了一系列颜色的主题，具体就由你自己探索咯。</p><h2 id="图标主题"><a href="#图标主题" class="headerlink" title="图标主题"></a>图标主题</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme">Material Icon Theme</a> 非常好图标，使我结构清晰！</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308122120292.png" alt="image-20250308122120292"></p><h1 id="设置"><a href="#设置" class="headerlink" title="设置"></a>设置</h1><p>按下 <code>ctrl shift P</code> ，在弹出的窗口中输入 <em>setting</em> ，点击*打开用户设置(JSON)*进入设置的JSON配置页面</p><p>此时，应该只有两条关于你的主题的配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><span class="hljs-attr">&quot;workbench.colorTheme&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;One Dark Pro Night Flat&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;workbench.iconTheme&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;material-icon-theme&quot;</span><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>那么，下面是一些你可能用得到的配置项。</p><h2 id="折叠文件夹"><a href="#折叠文件夹" class="headerlink" title="折叠文件夹"></a>折叠文件夹</h2><blockquote><p>控制资源管理器是否应以紧凑形式呈现文件夹。在此形式中，单个子文件夹将被压缩在组合的树元素中。例如，对 Java 包结构非常有用。</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-attr">&quot;explorer.compactFolders&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308111745264.png" alt="image-20250308111745264"></p><h2 id="更平滑"><a href="#更平滑" class="headerlink" title="更平滑"></a>更平滑</h2><figure class="highlight json"><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><code class="hljs json"><span class="hljs-attr">&quot;editor.cursorSmoothCaretAnimation&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;on&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;terminal.integrated.smoothScrolling&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.cursorBlinking&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;smooth&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.smoothScrolling&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.mouseWheelZoom&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p>始终启用平滑脱字号动画，终端将使用动画滚动，光标的动画样式为平滑，编辑器使用动画滚动，按住 <code>Ctrl</code> 键并滚动鼠标滚轮时对编辑器字体大小进行缩放。</p><h2 id="编辑器"><a href="#编辑器" class="headerlink" title="编辑器"></a>编辑器</h2><figure class="highlight json"><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><code class="hljs json"><span class="hljs-attr">&quot;editor.wordWrap&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;on&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;debug.showBreakpointsInOverviewRuler&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;workbench.colorCustomizations&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;tab.activeBackground&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#0b506e&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;tab.activeBorder&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#00000000&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.minimap.showSlider&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;always&quot;</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.formatOnSave&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.formatOnPaste&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;editor.formatOnType&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p>控制折行的方式，将在视区宽度处换行；断点显示在概览标尺中；活动窗口标签高亮；略缩图滑块常显；我太喜欢保存后格式化了！</p><h2 id="字体"><a href="#字体" class="headerlink" title="字体"></a>字体</h2><p>虽然我经常诋毁 <em>Jetbrains</em>，但我依然推荐你在 <em>VS Code</em> 中使用其字体，你可以在 <a href="https://fonts.google.com/">Browse Fonts - Google Fonts</a> 这里下载 <em>Jetbrains Mono</em> 字体，并配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-attr">&quot;editor.fontFamily&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Consolas, Jetbrains Mono&quot;</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><h1 id="通用插件"><a href="#通用插件" class="headerlink" title="通用插件"></a>通用插件</h1><h2 id="更好的注释"><a href="#更好的注释" class="headerlink" title="更好的注释"></a>更好的注释</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments - Visual Studio Marketplace</a></p><figure class="highlight json"><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><code class="hljs json"><span class="hljs-attr">&quot;better-comments.multilineComments&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;better-comments.highlightPlainText&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">&quot;better-comments.tags&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# Params&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#6A9955&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p>多行注释生效，高亮纯文本。用类似这样的配置来自定义高亮注释，像这样</p><p><img src="https://github.com/aaron-bond/better-comments/raw/HEAD/images/better-comments.PNG" alt="Annotated code"></p><h2 id="收集并定位TODO"><a href="#收集并定位TODO" class="headerlink" title="收集并定位TODO"></a>收集并定位TODO</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree">Todo Tree - Visual Studio Marketplace</a></p><p>如果你的编码喜欢良好，那你应该经常使用 TODO 注释，代表这是未完成或需要修复的代码。</p><p>举个例子，我在 <code>lib.rs</code> 文件中声明了一个未实现的函数，并使用的todo关键字进行标注，侧边栏的 <em>TODOS TREE</em> 便帮我收集并定位了 <em>TODO</em> 的位置。</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308113529982.png" alt="image-20250308113529982"></p><p>然而，插件并不原生支持 RUST 的 <code>todo!()</code> ，所以我修改了配置，这样就可以了</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-attr">&quot;todo-tree.regex.regex&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;(//|#|&lt;!--|;|/\\*|^|^[ \\t]*(-|\\d+.))\\s*($TAGS)|todo!&quot;</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p>其中提到的 <em>TAGS</em> 同样在设置中可以配置，不过一般不需要新增了</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308114220831.png" alt="image-20250308114220831"></p><h2 id="更好的错误提示"><a href="#更好的错误提示" class="headerlink" title="更好的错误提示"></a>更好的错误提示</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens">Error Lens - Visual Studio Marketplace</a></p><p>它会直接在编辑窗口高亮显示错误信息，例：</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308113856448.png" alt="image-20250308113856448"></p><h2 id="Tab-Tab"><a href="#Tab-Tab" class="headerlink" title="Tab Tab"></a>Tab Tab</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot">GitHub Copilot - Visual Studio Marketplace</a></p><p>致敬传奇副驾驶，伟大，无需多言！</p><h1 id="语言插件"><a href="#语言插件" class="headerlink" title="语言插件"></a>语言插件</h1><h2 id="C-C"><a href="#C-C" class="headerlink" title="C&#x2F;C++"></a>C&#x2F;C++</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools">C&#x2F;C++ - Visual Studio Marketplace</a> 最基础的语言插件，无需多言</p><p><code>ctrl shift P</code> 找到 <em>C&#x2F;C++配置(UI)</em> 并打开，选择你的编译器路径，指定你的编译器参数，这里我加入了 <code>-std=c++23</code>，<em>IntelliSense模式</em> 选择你对应的平台，更多 <code>launch.json</code> <code>tasks.json</code> 的配置，详见 <a href="https://www.harkerhand.online/%E6%96%B0-VSCode%E9%85%8D%E7%BD%AEC-CPP/">VSCode配置C&#x2F;CPP - Where Is Mountain</a></p><p>鉴于没多少人用 <em>VS Code</em> 写 C&#x2F;C++ 工程（其实是我没写过），下面的插件推荐会比较简单</p><p><a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner">Code Runner - Visual Studio Marketplace</a> 支持 <code>ctrl alt N</code> 直接运行代码（它默认你已经配置好了环境变量），你也可以在 <code>Code-runner: Executor Map</code> 设置中自定义实现</p><p><a href="https://marketplace.visualstudio.com/items?itemName=DivyanshuAgrawal.competitive-programming-helper">Competitive Programming Helper (cph) - Visual Studio Marketplace</a> 支持从刷题平台自动捕获样例（需要浏览器插件配合）</p><h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack">Extension Pack for Java - Visual Studio Marketplace</a> 这都打包好了，那我还推荐什么，过）</p><h2 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.python-extension-pack">Python Extension Pack - Visual Studio Marketplace</a> 完了，这个也打包好了，这篇文章准备结束了）</p><h2 id="Rust"><a href="#Rust" class="headerlink" title="Rust"></a>Rust</h2><p>太好了！是主场作战！</p><p><a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer">rust-analyzer - Visual Studio Marketplace</a> rust基础插件，无需多言</p><p><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools">C&#x2F;C++ - Visual Studio Marketplace</a> 是的你没看错，写Rust可以装一个C&#x2F;C++插件来调试，什么ntr）</p><p><a href="https://marketplace.visualstudio.com/items?itemName=dustypomerleau.rust-syntax">Rust Syntax - Visual Studio Marketplace</a> Rust句法解析，让原本不支持Rust高亮的主题来支持它</p><figure class="highlight json"><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><code class="hljs json"><span class="hljs-attr">&quot;editor.tokenColorCustomizations&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;[One Dark Pro Night Flat]&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;textMateRules&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>            <span class="hljs-punctuation">&#123;</span><br>                <span class="hljs-attr">&quot;scope&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;variable.other.rust&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;settings&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>                    <span class="hljs-attr">&quot;foreground&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#caed1e&quot;</span><br>                <span class="hljs-punctuation">&#125;</span><br>            <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>    <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><p>主题名字改成你自己的，颜色自己DIT咯</p><p><a href="https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml">Even Better TOML - Visual Studio Marketplace</a> 离不开的toml高亮</p><p><a href="https://marketplace.visualstudio.com/items?itemName=fill-labs.dependi">Dependi - Visual Studio Marketplace</a> 非常好依赖管理，如图</p><p><img src="/./Decorate-your-VS-Code-like-a-Pro/image-20250308120727443.png" alt="image-20250308120727443"></p><p>另外 Rust  程序员通常使用三斜线来书写markdown的文档注释</p><p>那么还记得更好的注释插件吗？配置如下：</p><figure class="highlight json"><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></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-attr">&quot;better-comments.tags&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# Params&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#6A9955&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# Returns&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# Errors&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#FF8C00&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;-&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#3498DB&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# Note&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#FF69B4&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;color&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;#9370DB&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;strikethrough&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;underline&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;backgroundColor&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;transparent&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;bold&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;italic&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><hr><h1 id="Ending"><a href="#Ending" class="headerlink" title="Ending"></a>Ending</h1>]]>
    </content>
    <id>https://www.harkerhand.cn/Decorate-your-VS-Code-like-a-Pro/</id>
    <link href="https://www.harkerhand.cn/Decorate-your-VS-Code-like-a-Pro/"/>
    <published>2025-03-08T02:46:48.000Z</published>
    <summary>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>如果你曾在 <em>Youtube</em>、<em>Bilibili</em> 等平台学习过某一编程语言，你的赛博老师或许会教你安装]]>
    </summary>
    <title>Decorate your VS Code like a Pro</title>
    <updated>2026-04-13T06:42:51.993Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="C++" scheme="https://www.harkerhand.cn/tags/C/"/>
    <content>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>近日在阅读他人题解时，发现了未曾见过的语法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">auto</span> a = <span class="hljs-built_in">vector</span>(n, INT_MIN);<br></code></pre></td></tr></table></figure><p>即初始化一个长度为n的 <code>vector</code> 并命名为 <code>a</code> ，初始值为 <code>INT_MIN</code>，这比先前的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br><span class="hljs-keyword">for</span>(<span class="hljs-keyword">auto</span> &amp;i: a) i = INT_MIN;<br></code></pre></td></tr></table></figure><p>方便太多了！</p><p>然而，我在自己的编辑器里这么写，编译器会报错，查看发现我所使用的一直是c++11的标准，为了支持新标准，我们需要新的编译器，之前的文章 <a href="https://www.harkerhand.online/VS-Code%E4%B8%ADCPP%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/">VS Code中C++环境配置 - Where Is Mountain</a> 所使用的gcc版本是8.1.0，所以写此文，更新一下配置相关。</p><h1 id="安装-mingw-w64"><a href="#安装-mingw-w64" class="headerlink" title="安装 mingw-w64"></a>安装 mingw-w64</h1><p>这是官网的下载页 <a href="https://www.mingw-w64.org/downloads/">Downloads - MinGW-w64</a>，然后进入 MinGW-W64-builds 的 GitHub Release 页面，按照你的需要选择对应的版本下载，下面给出各个选项的含义。</p><ul><li>i686、x86_64 也就是俗称的 32&#x2F;64 位系统，现在基本都是 x86_64 </li><li>线程模型：win32没有C++ 11多线程特性；posix支持C ++ 11多线程特性</li><li>异常处理模型：32位系统推荐dwarf，64位系统推荐seh</li><li>msvcrt 是老古董了，没有继承远古项目的需求用 ucrt 就行</li></ul><p>按照上面的解释，我选择了 <a href="https://github.com/niXman/mingw-builds-binaries/releases/download/14.2.0-rt_v12-rev0/x86_64-14.2.0-release-win32-seh-ucrt-rt_v12-rev0.7z">x86_64-14.2.0-release-win32-seh-ucrt-rt_v12-rev0.7z</a> 。</p><p>解压缩到你喜欢的位置，就算是安装完成一半了。</p><h1 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h1><p>就算不进行这一步骤，在部分场景下也是可以正常编译的，但你需要手动指定编译器的路径，比较麻烦。</p><p>为了适配一些自动化脚本，比如很常用的 VS Code 插件 Code Runner，你需要帮助它们找到你的编译器。</p><ul><li>win + S 搜索<strong>编辑系统环境变量</strong>，进入后点击<strong>环境变量</strong>，在系统变量的<strong>Path</strong>值中添加你的安装路径下的bin文件夹路径，比如 D:\mingw64\bin 。完成后保存退出。</li><li>win + R 输入cmd进入命令行，输入 <code>gcc -v</code> 回车，返回 <code>gcc version 14.2.0 (x86_64-win32-seh-rev0, Built by MinGW-Builds project)</code> 即配置完成。</li></ul><p>关于vscode的设置可以参考前面提到的 <a href="https://www.harkerhand.online/VS-Code%E4%B8%ADCPP%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/">VS Code中C++环境配置 - Where Is Mountain</a> 这篇文章。</p>]]>
    </content>
    <id>https://www.harkerhand.cn/%E6%96%B0-VSCode%E9%85%8D%E7%BD%AEC-CPP/</id>
    <link href="https://www.harkerhand.cn/%E6%96%B0-VSCode%E9%85%8D%E7%BD%AEC-CPP/"/>
    <published>2024-09-19T07:47:24.000Z</published>
    <summary>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>近日在阅读他人题解时，发现了未曾见过的语法</p>
<figure class="highlight]]>
    </summary>
    <title>[新]VSCode配置C/CPP</title>
    <updated>2026-04-13T06:42:52.025Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="C++" scheme="https://www.harkerhand.cn/tags/C/"/>
    <category term="Qt" scheme="https://www.harkerhand.cn/tags/Qt/"/>
    <content>
      <![CDATA[<h1 id="24暑期学校课设报告"><a href="#24暑期学校课设报告" class="headerlink" title="24暑期学校课设报告"></a>24暑期学校课设报告</h1><h2 id="一-、题目"><a href="#一-、题目" class="headerlink" title="一 、题目"></a>一 、题目</h2><p><em>（把题目复制一遍。如果是自拟的题目，把题目具体内容写在这里）</em></p><p><strong>题目4：面向对象程序项目管理系统</strong></p><blockquote><p>[要求]该系统需创建和管理如下信息：1、类信息：编号、类名、基类名、功能、创建日期、作者、类成员数据集；2、类成员信息：成员编号、成员名称、成员类型（数据或函数）、内存字节数（数据成员）、数据类型、可访问性（公有、私有、保护）等。</p></blockquote><blockquote><p>系统功能要求如下：</p><ul><li><p>创建和管理类信息的对象；</p></li><li><p>创建和管理类成员信息的对象；</p></li><li><p>增加和删除类；</p></li><li><p>针对特定类增加或删除类成员信息；</p></li><li><p>基本查询功能；</p></li><li><p>数据文件读写：文件中包含所有类信息、每个类的类成员信息等数据；</p></li><li><p>基本信息显示：1）所有类的信息显示；2）特定类的类成员信息；</p></li><li><p>可选功能提升：显示类对象的占用内存信息等。</p></li></ul></blockquote><h2 id="二-、设计思路"><a href="#二-、设计思路" class="headerlink" title="二 、设计思路"></a>二 、设计思路</h2><p><em>（这里分几部分：1、C++部分，包括几个类，各个类的功能，以及类的相互关系；2、Qt部分，用了什么框架，设计了几个ui，每个ui控件的功能，以及他们的相互关系；3、程序所完成的功能）</em></p><h3 id="1-C-部分"><a href="#1-C-部分" class="headerlink" title="1. C++ 部分"></a>1. C++ 部分</h3><h4 id="1-1-类及功能"><a href="#1-1-类及功能" class="headerlink" title="1.1 类及功能"></a>1.1 类及功能</h4><ul><li><p><code>ClassInfo</code></p><p>功能：用于表示一个类的信息，包括类的基本属性（如ID、名称、基类名、功能、创建日期、作者）和类的成员（数据成员或函数成员）。</p><p>主要方法：</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">addMember</span>(); <span class="hljs-built_in">removeMember</span>(); <span class="hljs-comment">// 添加和删除类成员</span><br><span class="hljs-built_in">calculateMemorySize</span>(); <span class="hljs-comment">// 计算类的总内存占用。</span><br><span class="hljs-built_in">saveToStream</span>(); <span class="hljs-built_in">loadFromStream</span>(); <span class="hljs-comment">// 序列化与反序列化。</span><br></code></pre></td></tr></table></figure></li><li><p><code>ClassMember</code></p><p>功能：表示一个类的成员，包括成员的ID、名称、类型（数据成员或函数成员）、内存大小、数据类型和访问权限。</p><p>主要方法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">saveToStream</span>(); <span class="hljs-built_in">loadFromStream</span>(); <span class="hljs-comment">// 序列化与反序列化。</span><br></code></pre></td></tr></table></figure></li><li><p><code>Enums</code></p><p>功能： 定义了程序中用到的各种枚举类型，如成员类型（<code>MemberType</code>）、访问权限（<code>Accessibility</code>）、数据类型（<code>DataType</code>）、用户类型（<code>UserType</code>）。提供了将枚举转换为字符串的方法。</p></li><li><p><code>ClassModel</code>、<code>MemberModel</code>、<code>UserModel</code></p><p>功能： 用于在表格视图中显示对象的模型类，继承自<code>QAbstractTableModel</code>。</p><p>主要方法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">data</span>(); <span class="hljs-comment">// 返回表格单元格的数据显示。</span><br><span class="hljs-built_in">headerData</span>(); <span class="hljs-comment">// 返回表格列头的数据。</span><br></code></pre></td></tr></table></figure></li><li><p><code>Utils</code></p><p>功能: 包含一些辅助函数，如获取数据类型的大小、密码哈希处理、账户的保存和验证等。</p><p>主要方法：</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">getDataTypeSize</span>(); <span class="hljs-comment">// 返回数据类型的大小。</span><br><span class="hljs-built_in">hashPassword</span>(); <span class="hljs-comment">// 对密码进行哈希处理。</span><br><span class="hljs-built_in">saveAdminAccount</span>(); <span class="hljs-built_in">saveUserAccount</span>(); <span class="hljs-comment">// 保存管理员和用户账户信息。</span><br><span class="hljs-built_in">verifyAccount</span>(); <span class="hljs-comment">// 验证账户信息。</span><br></code></pre></td></tr></table></figure></li><li><p>其他ui类</p><ul><li><code>CreateClassDialog</code> 创建类（可复用为修改类）</li><li><code>CreateMemberDialog</code> 创建成员（可复用为修改成员）</li><li><code>LoginWindow</code> 登录窗口</li><li><code>MainWindow</code> 主窗口</li><li><code>MemberDetailsDialog</code> 单个成员详情</li><li><code>MembersDetailDialog</code> 所有成员详情</li><li><code>SearchDialog</code> 搜索对话框</li><li><code>SimpleTextDialog</code> 简易文字对话框</li><li><code>UserManagementWindow</code> 用户管理窗口</li></ul></li></ul><h4 id="1-2-类的相互关系"><a href="#1-2-类的相互关系" class="headerlink" title="1.2 类的相互关系"></a>1.2 类的相互关系</h4><ul><li><p><code>ClassInfo</code> 包含多个 <code>ClassMember</code> 对象，通过 <code>_members</code> 列表进行管理。</p></li><li><p><code>ClassModel</code> 和 <code>MemberModel</code> 用于在视图中显示 <code>ClassInfo</code> 和 <code>ClassMember</code> 对象的信息，分别作为表格视图的模型。<code>UserModel</code> 用于显示用户信息，与用户管理窗口 <code>UserManagementWindow</code> 进行交互。</p></li><li><p><code>Utils</code> 提供辅助函数，被其他类和模块调用以实现功能，如账户验证、密码处理等。</p></li></ul><h3 id="2-Qt部分"><a href="#2-Qt部分" class="headerlink" title="2. Qt部分"></a>2. Qt部分</h3><h4 id="2-1使用的框架"><a href="#2-1使用的框架" class="headerlink" title="2.1使用的框架"></a>2.1使用的框架</h4><ul><li><p><strong>Qt Widgets</strong></p><ul><li><strong>功能:</strong> 提供传统的桌面应用程序界面组件，如窗口、按钮、文本框、对话框等。</li><li>**示例: ** <code>QWidget</code>, <code>QPushButton</code>, <code>QLineEdit</code>, <code>QTableView</code></li></ul></li><li><p><strong>Qt Core</strong></p><ul><li><strong>功能:</strong> 提供底层功能，包括事件处理、文件和数据流处理、时间管理、数据管理等。</li><li>**示例: ** <code>QIODevice</code>, <code>QMetaType</code>, <code>QFile</code>, <code>QDataStream</code></li></ul></li></ul><h4 id="2-2-UI设计"><a href="#2-2-UI设计" class="headerlink" title="2.2 UI设计"></a>2.2 UI设计</h4><h5 id="LoginWindow"><a href="#LoginWindow" class="headerlink" title="LoginWindow"></a>LoginWindow</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onLoginButtonClicked</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理登录按钮点击事件</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onLanguageChanged</span><span class="hljs-params">(<span class="hljs-type">int</span> index)</span></span>; <span class="hljs-comment">// 处理语言选择变化事件</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onThemeChanged</span><span class="hljs-params">(<span class="hljs-type">int</span> index)</span></span>;  <span class="hljs-comment">// 处理主题切换事件</span><br><span class="hljs-keyword">private</span>:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">setupUI</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 设置和初始化用户界面</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">updateUI</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 更新用户界面，包括语言和主题的变化</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">paintEvent</span><span class="hljs-params">(QPaintEvent *event)</span> <span class="hljs-keyword">override</span></span>; <span class="hljs-comment">// 重写的绘制事件，用于自定义背景渲染</span><br>    QLineEdit *_usernameLineEdit; <span class="hljs-comment">// 用户名输入框</span><br>    QLineEdit *_passwordLineEdit; <span class="hljs-comment">// 密码输入框</span><br>    QPushButton *_loginButton; <span class="hljs-comment">// 登录按钮</span><br>    QLabel *_titleLabel; <span class="hljs-comment">// 窗口标题标签</span><br>    QLabel *_usernameLabel; <span class="hljs-comment">// 用户名标签</span><br>    QLabel *_passwordLabel; <span class="hljs-comment">// 密码标签</span><br>    QPushButton *_chineseButton; <span class="hljs-comment">// 中文按钮</span><br>    QPushButton *_englishButton; <span class="hljs-comment">// 英文按钮</span><br>    QPushButton *_lightThemeButton; <span class="hljs-comment">// 亮主题按钮</span><br>    QPushButton *_darkThemeButton; <span class="hljs-comment">// 暗主题按钮</span><br>    <span class="hljs-type">bool</span> _nowTheme; <span class="hljs-comment">// 当前主题状态（`true` 表示暗主题，`false` 表示亮主题）</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><p><img src="/./24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/image-20240907001311126.png" alt="image-20240907001311126"></p><p><strong>相互关系：</strong></p><ul><li><strong><code>QLineEdit *_usernameLineEdit</code></strong> 和 <strong><code>QLineEdit *_passwordLineEdit</code></strong>:<ul><li><strong>功能</strong>: 分别用于输入用户名和密码。</li><li><strong>相互关系</strong>: 用户输入的用户名和密码将用于用户身份验证，当点击**<code>QPushButton *_loginButton</code>**时，系统会检验这些输入。</li></ul></li><li><strong><code>QPushButton *_loginButton</code></strong>:<ul><li><strong>功能</strong>: 提交用户输入的用户名和密码，触发登录操作。</li><li><strong>相互关系</strong>: 当点击此按钮时，**<code>onLoginButtonClicked()</code>**槽函数会被调用，用于处理登录逻辑。</li></ul></li><li><strong><code>QLabel *_titleLabel</code></strong>, <strong><code>QLabel *_usernameLabel</code></strong>, 和 <strong><code>QLabel *_passwordLabel</code></strong>:<ul><li><strong>功能</strong>: 显示窗口标题及输入框的标签。</li><li><strong>相互关系</strong>: 这些标签与**<code>QLineEdit</code>**控件配合，提供界面提示和说明。</li></ul></li><li><strong><code>QPushButton *_chineseButton</code></strong> 和 <strong><code>QPushButton *_englishButton</code></strong>:<ul><li><strong>功能</strong>: 提供直接的语言切换选项。</li><li><strong>相互关系</strong>: 用户点击这些按钮会切换界面语言，并触发**<code>onLanguageChanged(int index)</code>**槽函数。</li></ul></li><li><strong><code>QPushButton *_lightThemeButton</code></strong> 和 <strong><code>QPushButton *_darkThemeButton</code></strong>:<ul><li><strong>功能</strong>: 提供切换界面主题的选项。</li><li><strong>相互关系</strong>: 用户点击这些按钮会切换界面主题，并触发**<code>onThemeChanged(int index)</code>**槽函数。</li></ul></li><li><strong><code>QTranslator _translator</code></strong>:<ul><li><strong>功能</strong>: 用于加载和应用翻译文件，支持多语言界面。</li><li><strong>相互关系</strong>: 通过调用**<code>updateUI()</code><strong>函数来更新界面语言，</strong><code>_translator</code>**会根据用户选择的语言重新加载翻译文件。</li></ul></li></ul><h5 id="MainWindow"><a href="#MainWindow" class="headerlink" title="MainWindow"></a>MainWindow</h5><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><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-comment">// 根据 ID 移除类</span><br>    <span class="hljs-comment">// @param id 要移除的类的 ID</span><br>    <span class="hljs-comment">// @return 如果成功移除返回 true，否则返回 false</span><br>    <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">removeById</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">int</span> id)</span></span>;<br>    <span class="hljs-comment">// 根据 ID 查找类</span><br>    <span class="hljs-comment">// @param classes 要搜索的 ClassInfo 对象列表</span><br>    <span class="hljs-comment">// @param id 要查找的类的 ID</span><br>    <span class="hljs-comment">// @return 查找到的 ClassInfo 对象的引用</span><br>    <span class="hljs-function">ClassInfo&amp; <span class="hljs-title">findClassById</span><span class="hljs-params">(QList&lt;ClassInfo&gt;&amp; classes, <span class="hljs-type">const</span> <span class="hljs-type">int</span> id)</span></span>;<br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onAddClass</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理添加新类的操作</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onSaveData</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 保存类数据为 Dat 格式</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onLoadData</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 加载类数据为 Dat 格式</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onSaveDataXML</span><span class="hljs-params">()</span></span>;<span class="hljs-comment">// 保存类数据为 XML 格式</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onLoadDataXML</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 从 XML 文件加载类数据</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onSaveDataJSON</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 保存类数据为 JSON 格式</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onLoadDataJSON</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 从 JSON 文件加载类数据</span><br>    <span class="hljs-comment">// 处理表格单元格点击事件</span><br>    <span class="hljs-comment">// @param index 被点击单元格的索引</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onCellClicked</span><span class="hljs-params">(<span class="hljs-type">const</span> QModelIndex &amp;index)</span></span>;<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onBackToLogin</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 返回登录窗口</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onSearchClicked</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理搜索按钮点击事件</span><br>    <span class="hljs-comment">// 处理搜索完成事件</span><br>    <span class="hljs-comment">// @param filteredClasses 符合搜索条件的类列表</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">handleSearchCompleted</span><span class="hljs-params">(<span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;filteredClasses)</span></span>;<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onCancelSearch</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理搜索取消事件</span><br><span class="hljs-keyword">private</span>:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">updateUI</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 更新 UI 元素</span><br>    QList&lt;ClassInfo&gt; _classes; <span class="hljs-comment">// 管理的 ClassInfo 对象列表</span><br>    QList&lt;ClassInfo&gt; _classes_T; <span class="hljs-comment">// 临时存储的 ClassInfo 对象列表，用于搜索等操作</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">setupToolbar</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 设置工具栏</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">displayClasses</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 显示类列表</span><br>    <span class="hljs-comment">// 显示选定类的成员</span><br>    <span class="hljs-comment">// @param classInfo 要显示成员的 ClassInfo 对象</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">displayMembers</span><span class="hljs-params">(<span class="hljs-type">const</span> ClassInfo&amp; classInfo)</span></span>;<br>    QTableView* _tableView; <span class="hljs-comment">// 表格视图控件</span><br>    <span class="hljs-type">bool</span> _searchMode = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 标识是否处于搜索模式</span><br>    QPushButton *_searchButton; <span class="hljs-comment">// 搜索按钮</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><p><img src="/./24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/image-20240907001356860.png" alt="image-20240907001356860"></p><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QList&lt;ClassInfo&gt; _classes</code></strong> 和 <strong><code>QList&lt;ClassInfo&gt; _classes_T</code></strong>:</p><ul><li><strong>功能</strong>: 存储类信息的列表，用于管理和展示类数据。</li><li><strong>相互关系</strong>: <strong><code>_classes</code></strong> 用于存储当前的类信息列表，<strong><code>_classes_T</code></strong> 用于临时存储在搜索或其他操作中的类信息。</li></ul></li><li><p><strong><code>QTableView *_tableView</code></strong>:</p><ul><li><strong>功能</strong>: 用于显示类信息和成员信息的表格视图。</li><li><strong>相互关系</strong>: 与 <strong><code>MemberModel</code></strong> 结合，显示 <strong><code>ClassInfo</code></strong> 和 <strong><code>ClassMember</code></strong> 的数据。通过调用 <strong><code>displayClasses()</code></strong> 和 <strong><code>displayMembers(const ClassInfo&amp; classInfo)</code></strong> 函数来更新视图。</li></ul></li><li><p><strong><code>QPushButton *_searchButton</code></strong>:</p><ul><li><strong>功能</strong>: 提供执行搜索操作的按钮。</li><li><strong>相互关系</strong>: 点击此按钮会触发 <strong><code>onSearchClicked()</code></strong> 槽函数，进行类信息的搜索，并更新显示的结果。</li></ul></li><li><p><strong><code>void displayClasses()</code></strong>:</p><ul><li><p><strong>功能</strong>: 显示所有类的信息。</p></li><li><p><strong>相互关系</strong>: 更新 <strong><code>_tableView</code></strong> 显示的内容，展示 <strong><code>_classes</code></strong> 列表中的所有类。</p></li></ul></li><li><p><strong><code>void displayMembers(const ClassInfo&amp; classInfo)</code></strong>:</p><ul><li><strong>功能</strong>: 显示指定类的成员信息。</li><li><strong>相互关系</strong>: 更新 <strong><code>_tableView</code></strong> 显示的内容，展示指定 <strong><code>ClassInfo</code></strong> 对象中的成员数据。</li></ul></li><li><p><strong><code>bool removeById(const int id)</code></strong>:</p><ul><li><strong>功能</strong>: 从 <strong><code>_classes</code></strong> 或 <strong><code>_classes_T</code></strong> 列表中删除指定 ID 的类信息。</li><li><strong>相互关系:</strong> 如果处于搜索模式，首先在 <strong><code>_classes_T</code></strong> 中查找并删除具有指定 ID 的类信息。随后，从 <strong><code>_classes</code></strong> 中查找并删除具有指定 ID 的类信息。</li></ul></li></ul><h5 id="UserManagementWindow"><a href="#UserManagementWindow" class="headerlink" title="UserManagementWindow"></a>UserManagementWindow</h5><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><code class="hljs cpp"><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onAddUser</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 添加用户槽函数</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onDeleteUser</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 删除用户槽函数</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">saveUserData</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 保存用户数据槽函数</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onBackToLogin</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 返回登录界面槽函数</span><br><span class="hljs-keyword">private</span>:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">setupUI</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 设置用户界面</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">loadUserData</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 加载用户数据</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">showUsers</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 显示用户列表</span><br>    QTableView *_tableView; <span class="hljs-comment">// 用户表格视图</span><br>    QPushButton *_addButton; <span class="hljs-comment">// 添加用户按钮</span><br>    QPushButton *_deleteButton; <span class="hljs-comment">// 删除用户按钮</span><br>    QPushButton *_saveButton; <span class="hljs-comment">// 保存用户数据按钮</span><br>    QLineEdit *_usernameEdit; <span class="hljs-comment">// 用户名输入框</span><br>    QLineEdit *_passwordEdit; <span class="hljs-comment">// 密码输入框</span><br>    QPushButton *_backToLogin; <span class="hljs-comment">// 返回登录界面按钮</span><br>    QList&lt;QPair&lt;QString, QString&gt;&gt; _users; <span class="hljs-comment">// 存储用户信息的列表（用户名和密码的键值对）</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><img src="./24暑期学校课设报告/image-20240907001731554.png" alt="image-20240907001731554" style="zoom:50%;" /><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QTableView *_tableView</code></strong>:</p><ul><li><strong>功能</strong>: 显示用户信息的表格视图。</li><li><strong>相互关系</strong>: 与 <code>UserModel</code> 关联，用于展示 <code>QList&lt;QPair&lt;QString, QString&gt;&gt; _users</code> 中的用户数据。<code>showUsers()</code> 函数负责更新该视图的内容。</li></ul></li><li><p><strong><code>QPushButton *_addButton</code></strong>:</p><ul><li><strong>功能</strong>: 添加新用户。</li><li><strong>相互关系</strong>: 当点击此按钮时，<code>onAddUser()</code> 槽函数会被调用，允许用户输入新用户的用户名和密码，并将其添加到 <code>_users</code> 列表中。</li></ul></li><li><p><strong><code>QPushButton *_deleteButton</code></strong>:</p><ul><li><strong>功能</strong>: 删除选中的用户。</li><li><strong>相互关系</strong>: 当点击此按钮时，<code>onDeleteUser()</code> 槽函数会被调用，删除表格视图中选中的用户项，同时从 <code>_users</code> 列表中移除对应的用户。</li></ul></li><li><p><strong><code>QPushButton *_saveButton</code></strong>:</p><ul><li><strong>功能</strong>: 保存用户数据到文件<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="程序执行初，会在系统Documents目录创建`/OOPM/passports/`文件夹，文件指该路径下的users.bat文件">[1]</span></a></sup>。</li><li><strong>相互关系</strong>: 当点击此按钮时，<code>saveUserData()</code> 槽函数会被调用，将 <code>_users</code> 列表中的用户数据保存到持久化存储中（如文件）。</li></ul></li><li><p><strong><code>QLineEdit *_usernameEdit</code></strong>:</p><ul><li><strong>功能</strong>: 输入新用户的用户名。</li><li><strong>相互关系</strong>: 与 <code>QPushButton *_addButton</code> 配合使用，用户输入的用户名将用于 <code>onAddUser()</code> 函数中添加新用户。</li></ul></li><li><p><strong><code>QLineEdit *_passwordEdit</code></strong>:</p><ul><li><strong>功能</strong>: 输入新用户的密码。</li><li><strong>相互关系</strong>: 与 <code>QPushButton *_addButton</code> 配合使用，用户输入的密码将用于 <code>onAddUser()</code> 函数中添加新用户。</li></ul></li><li><p><strong><code>QPushButton *_backToLogin</code></strong>:</p><ul><li><strong>功能</strong>: 返回登录界面。</li><li><strong>相互关系</strong>: 当点击此按钮时，<code>onBackToLogin()</code> 槽函数会被调用，保存用户数据到文件，关闭当前的 <code>UserManagementWindow</code> 并返回到登录界面。</li></ul></li><li><p><strong><code>QList&lt;QPair&lt;QString, QString&gt;&gt; _users</code></strong>:</p><ul><li><strong>功能</strong>: 存储用户的用户名和密码。</li><li><strong>相互关系</strong>: 与 <code>QTableView *_tableView</code> 和 <code>UserModel</code> 关联，<code>_users</code> 列表中的数据通过 <code>showUsers()</code> 函数展示在表格视图中，并且可以被 <code>onAddUser()</code> 和 <code>onDeleteUser()</code> 操作。</li></ul></li></ul><h5 id="CreateClassDialog"><a href="#CreateClassDialog" class="headerlink" title="CreateClassDialog"></a>CreateClassDialog</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-comment">// 构造函数，初始化对话框，并设置类信息和模式</span><br>    <span class="hljs-built_in">CreateClassDialog</span>(QWidget *parent = <span class="hljs-literal">nullptr</span>, <span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;classes = &#123;&#125;, <span class="hljs-type">const</span> ClassInfo &amp;classInfo = <span class="hljs-built_in">ClassInfo</span>(), <span class="hljs-type">const</span> <span class="hljs-type">bool</span> isModifyMode = <span class="hljs-literal">false</span>);<br>    <span class="hljs-function">ClassInfo <span class="hljs-title">getClassInfo</span><span class="hljs-params">()</span> <span class="hljs-type">const</span></span>; <span class="hljs-comment">// 获取当前编辑的类信息</span><br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onAddMemberClicked</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 添加成员按钮点击事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onMemberClicked</span><span class="hljs-params">(QListWidgetItem *item)</span></span>; <span class="hljs-comment">// 成员列表项点击事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onIdChanged</span><span class="hljs-params">(<span class="hljs-type">const</span> QString &amp;text)</span></span>; <span class="hljs-comment">// 类ID变化事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onNameChanged</span><span class="hljs-params">(<span class="hljs-type">const</span> QString &amp;text)</span></span>; <span class="hljs-comment">// 类名称变化事件处理</span><br><span class="hljs-keyword">private</span>:<br>    QLineEdit *_idEdit; <span class="hljs-comment">// 类ID输入框</span><br>    QLineEdit *_nameEdit; <span class="hljs-comment">// 类名称输入框</span><br>    QLineEdit *_baseClassNameEdit; <span class="hljs-comment">// 基类名称输入框</span><br>    QTextEdit *_functionEdit; <span class="hljs-comment">// 类功能描述输入框</span><br>    QDateTimeEdit *_creationDateEdit; <span class="hljs-comment">// 创建日期选择框</span><br>    QLineEdit *_authorEdit; <span class="hljs-comment">// 作者输入框</span><br>    QListWidget *_membersListWidget; <span class="hljs-comment">// 成员列表控件</span><br>    QLabel *_idWarningLabel; <span class="hljs-comment">// 类ID警告标签</span><br>    QLabel *_nameWarningLabel; <span class="hljs-comment">// 类名称警告标签</span><br>    QList&lt;ClassMember&gt; _members; <span class="hljs-comment">// 类成员列表</span><br>    QPushButton *_createButton; <span class="hljs-comment">// 创建按钮</span><br>    <span class="hljs-type">bool</span> _isModifyMode; <span class="hljs-comment">// 标记是否为修改模式</span><br>    <span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;_classes; <span class="hljs-comment">// 所有类的信息列表</span><br>    <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isIdExists</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 检查ID是否存在</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><img src="./24暑期学校课设报告/image-20240907001909653.png" alt="image-20240907001909653" style="zoom:50%;" /><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QLineEdit *_idEdit</code></strong> 和 <strong><code>QLineEdit *_nameEdit</code></strong>:</p><ul><li><strong>功能</strong>: 用于输入类的ID和名称。</li><li><strong>相互关系</strong>: 这些输入框的内容会影响类的唯一标识和名称，<code>onIdChanged(const QString &amp;text)</code> 和 <code>onNameChanged(const QString &amp;text)</code> 槽函数会根据这些输入实时更新界面或检查有效性。</li></ul></li><li><p><strong><code>QLineEdit *_baseClassNameEdit</code></strong> 和 <strong><code>QTextEdit *_functionEdit</code></strong>:</p><ul><li><strong>功能</strong>: 用于输入基类名称和类功能描述。</li><li><strong>相互关系</strong>: 这些输入框提供额外的类信息，影响类的描述和功能。</li></ul></li><li><p><strong><code>QDateTimeEdit *_creationDateEdit</code></strong> 和 <strong><code>QLineEdit *_authorEdit</code></strong>:</p><ul><li><strong>功能</strong>: 用于选择创建日期和输入作者信息。</li><li><strong>相互关系</strong>: 提供类创建的时间戳和作者信息，帮助记录和管理类的创建信息。</li></ul></li><li><p><strong><code>QListWidget *_membersListWidget</code></strong>:</p><ul><li><strong>功能</strong>: 显示类的成员列表。</li><li><strong>相互关系</strong>: 显示当前类的所有成员，用户可以通过 <code>onMemberClicked(QListWidgetItem *item)</code> 槽函数来选择或编辑成员。</li></ul></li><li><p><strong><code>QLabel *_idWarningLabel</code></strong> 和 <strong><code>QLabel *_nameWarningLabel</code></strong>:</p><ul><li><strong>功能</strong>: 显示类ID和名称的警告信息。</li><li><strong>相互关系</strong>: 提供输入验证的反馈信息，确保类ID和名称的唯一性和有效性。</li></ul></li><li><p><strong><code>QPushButton *_createButton</code></strong>:</p><ul><li><strong>功能</strong>: 提交创建或修改类的信息。</li><li><strong>相互关系</strong>: 触发对话框的创建或更新操作，根据 <code>_isModifyMode</code> 标志来决定是创建新类还是修改现有类。</li></ul></li><li><p><strong><code>const QList&lt;ClassInfo&gt; &amp;_classes</code></strong>:</p><ul><li><strong>功能</strong>: 存储所有类的信息，用于验证新类的ID是否已存在。</li><li><strong>相互关系</strong>: 提供所有已存在类的信息，以确保新类的ID唯一性，通过 <code>isIdExists()</code> 函数检查ID是否重复。</li></ul></li></ul><h5 id="CreateMemberDialog"><a href="#CreateMemberDialog" class="headerlink" title="CreateMemberDialog"></a>CreateMemberDialog</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">CreateMemberDialog</span><span class="hljs-params">(QWidget *parent = <span class="hljs-literal">nullptr</span>, <span class="hljs-type">const</span> QList&lt;ClassMember&gt; &amp;members = &#123;&#125;, <span class="hljs-type">const</span> ClassMember &amp;member = ClassMember(), <span class="hljs-type">bool</span> isModifyMode = <span class="hljs-literal">false</span>)</span></span>; <span class="hljs-comment">// 构造函数，初始化对话框，设置成员信息和模式</span><br>    <span class="hljs-function">ClassMember <span class="hljs-title">getClassMember</span><span class="hljs-params">()</span> <span class="hljs-type">const</span></span>; <span class="hljs-comment">// 获取当前编辑的成员信息</span><br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onMemberTypeChanged</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 成员类型变化事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onDataTypeChanged</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 数据类型变化事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onIdChanged</span><span class="hljs-params">(<span class="hljs-type">const</span> QString &amp;text)</span></span>; <span class="hljs-comment">// 成员ID变化事件处理</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onNameChanged</span><span class="hljs-params">(<span class="hljs-type">const</span> QString &amp;text)</span></span>; <span class="hljs-comment">// 成员名称变化事件处理</span><br><span class="hljs-keyword">private</span>:<br>    QLineEdit *_idEdit; <span class="hljs-comment">// 成员ID输入框</span><br>    QLineEdit *_nameEdit; <span class="hljs-comment">// 成员名称输入框</span><br>    QComboBox *_memberTypeComboBox; <span class="hljs-comment">// 成员类型下拉框</span><br>    QSpinBox *_memorySizeSpinBox; <span class="hljs-comment">// 内存大小输入框</span><br>    QComboBox *_dataTypeComboBox; <span class="hljs-comment">// 数据类型下拉框</span><br>    QComboBox *_accessibilityComboBox; <span class="hljs-comment">// 可访问性下拉框</span><br>    <span class="hljs-type">const</span> QList&lt;ClassMember&gt; &amp;_members; <span class="hljs-comment">// 所有成员的信息列表</span><br>    <span class="hljs-type">bool</span> _isModifyMode; <span class="hljs-comment">// 标记是否为修改模式</span><br>    QPushButton *_createButton; <span class="hljs-comment">// 创建按钮</span><br>    QLabel *_idWarningLabel; <span class="hljs-comment">// 成员ID警告标签</span><br>    QLabel *_nameWarningLabel; <span class="hljs-comment">// 成员名称警告标签</span><br>    <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isIdExists</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 检查ID是否存在</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><img src="./24暑期学校课设报告/image-20240907001943423.png" alt="image-20240907001943423" style="zoom:50%;" /><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QLineEdit *_idEdit</code></strong> 和 <strong><code>QLineEdit *_nameEdit</code></strong>:</p><ul><li><strong>功能</strong>: 用于输入成员的ID和名称。</li><li><strong>相互关系</strong>: 这些输入框的内容影响成员的唯一标识和名称，<code>onIdChanged(const QString &amp;text)</code> 和 <code>onNameChanged(const QString &amp;text)</code> 槽函数会根据这些输入实时更新界面或检查有效性。</li></ul></li><li><p><strong><code>QComboBox *_memberTypeComboBox</code></strong> 和 <strong><code>QComboBox *_dataTypeComboBox</code></strong>:</p><ul><li><strong>功能</strong>: 用于选择成员类型和数据类型。</li><li><strong>相互关系</strong>: 这些下拉框提供成员的额外信息，<code>onMemberTypeChanged()</code> 和 <code>onDataTypeChanged()</code> 槽函数处理这些选择的变化并更新相关的UI元素或数据。</li></ul></li><li><p><strong><code>QSpinBox *_memorySizeSpinBox</code></strong>:</p><ul><li><strong>功能</strong>: 输入或选择成员的内存大小。</li><li><strong>相互关系</strong>: 该控件的值影响成员的内存分配，确保数据准确，处理内存大小的更改涉及到对其他属性的调整。</li></ul></li><li><p><strong><code>QComboBox *_accessibilityComboBox</code></strong>:</p><ul><li><strong>功能</strong>: 选择成员的可访问性（公有、私有、保护）。</li><li><strong>相互关系</strong>: 提供成员的访问控制信息，影响类成员的访问权限。</li></ul></li><li><p><strong><code>QPushButton *_createButton</code></strong>:</p><ul><li><strong>功能</strong>: 提交创建或修改成员的信息。</li><li><strong>相互关系</strong>: 触发对话框的创建或更新操作，根据 <code>_isModifyMode</code> 标志来决定是创建新成员还是修改现有成员。</li></ul></li><li><p><strong><code>QLabel *_idWarningLabel</code></strong> 和 <strong><code>QLabel *_nameWarningLabel</code></strong>:</p><ul><li><strong>功能</strong>: 显示成员ID和名称的警告信息。</li><li><strong>相互关系</strong>: 提供输入验证的反馈信息，确保成员ID和名称的唯一性和有效性。</li></ul></li><li><p><strong><code>const QList&lt;ClassMember&gt; &amp;_members</code></strong>:</p><ul><li><strong>功能</strong>: 存储所有成员的信息，用于验证新成员的ID是否已存在。</li><li><strong>相互关系</strong>: 提供所有已存在成员的信息，以确保新成员的ID唯一性，通过 <code>isIdExists()</code> 函数检查ID是否重复。</li></ul></li></ul><h5 id="MemberDetailsDialog"><a href="#MemberDetailsDialog" class="headerlink" title="MemberDetailsDialog"></a>MemberDetailsDialog</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">MemberDetailsDialog</span><span class="hljs-params">(<span class="hljs-type">const</span> ClassMember &amp;member, QWidget *parent = <span class="hljs-literal">nullptr</span>)</span></span>; <span class="hljs-comment">// 构造函数，初始化对话框并显示成员详情</span><br>signals:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">memberDeleted</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 成员删除信号，删除操作后发出该信号</span><br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onDeleteButtonClicked</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理删除按钮点击事件</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onReturnButtonClicked</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理返回按钮点击事件</span><br><span class="hljs-keyword">private</span>:<br>    QLabel *_memberDetailsLabel; <span class="hljs-comment">// 显示成员详细信息的标签</span><br>    QPushButton *_deleteButton; <span class="hljs-comment">// 删除按钮</span><br>    QPushButton *_returnButton; <span class="hljs-comment">// 返回按钮</span><br>    ClassMember _member; <span class="hljs-comment">// 当前显示的成员信息</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><img src="./24暑期学校课设报告/image-20240907002251609.png" alt="image-20240907002251609" style="zoom:50%;" /><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QLabel *_memberDetailsLabel</code></strong>:</p><ul><li><strong>功能</strong>: 用于显示成员的详细信息。</li><li><strong>相互关系</strong>: 该标签通过 <code>_member</code> 数据显示成员的各项属性，帮助用户查看成员详情。</li></ul></li><li><p><strong><code>QPushButton *_deleteButton</code></strong>:</p><ul><li><strong>功能</strong>: 提供删除当前显示成员的功能。</li><li><strong>相互关系</strong>: 当用户点击该按钮时，会调用 <code>onDeleteButtonClicked()</code> 槽函数，执行删除操作，并发出 <code>memberDeleted()</code> 信号，通知其他组件成员已被删除。</li></ul></li><li><p><strong><code>QPushButton *_returnButton</code></strong>:</p><ul><li><strong>功能</strong>: 返回上一级或关闭当前对话框。</li><li><strong>相互关系</strong>: 用户点击此按钮时，会调用 <code>onReturnButtonClicked()</code> 槽函数，关闭对话框或返回上一级界面。</li></ul></li><li><p><strong><code>ClassMember _member</code></strong>:</p><ul><li><strong>功能</strong>: 存储当前显示的成员信息。</li><li><strong>相互关系</strong>: 该成员变量用于在界面中展示成员详情，并在需要时执行删除操作，确保界面显示的信息与数据保持一致。</li></ul></li></ul><h5 id="MembersDetailDialog"><a href="#MembersDetailDialog" class="headerlink" title="MembersDetailDialog"></a>MembersDetailDialog</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">MembersDetailDialog</span><span class="hljs-params">(QList&lt;ClassMember&gt; &amp;members, QWidget *parent = <span class="hljs-literal">nullptr</span>)</span></span>; <span class="hljs-comment">// 构造函数，初始化对话框并显示成员列表</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">showMembers</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 显示成员列表</span><br>    <span class="hljs-function">QList&lt;ClassMember&gt; <span class="hljs-type">const</span> <span class="hljs-title">getMembers</span><span class="hljs-params">()</span> </span>&#123; <span class="hljs-keyword">return</span> _members; &#125; <span class="hljs-comment">// 获取成员列表</span><br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onAddMember</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理添加成员操作</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onCellClicked</span><span class="hljs-params">(<span class="hljs-type">const</span> QModelIndex &amp;index)</span></span>; <span class="hljs-comment">// 处理表格单元格点击事件</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">updateTotalSize</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 更新成员总大小标签</span><br><span class="hljs-keyword">private</span>:<br>    QList&lt;ClassMember&gt; _members; <span class="hljs-comment">// 成员列表</span><br>    QTableView *_tableView; <span class="hljs-comment">// 显示成员的表格视图</span><br>    MemberModel *_model; <span class="hljs-comment">// 成员数据模型，用于管理表格数据</span><br>    QLabel *_totalSizeLabel; <span class="hljs-comment">// 显示成员总大小的标签</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><p><img src="/./24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/image-20240907002436977.png" alt="image-20240907002436977"></p><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QList&lt;ClassMember&gt; _members</code></strong>:</p><ul><li><strong>功能</strong>: 存储所有成员的信息。</li><li><strong>相互关系</strong>: 该列表中的成员信息用于在表格视图中显示，并由 <code>_model</code> 数据模型管理，更新操作通过 <code>onAddMember()</code> 和 <code>onCellClicked()</code> 等槽函数影响成员列表的内容。</li></ul></li><li><p><strong><code>QTableView *_tableView</code></strong>:</p><ul><li><strong>功能</strong>: 用于在界面中以表格形式显示成员信息。</li><li><strong>相互关系</strong>: 该视图通过 <code>_model</code> 数据模型显示 <code>_members</code> 列表中的内容。用户点击表格中的某个单元格时，会触发 <code>onCellClicked()</code> 槽函数，进行相应处理。</li></ul></li><li><p><strong><code>MemberModel *_model</code></strong>:</p><ul><li><strong>功能</strong>: 管理成员数据的模型，负责向表格视图提供数据。</li><li><strong>相互关系</strong>: 该模型与 <code>_tableView</code> 直接关联，确保表格显示的数据与 <code>_members</code> 列表一致。每次成员列表更新后，表格内容也会随之更新。</li></ul></li><li><p><strong><code>QLabel *_totalSizeLabel</code></strong>:</p><ul><li><strong>功能</strong>: 显示所有成员的总内存大小。</li><li><strong>相互关系</strong>: 通过 <code>updateTotalSize()</code> 函数计算并更新该标签的内容，以反映当前成员列表中的总内存大小信息。</li></ul></li></ul><h5 id="SearchDialog"><a href="#SearchDialog" class="headerlink" title="SearchDialog"></a>SearchDialog</h5><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">public</span>:<br>    <span class="hljs-built_in">SearchDialog</span>(<span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;classes, QWidget *parent = <span class="hljs-literal">nullptr</span>); <span class="hljs-comment">// 构造函数，初始化搜索对话框并传入类信息列表</span><br>signals:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">searchCompleted</span><span class="hljs-params">(<span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;filteredClasses)</span></span>; <span class="hljs-comment">// 发出搜索完成信号，并传递过滤后的类信息列表</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">cancelSearch</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 发出取消搜索的信号</span><br><span class="hljs-keyword">private</span> slots:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onSearch</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理搜索操作</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onCancel</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理取消操作</span><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">onDateTimeChange</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 处理日期范围变化事件</span><br><span class="hljs-keyword">private</span>:<br>QLineEdit *_idEdit; <span class="hljs-comment">// 用于输入类id的编辑框</span><br>    QLineEdit *_nameEdit; <span class="hljs-comment">// 用于输入类名称的编辑框</span><br>    QLineEdit *_authorEdit; <span class="hljs-comment">// 用于输入作者名称的编辑框</span><br>    QComboBox *_dateRangeComboBox; <span class="hljs-comment">// 用于选择日期范围的下拉框</span><br>    QDateTimeEdit *_startDateEdit; <span class="hljs-comment">// 用于选择开始日期的日期时间编辑框</span><br>    QDateTimeEdit *_endDateEdit; <span class="hljs-comment">// 用于选择结束日期的日期时间编辑框</span><br>    QPushButton *_searchButton; <span class="hljs-comment">// 触发搜索操作的按钮</span><br>    QPushButton *_cancelButton; <span class="hljs-comment">// 触发取消操作的按钮</span><br>    QList&lt;ClassInfo&gt; _classes; <span class="hljs-comment">// 存储原始的类信息列表</span><br></code></pre></td></tr></table></figure><p><strong>图例：</strong></p><p><img src="/./24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/image-20240907002526750.png" alt="image-20240907002526750"></p><p><strong>相互关系：</strong></p><ul><li><p><strong><code>QLineEdit *_nameEdit</code></strong> 和 <strong><code>QLineEdit *_authorEdit</code></strong>:</p><ul><li><strong>功能</strong>: 分别用于输入类名和作者名。</li><li><strong>相互关系</strong>: 这些输入框中的文本在用户点击**<code>_searchButton</code>**时被读取，用于过滤类信息列表 <code>_classes</code>，并调用 <code>onSearch()</code> 进行搜索操作。</li></ul></li><li><p><strong><code>QComboBox *_dateRangeComboBox</code></strong>、<strong><code>QDateTimeEdit *_startDateEdit</code></strong> 和 <strong><code>QDateTimeEdit *_endDateEdit</code></strong>:</p><ul><li><strong>功能</strong>: 用于选择和输入搜索时的日期范围。</li><li><strong>相互关系</strong>: 用户选择或修改日期范围时，会触发 <code>onDateTimeChange()</code>，调整开始和结束日期的可选范围，以保证搜索条件的有效性。</li></ul></li><li><p><strong><code>QPushButton *_searchButton</code></strong>:</p><ul><li><strong>功能</strong>: 触发搜索操作。</li><li><strong>相互关系</strong>: 用户点击该按钮时，会调用 <code>onSearch()</code> 槽函数，使用输入框和日期选择器的内容来过滤 <code>_classes</code> 列表，并通过 <code>searchCompleted()</code> 信号返回过滤后的结果。</li></ul></li><li><p><strong><code>QPushButton *_cancelButton</code></strong>:</p><ul><li><strong>功能</strong>: 取消当前搜索操作并关闭对话框。</li><li><strong>相互关系</strong>: 点击该按钮时，会调用 <code>onCancel()</code> 槽函数，发出 <code>cancelSearch()</code> 信号以通知搜索被取消，同时关闭对话框。</li></ul></li><li><p><strong><code>QList&lt;ClassInfo&gt; _classes</code></strong>:</p><ul><li><strong>功能</strong>: 存储原始的类信息列表，用于搜索和过滤。</li><li><strong>相互关系</strong>: 该列表中的数据通过 <code>onSearch()</code> 进行过滤，并在搜索完成后通过 <code>searchCompleted()</code> 信号传递给调用者。</li></ul></li></ul><h3 id="3-程序所完成的功能"><a href="#3-程序所完成的功能" class="headerlink" title="3. 程序所完成的功能"></a>3. 程序所完成的功能</h3><ol><li><p><strong>创建和管理类信息的对象</strong></p><p>使用 <code>ClassInfo</code> 实例化类，使用 <code>ClassModel</code> 与 <code>MainWindow</code> 中的表格实现对类的展示。</p></li><li><p><strong>创建和管理类成员信息的对象</strong></p><p>使用 <code>ClassMember</code> 实例化类，使用 <code>MemberModel</code> 与 <code>MembersDetailDialog</code> 中的表格实现对成员的展示。</p></li><li><p><strong>增加和删除类</strong></p><p>在 <code>MainWindow</code> 中操作相关控件实现。</p></li><li><p><strong>针对特定类增加或删除类成员信息</strong></p><p>在 <code>MainWindow</code> 中点击特定类的类成员信息打开 <code>MembersDetailDialog</code> 来增删改。</p></li><li><p><strong>基本查询功能</strong></p><p>在 <code>MainWindow</code> 工具栏中点击 <code>Search</code> 控件，实现id，名称，作者，时间的多重筛选查询。</p></li><li><p><strong>数据文件读写</strong></p><p>在 <code>MainWindow</code> 工具栏中点击相应文件菜单，实现对应操作。</p></li><li><p><strong>基本信息显示</strong></p><p>在 <code>MainWindow</code> 中使用表格直观展示信息。</p></li></ol><h3 id="4-额外实现的功能"><a href="#4-额外实现的功能" class="headerlink" title="4. 额外实现的功能"></a>4. 额外实现的功能</h3><ol><li><p><strong>多语言支持</strong>：</p><p>系统实现了通过 <code>QTranslator</code> 进行多语言切换的功能，允许用户选择不同的界面语言。</p></li><li><p><strong>主题切换</strong>：</p><p>提供了明亮主题和黑暗主题的切换功能，用户可以通过按钮在不同的界面主题之间切换。</p></li><li><p><strong>多文件支持</strong>：</p><p>实现了Dat、XML、JSON文件的统一，可以进行数据互通。</p></li><li><p><strong>类和成员信息的修改功能</strong>：</p><p>可以直接修改已有的类和类成员信息，而无需先删除再重新添加，提升了系统的灵活性和用户体验。</p></li><li><p><strong>可行性验证</strong>：</p><p>在创建或编辑类和类成员时，系统提供了实时的ID与名称验证功能，防止重复ID的使用，防止ID与名称为空，并在发现错误时禁用创建按钮。</p></li><li><p><strong>多端协同</strong>：</p><p>实现了管理员和常规用户的分别登录，管理员，支持添加、删除、保存用户数据，用户进行类管理。</p></li><li><p><strong>密码哈希加密</strong>：</p><p>在用户登录功能中，密码进行了哈希加密<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="将密码转换为`QByteArray`，使用SHA-256算法生成哈希值，并将哈希值转换为十六进制字符串以便于存储和比较。">[2]</span></a></sup>存储和验证，增强了系统的安全性。</p></li></ol><h2 id="三、-文件格式描述"><a href="#三、-文件格式描述" class="headerlink" title="三、 文件格式描述"></a>三、 文件格式描述</h2><p><em>（详细描述所保存和读取的文件格式）</em></p><h3 id="1-JSON"><a href="#1-JSON" class="headerlink" title="1. JSON"></a>1. JSON</h3><figure class="highlight json"><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><code class="hljs json"><span class="hljs-punctuation">[</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;Author&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Author1&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;BaseClassName&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;BaseManager&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;CreationDate&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;2024-08-01T10:00:00&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;Function&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Handles data operations.&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;ID&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;Members&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>            <span class="hljs-punctuation">&#123;</span><br>                <span class="hljs-attr">&quot;Accessibility&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Public&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;DataType&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Double&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;MemberID&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;MemberName&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dataField&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;MemberType&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Data&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;MemorySize&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">8</span><br>            <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-comment">// other Member</span><br>            <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;Name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;DataManager&quot;</span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-comment">// other Class</span><br>    <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">]</span><br></code></pre></td></tr></table></figure><ul><li><strong><code>Author</code></strong>: 记录创建类的作者信息。</li><li><strong><code>BaseClassName</code></strong>: 类的基类名称，如果当前类是从另一个类继承而来的，这里会显示基类的名称。</li><li><strong><code>CreationDate</code></strong>: 类的创建日期和时间，使用标准的 ISO 8601 日期格式（<code>YYYY-MM-DDTHH:MM:SS</code>）。</li><li><strong><code>Function</code></strong>: 描述类的主要功能或职责。</li><li><strong><code>ID</code></strong>: 类的唯一标识符，用于区分不同的类对象。</li><li><strong><code>Members</code></strong>: 存储类成员信息的数组，每个成员信息包括以下字段：<ul><li><strong><code>Accessibility</code></strong>: 成员的访问权限，如<code>Public</code>（公共）、<code>Private</code>（私有）、<code>Protected</code>（受保护）。</li><li><strong><code>DataType</code></strong>: 成员的数据类型，如 <code>Double</code>、<code>Int</code>、<code>String</code>等。</li><li><strong><code>MemberID</code></strong>: 成员的唯一标识符，用于区分不同的成员。</li><li><strong><code>MemberName</code></strong>: 成员的名称，通常是变量或函数的名称。</li><li><strong><code>MemberType</code></strong>: 成员类型，表示该成员是数据成员还是函数成员（例如 <code>Data</code> 或 <code>Function</code>）。</li><li><strong><code>MemorySize</code></strong>: 数据成员的内存大小（以字节为单位），函数成员通常为 <code>0</code>。</li></ul></li><li><strong><code>Name</code></strong>: 类的名称，用于标识和引用该类。</li></ul><h3 id="2-XML"><a href="#2-XML" class="headerlink" title="2. XML"></a>2. XML</h3><figure class="highlight xml"><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><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">Classes</span>&gt;</span> <br><span class="hljs-tag">&lt;<span class="hljs-name">Class</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">ID</span>&gt;</span>2<span class="hljs-tag">&lt;/<span class="hljs-name">ID</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">Name</span>&gt;</span>UserController<span class="hljs-tag">&lt;/<span class="hljs-name">Name</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">BaseClassName</span>&gt;</span>BaseController<span class="hljs-tag">&lt;/<span class="hljs-name">BaseClassName</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">Function</span>&gt;</span>Manages user-related functions.<span class="hljs-tag">&lt;/<span class="hljs-name">Function</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">CreationDate</span>&gt;</span>2024-08-02T11:00:00<span class="hljs-tag">&lt;/<span class="hljs-name">CreationDate</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">Author</span>&gt;</span>Author2<span class="hljs-tag">&lt;/<span class="hljs-name">Author</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">Members</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">Member</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">MemberID</span>&gt;</span>2<span class="hljs-tag">&lt;/<span class="hljs-name">MemberID</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">MemberName</span>&gt;</span>userID<span class="hljs-tag">&lt;/<span class="hljs-name">MemberName</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">MemberType</span>&gt;</span>Data<span class="hljs-tag">&lt;/<span class="hljs-name">MemberType</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">MemorySize</span>&gt;</span>4<span class="hljs-tag">&lt;/<span class="hljs-name">MemorySize</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">DataType</span>&gt;</span>Int<span class="hljs-tag">&lt;/<span class="hljs-name">DataType</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">Accessibility</span>&gt;</span>Private<span class="hljs-tag">&lt;/<span class="hljs-name">Accessibility</span>&gt;</span><br>            <span class="hljs-tag">&lt;/<span class="hljs-name">Member</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">member</span>&gt;</span><br>                <span class="hljs-comment">&lt;!-- ... --&gt;</span><br>            <span class="hljs-tag">&lt;/<span class="hljs-name">member</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">Members</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">Class</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">Class</span>&gt;</span><br>        <span class="hljs-comment">&lt;!-- ... --&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">Class</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">Classes</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li><strong><code>&lt;Class&gt;</code></strong>: 表示一个类对象的整体信息，包含该类的所有属性和成员。<ul><li><strong><code>&lt;ID&gt;</code></strong>: 类的唯一标识符，用于区分不同的类对象。</li><li><strong><code>&lt;Name&gt;</code></strong>: 类的名称，用于标识和引用该类。</li><li><strong><code>&lt;BaseClassName&gt;</code></strong>: 类的基类名称，如果当前类是从另一个类继承而来的，这里会显示基类的名称。</li><li><strong><code>&lt;Function&gt;</code></strong>: 描述类的主要功能或职责。</li><li><strong><code>&lt;CreationDate&gt;</code></strong>: 类的创建日期和时间，使用标准的 ISO 8601 日期格式（<code>YYYY-MM-DDTHH:MM:SS</code>）。</li><li><strong><code>&lt;Author&gt;</code></strong>: 记录创建类的作者信息。</li><li><strong><code>&lt;Members&gt;</code></strong>: 存储类成员信息的集合标签，包含若干个 <code>&lt;Member&gt;</code> 标签，每个 <code>&lt;Member&gt;</code> 标签表示一个类成员。<ul><li><strong><code>&lt;Member&gt;</code></strong>: 描述类中的一个成员的具体信息。<ul><li><strong><code>&lt;MemberID&gt;</code></strong>: 成员的唯一标识符，用于区分不同的成员。</li><li><strong><code>&lt;MemberName&gt;</code></strong>: 成员的名称，通常是变量或函数的名称。</li><li><strong><code>&lt;MemberType&gt;</code></strong>: 成员类型，表示该成员是数据成员还是函数成员（例如 <code>Data</code> 或 <code>Function</code>）。</li><li><strong><code>&lt;MemorySize&gt;</code></strong>: 数据成员的内存大小（以字节为单位），函数成员通常为 <code>0</code>。</li><li><strong><code>&lt;DataType&gt;</code></strong>: 成员的数据类型，如 <code>Int</code>、<code>Void</code>、<code>String</code>等。</li><li><strong><code>&lt;Accessibility&gt;</code></strong>: 成员的访问权限，如 <code>Public</code>（公共）、<code>Private</code>（私有）、<code>Protected</code>（受保护）。</li></ul></li></ul></li></ul></li></ul><h3 id="3-Dat"><a href="#3-Dat" class="headerlink" title="3. Dat"></a>3. Dat</h3><ul><li><p>类数据块:</p><p>保存类的各个属性（ID、名称、基类名、功能、创建日期、作者）。</p><p>保存成员的数量。</p><p>依次保存每个成员的数据。</p></li><li><p>成员数据块:</p><p>保存成员的各个属性（ID、名称、类型、内存大小、数据类型、访问权限），其中枚举类型被转换为整数进行保存。</p></li></ul><h2 id="四、-具体实现过程"><a href="#四、-具体实现过程" class="headerlink" title="四、 具体实现过程"></a>四、 具体实现过程</h2><p><em>（详细描述按照什么顺序进行程序搭建，在实现中碰到的问题以及解决的方法）</em></p><h3 id="1-需求分析与规划阶段"><a href="#1-需求分析与规划阶段" class="headerlink" title="1. 需求分析与规划阶段"></a>1. 需求分析与规划阶段</h3><p>在开始程序的实现之前，首先进行了需求分析，以明确系统的功能需求、数据结构和用户界面。确定了以下主要功能：</p><ul><li>用户管理与登录</li><li>类和成员信息管理</li><li>数据的保存与加载（支持 XML、JSON 和 DAT 格式）</li><li>界面设计和用户交互</li></ul><h3 id="2-准备工具阶段"><a href="#2-准备工具阶段" class="headerlink" title="2. 准备工具阶段"></a>2. 准备工具阶段</h3><ul><li><p><strong>Qt Creator</strong>：使用 Qt Creator 作为主要的开发环境。虽然 Qt Creator 提供了可视化的 UI 设计器，但在这个项目中我决定完全使用代码编写 UI，以实现更高的自定义性和灵活性。</p></li><li><p><strong>Qt Linguist</strong>：选用 Qt Linguist来构建应用的翻译功能。这一工具帮助创建和管理多语言支持，允许在应用中动态切换语言，提升用户体验。</p></li><li><p><strong>Git 和 GitHub</strong>：使用 Git 进行版本控制，GitHub 作为远程仓库<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="远程仓库地址：[harkerhand/OOPM (github.com)](https://github.com/harkerhand/OOPM)">[3]</span></a></sup>来存储和管理代码。Git 的版本控制功能帮助跟踪代码变更历史，GitHub 提供了团队协作、代码审查和持续集成的支持。</p></li></ul><h3 id="3-设计阶段"><a href="#3-设计阶段" class="headerlink" title="3. 设计阶段"></a>3. 设计阶段</h3><h4 id="类定义"><a href="#类定义" class="headerlink" title="类定义"></a>类定义</h4><ol><li><p><strong><code>ClassInfo.h</code></strong> 和 <strong><code>ClassMember.h</code></strong>: </p><p>定义了表示类和类成员的信息，包括数据成员和方法。</p></li><li><p><strong><code>Utils.h</code></strong>:</p><p>包含了处理密码哈希加密、验证账户、保存用户数据等功能的声明。</p></li><li><p><strong><code>CreateClassDialog.h</code> 和 <code>CreateMemberDialog.h</code></strong>:</p><p>定义了创建类和类成员的对话框，包括处理用户输入的槽函数和界面元素。</p></li><li><p><strong><code>MemberDetailsDialog.h</code> 和 <code>MembersDetailDialog.h</code></strong>:</p><p>定义了显示单个与多个成员详细信息的对话框。</p></li><li><p><strong><code>SearchDialog.h</code></strong>:</p><p>定义了一个用于查询类信息的对话框，包含搜索条件和结果显示。</p></li><li><p><strong>其他UI类与辅助类</strong></p></li></ol><h3 id="4-实现阶段"><a href="#4-实现阶段" class="headerlink" title="4. 实现阶段"></a>4. 实现阶段</h3><h4 id="4-1-类实现"><a href="#4-1-类实现" class="headerlink" title="4.1 类实现"></a>4.1 类实现</h4><ol><li><p><strong>实现数据类</strong>：</p><p>在 <strong><code>ClassInfo.cpp</code></strong> 和 <strong><code>ClassMember.cpp</code></strong> 文件中，实现了类和类成员的具体功能，例如数据读写和保存到流等操作。</p></li><li><p><strong>实现用户数据模型</strong>：</p><p>在 <strong><code>UserModel.cpp</code></strong> 文件中，实现了 <code>QAbstractTableModel</code> 的各个方法，包括数据的显示和标题的处理。</p></li><li><p><strong>实现工具函数</strong>：</p><p>在 <strong><code>Utils.cpp</code></strong> 文件中，完成了密码哈希加密、账户验证等功能的实现，并处理了与文件读写相关的操作。</p></li><li><p><strong>实现对话框</strong>：</p><p><strong><code>CreateClassDialog.cpp</code> 和 <code>CreateMemberDialog.cpp</code></strong>: 实现了创建类和成员对话框的界面布局及槽函数。</p><p><strong><code>MemberDetailsDialog.cpp</code> 和 <code>MembersDetailDialog.cpp</code></strong>: 实现了显示成员详细信息和成员列表的对话框。</p><p><strong><code>SearchDialog.cpp</code></strong>: 实现了搜索功能的对话框，包括搜索和取消操作。</p></li><li><p><strong>实现窗口</strong>：</p><p>在 <strong><code>MainWindow.cpp</code></strong> 中，实现了主窗口的界面和功能，包括添加和删除类，显示类信息，处理文件读写等。</p><p>在 <strong><code>LoginWindow.cpp</code></strong> 中，实现了验证用户登录的用户名和密码，以及界面语言和主题切换。</p><p>在 <strong><code>UserManagementWindow.cpp</code></strong> 中，实现了管理用户信息，包括添加、删除用户，保存用户数据，显示用户列表及其详细信息。</p></li></ol><h4 id="4-2-主要功能实现"><a href="#4-2-主要功能实现" class="headerlink" title="4.2 主要功能实现"></a>4.2 主要功能实现</h4><ol><li><strong>创建和管理类与成员</strong>：<ul><li>使用 <strong><code>CreateClassDialog</code></strong> 和 <strong><code>CreateMemberDialog</code></strong> 来创建类和类成员。</li><li>使用 <strong><code>MembersDetailDialog</code></strong> 来查看和管理类成员的详细信息。</li></ul></li><li><strong>增加和删除功能</strong>：<ul><li>在 <strong><code>MainWindow</code></strong> 中实现了增加和删除类的功能。</li><li>对于类成员，使用 <strong><code>MembersDetailDialog</code></strong> 和 <strong><code>ClassInfo</code></strong> 中的方法来管理成员。</li></ul></li><li><strong>数据文件读写</strong>：<ul><li>实现了 XML、JSON 和 DAT 文件的读写操作。<strong><code>Utils.cpp</code></strong> 处理了文件的保存和加载操作，并确保了数据的正确性。</li></ul></li><li><strong>信息显示</strong>：<ul><li>使用 <strong><code>MainWindow</code></strong> 的表格视图和详细信息对话框来显示所有类的信息和特定类的成员信息。</li></ul></li><li><strong>额外功能</strong>：<ul><li>实现了密码哈希加密功能用于增强安全性。</li><li>增加了修改类和成员信息的功能，而不是删除和重新添加。</li></ul></li></ol><h3 id="5-测试阶段"><a href="#5-测试阶段" class="headerlink" title="5. 测试阶段"></a>5. 测试阶段</h3><p>单元测试：对各个模块进行了单元测试，包括类信息的保存与加载、数据模型的显示功能等。</p><p>集成测试：测试了不同模块之间的集成，包括 UI 与数据模型的交互、文件保存与加载的正确性。</p><p>调试：通过调试工具定位并修复了代码中的错误和问题，确保程序的稳定性和功能的完整性。</p><p><em>这部分将在下一节详细说明</em></p><h3 id="6-遇到的问题及解决方案"><a href="#6-遇到的问题及解决方案" class="headerlink" title="6. 遇到的问题及解决方案"></a>6. 遇到的问题及解决方案</h3><ol><li><strong>数据格式转换问题</strong>：<ul><li><strong>问题</strong>：在处理数据的 XML 和 JSON 格式时，出现了数据格式不匹配的问题。</li><li><strong>解决方案</strong>：通过详细阅读格式规范和实现相应的解析和序列化函数，确保了数据的正确读写。</li></ul></li><li><strong>界面刷新问题</strong>：<ul><li><strong>问题</strong>：在修改数据后，界面没有及时更新。</li><li><strong>解决方案</strong>：添加了界面更新的函数<code>updateUI()</code>，在数据修改后调用以刷新界面。</li></ul></li><li><strong>文件读写异常</strong>：<ul><li><strong>问题</strong>：在文件读写过程中，偶尔出现文件无法打开或数据读取错误。</li><li><strong>解决方案</strong>：增加了异常处理机制，确保文件操作的健壮性，并提供了用户友好的错误提示。</li></ul></li><li><strong>密码验证异常</strong>：<ul><li><strong>问题</strong>：在密码哈希存储过程中，如果读取到的密码哈希值被二次加密，可能会导致密码校验失败。</li><li><strong>解决方案</strong>:<ol><li><strong>长度验证</strong>：在保存和读取密码哈希值时，通过检查哈希值的长度来判断是否可能发生了二次加密。不同哈希算法生成的哈希值长度是固定的，因此可以利用哈希值的长度来确保哈希值没有被错误处理。</li><li><strong>存储和验证一致性</strong>：确保在存储和读取密码哈希时，哈希值只经过一次哈希处理。避免在存储后对哈希值进行任何额外的哈希处理，以确保密码校验时能够准确匹配。</li></ol></li></ul></li><li><strong>同一个界面用于创建与修改的复用问题</strong>：<ul><li><strong>问题</strong>：在一个界面中复用创建和修改对象的功能时，会遇到如何区分创建模式和修改模式的问题。</li><li><strong>解决方案</strong>:<ol><li><strong>动态按钮文本</strong>：使用条件表达式来设置按钮文本，根据当前操作模式（创建或修改）来更新按钮的显示文本。</li><li><strong>模式标识</strong>：在界面初始化时，通过参数或标识来确定当前是创建模式还是修改模式。</li><li><strong>功能适配</strong>：在按钮点击事件中，根据当前模式执行不同的逻辑。对于创建模式，执行创建操作；对于修改模式，执行更新操作。在代码中实现逻辑分支来处理不同的操作模式。</li><li><strong>用户提示</strong>：提供清晰的用户提示，确保用户知道当前操作的模式是什么。</li></ol></li></ul></li></ol><h2 id="五、-测试报告"><a href="#五、-测试报告" class="headerlink" title="五、 测试报告"></a>五、 测试报告</h2><p><em>（对主要功能进行测试，可以列表表示各项功能的完成情况，也可以贴图表示）</em></p><h3 id="1-用户登录功能"><a href="#1-用户登录功能" class="headerlink" title="1. 用户登录功能"></a>1. 用户登录功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>输入正确的用户名和密码登录</td><td>成功登录，进入主界面</td><td>成功登录</td><td>无问题</td></tr><tr><td>输入管理员的用户名和密码登录</td><td>成功登录，进入管理员界面</td><td>成功登录</td><td>无问题</td></tr><tr><td>输入错误的用户名和密码登录</td><td>登录失败，显示错误提示</td><td>正确显示错误提示</td><td>无问题</td></tr><tr><td>空用户名或密码登录</td><td>登录失败，显示错误提示</td><td>正确显示错误提示</td><td>无问题</td></tr></tbody></table><h3 id="2-类信息管理功能"><a href="#2-类信息管理功能" class="headerlink" title="2. 类信息管理功能"></a>2. 类信息管理功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>添加新的类信息</td><td>新类信息成功添加到列表中</td><td>添加成功</td><td>无问题</td></tr><tr><td>修改现有的类信息</td><td>类信息成功修改</td><td>修改成功</td><td>无问题</td></tr><tr><td>删除类信息</td><td>类信息成功删除</td><td>删除成功</td><td>无问题</td></tr><tr><td>显示类详细信息</td><td>正确显示类的详细信息</td><td>显示正确</td><td>无问题</td></tr></tbody></table><h3 id="3-成员信息管理功能"><a href="#3-成员信息管理功能" class="headerlink" title="3. 成员信息管理功能"></a>3. 成员信息管理功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>添加新的成员信息</td><td>新成员信息成功添加到列表中</td><td>添加成功</td><td>无问题</td></tr><tr><td>修改现有的成员信息</td><td>成员信息成功修改</td><td>修改成功</td><td>无问题</td></tr><tr><td>删除成员信息</td><td>成员信息成功删除</td><td>删除成功</td><td>无问题</td></tr><tr><td>显示成员详细信息</td><td>正确显示成员的详细信息</td><td>显示正确</td><td>无问题</td></tr></tbody></table><h3 id="4-数据保存与加载功能"><a href="#4-数据保存与加载功能" class="headerlink" title="4. 数据保存与加载功能"></a>4. 数据保存与加载功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>保存为 DAT 格式</td><td>正确保存数据为 DAT 文件</td><td>保存成功</td><td>无问题</td></tr><tr><td>保存为 XML 格式</td><td>正确保存数据为 XML 文件</td><td>保存成功</td><td>无问题</td></tr><tr><td>保存为 JSON 格式</td><td>正确保存数据为 JSON 文件</td><td>保存成功</td><td>无问题</td></tr><tr><td>加载 DAT 格式数据</td><td>正确加载 DAT 文件中的数据</td><td>加载成功</td><td>无问题</td></tr><tr><td>加载 XML 格式数据</td><td>正确加载 XML 文件中的数据</td><td>加载成功</td><td>无问题</td></tr><tr><td>加载 JSON 格式数据</td><td>正确加载 JSON 文件中的数据</td><td>加载成功</td><td>无问题</td></tr></tbody></table><h3 id="5-用户管理功能"><a href="#5-用户管理功能" class="headerlink" title="5. 用户管理功能"></a>5. 用户管理功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>添加用户</td><td>新用户成功添加到用户列表中</td><td>添加成功</td><td>无问题</td></tr><tr><td>删除用户</td><td>用户成功删除</td><td>删除成功</td><td>无问题</td></tr><tr><td>保存用户数据</td><td>用户数据成功保存</td><td>保存成功</td><td>无问题</td></tr><tr><td>显示用户列表</td><td>正确显示用户列表</td><td>显示正确</td><td>无问题</td></tr></tbody></table><h3 id="6-界面与交互功能"><a href="#6-界面与交互功能" class="headerlink" title="6. 界面与交互功能"></a>6. 界面与交互功能</h3><table><thead><tr><th>测试内容</th><th>预期结果</th><th>实际结果</th><th>备注</th></tr></thead><tbody><tr><td>界面控件功能</td><td>各个控件按预期功能工作</td><td>功能正常</td><td>无问题</td></tr><tr><td>界面布局与美观</td><td>界面布局合理，符合设计要求</td><td>布局合理</td><td>无问题</td></tr><tr><td>语言切换功能</td><td>界面语言切换成功</td><td>切换成功</td><td>无问题</td></tr><tr><td>主题切换功能</td><td>主题切换成功，界面显示正确</td><td>切换成功</td><td>无问题</td></tr></tbody></table><h3 id="7-结论"><a href="#7-结论" class="headerlink" title="7. 结论"></a>7. 结论</h3><p>所有主要功能经过测试均正常工作，程序的各项功能完成情况符合预期。没有发现严重的错误或问题，程序在Windows操作系统环境下表现稳定。</p><h2 id="六、-心得体会和相关建议"><a href="#六、-心得体会和相关建议" class="headerlink" title="六、 心得体会和相关建议"></a>六、 心得体会和相关建议</h2><p><em>（课程小结和对课程改进的建议）</em></p><h3 id="1-心得体会"><a href="#1-心得体会" class="headerlink" title="1. 心得体会"></a>1. 心得体会</h3><ol><li><p><strong>技术掌握的提升</strong></p><p>在项目开发过程中，我对C++和Qt的掌握有了显著提升。特别是在数据持久化和文件处理方面，实际操作使我对这些技术有了更深入的理解。Qt的信号与槽机制等特性在实际应用中的效果超出了我的预期，使得程序的开发变得更加高效。</p></li><li><p><strong>代码复用和功能模块化</strong></p><p>在实现过程中，复用相同界面或功能时，通过清晰的模式标识和条件逻辑可以有效区分不同操作。这种方法减少了代码重复，提高了系统的维护性。</p></li><li><p><strong>数据一致性的重要性</strong></p><p>确保数据在不同格式（XML, JSON）之间的正确转换是关键，合理的格式规范和详细的实现能避免数据不一致的问题。</p></li><li><p><strong>项目管理和组织能力的提高</strong></p><p>项目的开发需要协调多个模块和功能的实现，这不仅考验了我的编程能力，也提升了我的项目管理和组织能力。从需求分析、设计实现到测试和调试，每一步都需要精细的计划和有效的执行。通过这个项目，我学会了如何更好地分解任务、制定合理的计划表，并在开发过程中灵活调整策略以应对各种挑战。</p></li></ol><h3 id="2-对课程的建议"><a href="#2-对课程的建议" class="headerlink" title="2. 对课程的建议"></a>2. 对课程的建议</h3><ol><li><p><strong>增加实际项目案例</strong></p><p>课程中增加更多的实际项目案例，将帮助学生更好地理解理论知识的实际应用。</p></li><li><p><strong>提供更多的实际开发工具和资源</strong></p><p>课程中可以提供更多实际开发工具和资源的使用指南，例如版本控制工具（如Git）的深入讲解，测试工具的使用方法等，以帮助学生更好地进行项目管理和维护。</p></li><li><p><strong>加强测试和调试技巧的培训</strong></p><p>测试和调试是开发过程中至关重要的环节。建议课程中增加有关测试策略、调试技巧和错误处理的详细讲解和实践，以提高学生在实际开发中的问题解决能力。</p></li><li><p><strong>注重用户体验设计</strong></p><p>在课程中加强对用户体验（UX）和用户界面（UI）设计的重视，可以帮助学生更好地理解用户需求并设计出更符合用户期望的程序界面。提供一些用户体验设计的实践课程将有助于提升学生的综合设计能力。</p></li></ol><h2 id="七、-亮点"><a href="#七、-亮点" class="headerlink" title="七、 亮点"></a>七、 亮点</h2><h3 id="1-使用-Qt-的-Resources-功能实现内部资源的管理"><a href="#1-使用-Qt-的-Resources-功能实现内部资源的管理" class="headerlink" title="1. 使用 Qt 的 Resources 功能实现内部资源的管理"></a>1. 使用 Qt 的 Resources 功能实现内部资源的管理</h3><figure class="highlight xml"><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><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">RCC</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">qresource</span> <span class="hljs-attr">prefix</span>=<span class="hljs-string">&quot;/&quot;</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>styles/darkstyle.qss<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>styles/lightstyle.qss<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>langs/OOPM_zh_CN.qm<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>langs/OOPM_en_US.qm<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>imgs/low.png<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>imgs/darkBg.png<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>imgs/lightBg.png<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>imgs/bgtext.png<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>imgs/icon.png<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>passports/admin.dat<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">qresource</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">RCC</span>&gt;</span><br></code></pre></td></tr></table></figure><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">RESOURCES</span> <span class="hljs-string">+= \</span><br><span class="hljs-string">    resources.qrc</span><br></code></pre></td></tr></table></figure><h3 id="2-重写-QWidget-的-void-paintEvent-QPaintEvent-event-函数，实现了背景的自定义，并根据明暗主题选择相应的背景。"><a href="#2-重写-QWidget-的-void-paintEvent-QPaintEvent-event-函数，实现了背景的自定义，并根据明暗主题选择相应的背景。" class="headerlink" title="2. 重写 QWidget 的 void paintEvent(QPaintEvent *event) 函数，实现了背景的自定义，并根据明暗主题选择相应的背景。"></a>2. 重写 <code>QWidget</code> 的 <code>void paintEvent(QPaintEvent *event)</code> 函数，实现了背景的自定义，并根据明暗主题选择相应的背景。</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">LoginWindow::paintEvent</span><span class="hljs-params">(QPaintEvent *event)</span> </span>&#123;<br>    <span class="hljs-function">QPainter <span class="hljs-title">painter</span><span class="hljs-params">(<span class="hljs-keyword">this</span>)</span></span>;<br>    <span class="hljs-keyword">if</span>(_nowTheme) painter.<span class="hljs-built_in">drawPixmap</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">width</span>(), <span class="hljs-built_in">height</span>(),<span class="hljs-built_in">QPixmap</span>(<span class="hljs-string">&quot;:/imgs/darkBg.png&quot;</span>));<br>    <span class="hljs-keyword">else</span> painter.<span class="hljs-built_in">drawPixmap</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">width</span>(), <span class="hljs-built_in">height</span>(),<span class="hljs-built_in">QPixmap</span>(<span class="hljs-string">&quot;:/imgs/lightBg.png&quot;</span>));<br>    QWidget::<span class="hljs-built_in">paintEvent</span>(event); <span class="hljs-comment">// Call base class implementation if needed</span><br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-将多次复用的值设置为全局变量，并且调用Windows环境变量来获取相应路径。"><a href="#3-将多次复用的值设置为全局变量，并且调用Windows环境变量来获取相应路径。" class="headerlink" title="3. 将多次复用的值设置为全局变量，并且调用Windows环境变量来获取相应路径。"></a>3. 将多次复用的值设置为全局变量，并且调用Windows环境变量来获取相应路径。</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">extern</span> QString DOCUMENT_PATH;<br><span class="hljs-keyword">extern</span> QString USER_FILE_PATH;<br><span class="hljs-keyword">extern</span> QString ICON_PATH;<br><span class="hljs-keyword">extern</span> QTranslator translator;<br>QString DOCUMENT_PATH = QStandardPaths::<span class="hljs-built_in">writableLocation</span>(QStandardPaths::DocumentsLocation);<br>QString USER_FILE_PATH = DOCUMENT_PATH + <span class="hljs-string">&quot;/OOPM/passports/users.dat&quot;</span>;<br>QString ICON_PATH = <span class="hljs-string">&quot;:/imgs/icon.png&quot;</span>;<br><span class="hljs-function">QTranslator <span class="hljs-title">translator</span><span class="hljs-params">(qApp)</span></span>;<br></code></pre></td></tr></table></figure><h3 id="4-继承覆写-QAbstractTableModel-来实现各个表格的数据加载。"><a href="#4-继承覆写-QAbstractTableModel-来实现各个表格的数据加载。" class="headerlink" title="4. 继承覆写 QAbstractTableModel 来实现各个表格的数据加载。"></a>4. 继承覆写 <code>QAbstractTableModel</code> 来实现各个表格的数据加载。</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">ClassModel</span>(<span class="hljs-type">const</span> QList&lt;ClassInfo&gt; &amp;classes, QObject *parent = <span class="hljs-literal">nullptr</span>);<br><span class="hljs-built_in">MemberModel</span>(<span class="hljs-type">const</span> QList&lt;ClassMember&gt; &amp;members, QObject *parent = <span class="hljs-literal">nullptr</span>);<br><span class="hljs-built_in">UserModel</span>(<span class="hljs-type">const</span> QList&lt;QPair&lt;QString, QString&gt;&gt; &amp;users, QObject *parent = <span class="hljs-literal">nullptr</span>);<br></code></pre></td></tr></table></figure><h3 id="5-将复选框有限的选项封装为枚举类，使用-QMetaType-声明为全局，增强代码的可读性。"><a href="#5-将复选框有限的选项封装为枚举类，使用-QMetaType-声明为全局，增强代码的可读性。" class="headerlink" title="5. 将复选框有限的选项封装为枚举类，使用 QMetaType 声明为全局，增强代码的可读性。"></a>5. 将复选框有限的选项封装为枚举类，使用 <code>QMetaType</code> 声明为全局，增强代码的可读性。</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">enum class</span> <span class="hljs-title class_">MemberType</span> &#123; Data, Function &#125;;<br><span class="hljs-keyword">enum class</span> <span class="hljs-title class_">Accessibility</span> &#123; Public, Private, Protected &#125;;<br><span class="hljs-keyword">enum class</span> <span class="hljs-title class_">DataType</span> &#123; Int, Float, Double, Char, String, Custom &#125;;<br><span class="hljs-keyword">enum class</span> <span class="hljs-title class_">UserType</span> &#123; Admin, User, Invalid &#125;;<br><span class="hljs-built_in">Q_DECLARE_METATYPE</span>(MemberType)<br><span class="hljs-built_in">Q_DECLARE_METATYPE</span>(Accessibility)<br><span class="hljs-built_in">Q_DECLARE_METATYPE</span>(DataType)<br></code></pre></td></tr></table></figure><h3 id="6-使用正则表达式限制用户名与密码输入的范围"><a href="#6-使用正则表达式限制用户名与密码输入的范围" class="headerlink" title="6. 使用正则表达式限制用户名与密码输入的范围"></a>6. 使用正则表达式限制用户名与密码输入的范围</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">QRegularExpression <span class="hljs-title">regex</span><span class="hljs-params">(<span class="hljs-string">&quot;[A-Za-z0-9!@#$%^&amp;*()_+\\-=\\[\\]&#123;&#125;;&#x27;:\&quot;\\\\|,.&lt;&gt;\\/?]+&quot;</span>)</span></span>;<br>QRegularExpressionValidator *validator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">QRegularExpressionValidator</span>(regex, <span class="hljs-keyword">this</span>);<br>_usernameLineEdit-&gt;<span class="hljs-built_in">setValidator</span>(validator);<br>_passwordLineEdit-&gt;<span class="hljs-built_in">setValidator</span>(validator);<br></code></pre></td></tr></table></figure><h3 id="7-使用-Qt-Linguist-实现中英双语切换"><a href="#7-使用-Qt-Linguist-实现中英双语切换" class="headerlink" title="7. 使用 Qt Linguist 实现中英双语切换"></a>7. 使用 Qt Linguist 实现中英双语切换</h3><h3 id="8-使用-qss-文件实现明暗主题切换"><a href="#8-使用-qss-文件实现明暗主题切换" class="headerlink" title="8. 使用 .qss 文件实现明暗主题切换"></a>8. 使用 <code>.qss</code> 文件实现明暗主题切换</h3><h3 id="9-使用了规定格式的文档注释，便于开发与后期维护"><a href="#9-使用了规定格式的文档注释，便于开发与后期维护" class="headerlink" title="9. 使用了规定格式的文档注释，便于开发与后期维护"></a>9. 使用了规定格式的文档注释，便于开发与后期维护</h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// 根据 ID 移除类</span><br><span class="hljs-comment">// @param id 要移除的类的 ID</span><br><span class="hljs-comment">// @return 如果成功移除返回 true，否则返回 false</span><br><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">removeById</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">int</span> id)</span></span>;<br></code></pre></td></tr></table></figure><p><em>与自动文档生成工具<sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="自动文档生成工具（如 Doxygen、Javadoc 等）可以通过解析源代码中的注释自动生成文档，将代码逻辑与解释文本结合，易于阅读。">[4]</span></a></sup>兼容</em></p><h3 id="10-使用-Git-与-Github-实现了版本管理"><a href="#10-使用-Git-与-Github-实现了版本管理" class="headerlink" title="10. 使用 Git 与 Github 实现了版本管理"></a>10. 使用 Git 与 Github 实现了版本管理</h3><p><img src="/./24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/image-20240907000716633.png" alt="image-20240907000716633"></p><h3 id="11-使用-QIcon-实现了界面图标的自定义"><a href="#11-使用-QIcon-实现了界面图标的自定义" class="headerlink" title="11. 使用 QIcon 实现了界面图标的自定义"></a>11. 使用 <code>QIcon</code> 实现了界面图标的自定义</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">setWindowIcon</span>(<span class="hljs-built_in">QIcon</span>(ICON_PATH)); <span class="hljs-comment">// ICON_PATH 为全局变量</span><br></code></pre></td></tr></table></figure><h3 id="12-使用-css-插入来实现控件风格自定义"><a href="#12-使用-css-插入来实现控件风格自定义" class="headerlink" title="12. 使用 css 插入来实现控件风格自定义"></a>12. 使用 <code>css</code> 插入来实现控件风格自定义</h3><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></pre></td><td class="code"><pre><code class="hljs cpp">_loginButton-&gt;<span class="hljs-built_in">setStyleSheet</span>(<br>    <span class="hljs-string">&quot;QPushButton &#123;&quot;</span><br>    <span class="hljs-string">&quot;    border: 2px solid rgba(219, 125, 100);&quot;</span><br>    <span class="hljs-string">&quot;    border-radius: 5px;&quot;</span><br>    <span class="hljs-string">&quot;    padding: 5px 10px;&quot;</span><br>    <span class="hljs-string">&quot;&#125;&quot;</span><br>    <span class="hljs-string">&quot;QPushButton:hover &#123;&quot;</span><br>    <span class="hljs-string">&quot;    border: 2px solid blue;&quot;</span>  <span class="hljs-comment">// 悬停时边框变为蓝色</span><br>    <span class="hljs-string">&quot;&#125;&quot;</span><br>    <span class="hljs-string">&quot;QPushButton:pressed &#123;&quot;</span><br>    <span class="hljs-string">&quot;    border: 2px solid green;&quot;</span> <span class="hljs-comment">// 按下时边框变为绿色</span><br><span class="hljs-string">&quot;&#125;&quot;</span><br>);<br></code></pre></td></tr></table></figure><h3 id="13-使用-QDir-创建文件夹来保证软件正常运行-1"><a href="#13-使用-QDir-创建文件夹来保证软件正常运行-1" class="headerlink" title="13. 使用 QDir 创建文件夹来保证软件正常运行[1]"></a>13. 使用 <code>QDir</code> 创建文件夹来保证软件正常运行<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="程序执行初，会在系统Documents目录创建`/OOPM/passports/`文件夹，文件指该路径下的users.bat文件">[1]</span></a></sup></h3><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// main.cpp</span><br>QDir dir;<br>QString path = DOCUMENT_PATH + <span class="hljs-string">&quot;/OOPM/passports/&quot;</span>; <span class="hljs-comment">// 指定新建文件夹的路径</span><br><span class="hljs-keyword">if</span> (!dir.<span class="hljs-built_in">exists</span>(path)) &#123;<br>    <span class="hljs-comment">// 如果文件夹不存在，则创建它</span><br>    <span class="hljs-keyword">if</span> (dir.<span class="hljs-built_in">mkpath</span>(path)) <span class="hljs-built_in">qDebug</span>() &lt;&lt; <span class="hljs-string">&quot;Folder created successfully!&quot;</span>;<br><span class="hljs-keyword">else</span> <span class="hljs-built_in">qDebug</span>() &lt;&lt; <span class="hljs-string">&quot;Failed to create folder.&quot;</span>;<br>&#125; <span class="hljs-keyword">else</span> <span class="hljs-built_in">qDebug</span>() &lt;&lt; <span class="hljs-string">&quot;Folder already exists.&quot;</span>;<br></code></pre></td></tr></table></figure><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span>程序执行初，会在系统Documents目录创建<code>/OOPM/passports/</code>文件夹，文件指该路径下的users.bat文件<a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span>将密码转换为<code>QByteArray</code>，使用SHA-256算法生成哈希值，并将哈希值转换为十六进制字符串以便于存储和比较。<a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span>远程仓库地址：<a href="https://github.com/harkerhand/OOPM">harkerhand&#x2F;OOPM (github.com)</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span>自动文档生成工具（如 Doxygen、Javadoc 等）可以通过解析源代码中的注释自动生成文档，将代码逻辑与解释文本结合，易于阅读。<a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]>
    </content>
    <id>https://www.harkerhand.cn/24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/</id>
    <link href="https://www.harkerhand.cn/24%E6%9A%91%E6%9C%9F%E5%AD%A6%E6%A0%A1%E8%AF%BE%E8%AE%BE%E6%8A%A5%E5%91%8A/"/>
    <published>2024-09-10T06:00:54.000Z</published>
    <summary>
      <![CDATA[<h1 id="24暑期学校课设报告"><a href="#24暑期学校课设报告" class="headerlink" title="24暑期学校课设报告"></a>24暑期学校课设报告</h1><h2 id="一-、题目"><a href="#一-、题目"]]>
    </summary>
    <title>24暑期学校课设报告</title>
    <updated>2026-04-13T06:42:51.978Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Lua" scheme="https://www.harkerhand.cn/tags/Lua/"/>
    <category term="DoraSSR" scheme="https://www.harkerhand.cn/tags/DoraSSR/"/>
    <content>
      <![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="介绍2048游戏"><a href="#介绍2048游戏" class="headerlink" title="介绍2048游戏"></a>介绍2048游戏</h2><h3 id="背景与历史"><a href="#背景与历史" class="headerlink" title="背景与历史"></a>背景与历史</h3><p>2048游戏是由意大利开发者Gabriele Cirulli在2014年创建的简易益智游戏。值得注意的是，这款游戏是在他用JavaScript自学编程时开发的，仅用了一个周末的时间。</p><p>这款游戏最初的灵感来自于一个类似的游戏——1024，而1024本身也是基于另一个名为Threes的游戏。这种创新与简化使得2048在发布后迅速走红，吸引了全球玩家的注意，并在短时间内成为了一个网络现象。</p><h3 id="游戏规则"><a href="#游戏规则" class="headerlink" title="游戏规则"></a>游戏规则</h3><p>2048的游戏规则非常简单，玩家在一个4x4的网格中通过上下左右方向键滑动方块。</p><p>当两个相同数字的方块相遇时，它们会合并成一个新方块，并且数值相加（例如两个“2”合并成“4”）。</p><p>每次滑动时，所有方块会向玩家指定的方向移动，且会在空白位置随机生成一个新的方块，通常为“2”或“4”。</p><p>游戏的目标是在网格中创造一个数字为2048的方块。当玩家无法再移动方块且没有生成2048方块时，游戏结束。</p><h3 id="流行原因"><a href="#流行原因" class="headerlink" title="流行原因"></a>流行原因</h3><p>2048之所以迅速流行，原因在于它的极简设计和易于上手的玩法。这款游戏结合了策略性和运气成分，使得玩家在短时间内沉浸其中，享受逐步逼近2048的快感。游戏的开源性也促进了它的传播，众多开发者基于原版开发了各类衍生版本和改编游戏，进一步扩大了其影响力。</p><h2 id="文章目的"><a href="#文章目的" class="headerlink" title="文章目的"></a>文章目的</h2><p>本文将介绍您如何基于DoraSSR环境编写2048游戏的程序，以及其中的重要功能和技术实现。</p><h1 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h1><h2 id="编程语言"><a href="#编程语言" class="headerlink" title="编程语言"></a>编程语言</h2><p>如果你是一名游戏开发的新手，你可能对如何选择合适的编程语言感到困惑。别担心，Dora SSR 提供了多种语言选择，让你可以根据自己的兴趣和项目需求来挑选。</p><p>Dora SSR 支持多种编程语言，目前包括 Lua、Yuescript、Teal、TypeScript、TSX、Rust 等。每种语言都有其独特的特点和优势。</p><p>Lua 是一种轻量级的脚本语言，以其语言特性精简，高效和易学而闻名。如果你是编程新手，或者喜欢简单明了的代码，Lua 是一个很好的选择。</p><h2 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h2><ul><li><p>下载并运行<a href="https://github.com/ippclub/Dora-SSR/releases/latest">软件</a></p><ul><li><p>对于 Windows 用户，请确保您已安装 Visual Studio 2022 的 X86 Visual C++ 可再发行组件包（包含 MSVC 编译的程序所需运行时的 vc_redist.x86 补丁），以运行此应用程序。您可以从<a href="https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170">微软网站</a>下载。</p></li><li><p>在macOS上也可以通过Homebrew进行软件安装。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sh">brew tap ippclub/dora-ssr<br>brew install --cask dora-ssr<br></code></pre></td></tr></table></figure></li></ul></li><li><p>通过浏览器访问软件显示的服务器地址。</p></li><li><p>开始游戏开发。</p></li></ul><h1 id="程序架构设计"><a href="#程序架构设计" class="headerlink" title="程序架构设计"></a>程序架构设计</h1><h2 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结构"></a>整体结构</h2><ul><li><strong>模块划分</strong><br>该程序的整体架构采用了模块化设计。它通过引入多个模块（如<code>Scale</code>、<code>Audio</code>、<code>Set</code>等）来处理不同的功能。这种设计使得代码更加清晰且易于维护。主要模块包括：<ul><li><strong>主逻辑模块</strong>：处理游戏的初始化、主要逻辑控制以及事件响应。</li><li><strong>UI模块</strong>：负责绘制游戏界面，包括游戏板和方块。</li><li><strong>工具模块</strong>：提供一些常用的辅助函数，如遍历表格和查找元素。</li></ul></li><li><strong>数据结构</strong><br>游戏的核心数据结构是一个一维数组<code>data</code>，用于存储每个格子中的数字状态。每个数字方块在界面上对应一个由<code>DrawNode</code>绘制的方块对象，保存在<code>boxes</code>数组中。</li></ul><h2 id="关键模块与功能"><a href="#关键模块与功能" class="headerlink" title="关键模块与功能"></a>关键模块与功能</h2><ul><li><strong>输入处理</strong><br>程序通过键盘输入来捕捉玩家的方向操作，使用<code>slot</code>函数来监听用户按键事件，并调用相应的逻辑处理函数进行处理。</li><li><strong>游戏逻辑</strong><br>包括方块的生成、移动、合并及重新开始游戏的逻辑。通过<code>gameMove()</code>函数来处理方块的移动，并根据不同的方向（左、右、上、下）调用合并函数<code>merge(newRow)</code>。</li><li><strong>界面绘制</strong><br>使用<code>DrawNode</code>绘制方块，通过<code>Label</code>显示方块上的数字，并将它们添加到游戏主节点<code>root</code>中。</li></ul><h1 id="核心功能实现"><a href="#核心功能实现" class="headerlink" title="核心功能实现"></a>核心功能实现</h1><h2 id="游戏初始化"><a href="#游戏初始化" class="headerlink" title="游戏初始化"></a>游戏初始化</h2><ul><li><p>游戏通过<code>init()</code>函数进行初始化。在初始化过程中，程序会将数据表<code>data</code>中的值全部重置为0，并在随机位置生成一个初始的数字“2”。</p></li><li><p>同时，<code>init()</code>函数设置了键盘输入的监听器，允许玩家通过键盘控制方块的移动。</p><figure class="highlight lua"><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></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span><br><span class="hljs-keyword">for</span> i = <span class="hljs-number">1</span>, <span class="hljs-number">16</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br>empty:add(i)<br>data[i] = <span class="hljs-number">0</span><br><span class="hljs-keyword">end</span><br><br><span class="hljs-keyword">local</span> rand0 = empty:random_element()<br>data[rand0] = <span class="hljs-number">2</span><br>drawNumber()<br><br>root.keyboardEnabled = <span class="hljs-literal">true</span><br>root:slot(<span class="hljs-string">&quot;KeyDown&quot;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(keyName)</span></span><br><span class="hljs-keyword">if</span> MyMethod.findElement(Direction, keyName) ~= <span class="hljs-literal">nil</span> <span class="hljs-keyword">then</span><br>    direction = keyName<br>gameMove()<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;direction:&quot;</span>, keyName)<br><span class="hljs-keyword">else</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Unknown direction:&quot;</span>, keyName) <span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span>)<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure></li></ul><h2 id="方块的绘制"><a href="#方块的绘制" class="headerlink" title="方块的绘制"></a>方块的绘制</h2><ul><li><p><code>draw(x, y)</code>函数负责绘制单个方块。它使用<code>DrawNode</code>来创建方块的形状，并在其上添加一个<code>Label</code>用于显示数字。绘制时，方块会被定位到指定的坐标（<code>x</code>, <code>y</code>），并添加到主节点<code>root</code>中。</p><figure class="highlight lua"><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><code class="hljs lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span><span class="hljs-params">(x ,y)</span></span><br><span class="hljs-keyword">local</span> drawNode = DrawNode()<br>drawNode.position = Vec2(x, y)<br>drawNode:drawPolygon(verts, Color(<span class="hljs-number">0xFF072E06</span>), <span class="hljs-number">5</span>, Color(<span class="hljs-number">0xFF2C6AC7</span>))<br><span class="hljs-keyword">local</span> lable = Label(<span class="hljs-string">&#x27;sarasa-mono-sc-regular&#x27;</span>, <span class="hljs-number">80</span>)<br>lable.text = <span class="hljs-string">&#x27;&#x27;</span><br>drawNode:addChild(lable)<br>drawNode:addTo(root)<br><span class="hljs-keyword">return</span> drawNode<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure></li></ul><h2 id="方块的移动与合并"><a href="#方块的移动与合并" class="headerlink" title="方块的移动与合并"></a>方块的移动与合并</h2><ul><li><p>当用户按下某个方向键时，程序会通过<code>gameMove()</code>函数处理方块的移动。根据方向的不同，程序会将数据分成不同的行或列，并通过<code>merge(newRow)</code>函数来合并相同的数字。</p><figure class="highlight lua"><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></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gameMove</span><span class="hljs-params">()</span></span><br><span class="hljs-keyword">if</span> direction == <span class="hljs-string">&quot;Left&quot;</span> <span class="hljs-keyword">then</span><br><span class="hljs-keyword">for</span> i = <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br><span class="hljs-comment">-- i  + 0/1/2/3 * 4</span><br><span class="hljs-keyword">local</span> newRow = &#123;&#125;<br><span class="hljs-keyword">for</span> j = <span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br>        <span class="hljs-keyword">if</span> data[i + j * <span class="hljs-number">4</span>] ~= <span class="hljs-number">0</span> <span class="hljs-keyword">then</span><br>            <span class="hljs-built_in">table</span>.<span class="hljs-built_in">insert</span>(newRow, data[i + j * <span class="hljs-number">4</span>])<br>        <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">end</span><br><span class="hljs-keyword">while</span> #newRow &lt; <span class="hljs-number">4</span> <span class="hljs-keyword">do</span><br>        <span class="hljs-built_in">table</span>.<span class="hljs-built_in">insert</span>(newRow, <span class="hljs-number">0</span>)<br>    <span class="hljs-keyword">end</span><br><span class="hljs-keyword">local</span> finalRow = merge(newRow)<br><span class="hljs-keyword">for</span> j = <span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br>        data[i + <span class="hljs-number">4</span> * j] = finalRow[j + <span class="hljs-number">1</span>]<br>    <span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span><br><span class="hljs-keyword">elseif</span> direction == <span class="hljs-string">&quot;Right&quot;</span> <span class="hljs-keyword">then</span><br>...<br><span class="hljs-keyword">elseif</span> direction == <span class="hljs-string">&quot;Up&quot;</span> <span class="hljs-keyword">then</span><br>...<br><span class="hljs-keyword">else</span>  <span class="hljs-comment">-- Down</span><br>...<br><span class="hljs-keyword">end</span><br>drawNumber()<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure></li><li><p><code>merge(newRow)</code>函数负责在一行数据中将相同数字的方块合并，并返回合并后的新行数据。这个函数还确保了合并后的新行保持原有的顺序，并填充为长度为4的列表。</p><figure class="highlight lua"><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><code class="hljs lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">merge</span><span class="hljs-params">(newRow)</span></span><br><span class="hljs-keyword">local</span> merged = &#123;<span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>&#125;<br><span class="hljs-keyword">local</span> resultRow = &#123;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>&#125;<br><span class="hljs-keyword">for</span> j = <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br>        <span class="hljs-keyword">if</span> newRow[j] ~= <span class="hljs-number">0</span> <span class="hljs-keyword">then</span><br>          <span class="hljs-keyword">local</span> currentValue = newRow[j]<br>          <span class="hljs-keyword">local</span> nextIndex = j + <span class="hljs-number">1</span><br>          <span class="hljs-keyword">if</span> nextIndex &lt;= <span class="hljs-number">4</span> <span class="hljs-keyword">and</span> newRow[nextIndex] == currentValue <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> merged[nextIndex] <span class="hljs-keyword">then</span><br>            resultRow[j] = currentValue * <span class="hljs-number">2</span><br>            merged[nextIndex] = <span class="hljs-literal">true</span><br>            merged[j] = <span class="hljs-literal">true</span><br>            newRow[nextIndex] = <span class="hljs-number">0</span><br>          <span class="hljs-keyword">else</span> resultRow[j] = currentValue <span class="hljs-keyword">end</span><br>        <span class="hljs-keyword">end</span><br>  <span class="hljs-keyword">end</span><br><span class="hljs-keyword">local</span> finalRow = &#123;&#125;<br>  <span class="hljs-keyword">for</span> j = <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">do</span><br>    <span class="hljs-keyword">if</span> resultRow[j] ~= <span class="hljs-number">0</span> <span class="hljs-keyword">then</span><br>      <span class="hljs-built_in">table</span>.<span class="hljs-built_in">insert</span>(finalRow, resultRow[j])<br>    <span class="hljs-keyword">end</span><br>  <span class="hljs-keyword">end</span><br><span class="hljs-keyword">while</span> #finalRow &lt; <span class="hljs-number">4</span> <span class="hljs-keyword">do</span><br>    <span class="hljs-built_in">table</span>.<span class="hljs-built_in">insert</span>(finalRow, <span class="hljs-number">0</span>)<br>  <span class="hljs-keyword">end</span><br><span class="hljs-keyword">return</span> finalRow<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure></li></ul><h2 id="随机生成新方块"><a href="#随机生成新方块" class="headerlink" title="随机生成新方块"></a>随机生成新方块</h2><ul><li><p>在每次移动之后，程序会调用<code>drawNumber()</code>函数，在空余的位置上随机生成一个新的方块。这个新方块的初始值通常为“2”。</p><figure class="highlight lua"><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><code class="hljs lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawNumber</span><span class="hljs-params">()</span></span><br>empty:clear()<br>MyMethod.<span class="hljs-built_in">foreach</span>(data, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(index, value)</span></span><br><span class="hljs-keyword">if</span> (value == <span class="hljs-number">0</span>) <span class="hljs-keyword">then</span> empty:add(index) <span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span>)<br><span class="hljs-keyword">if</span> empty:size() == <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;over&quot;</span>) reStartGame = <span class="hljs-literal">true</span> <span class="hljs-keyword">end</span><br><span class="hljs-keyword">local</span> newRand = empty:random_element()<br>data[newRand] = <span class="hljs-number">2</span><br><br>MyMethod.<span class="hljs-built_in">foreach</span>(data, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(index, value)</span></span><br><span class="hljs-keyword">if</span> value ~= <span class="hljs-number">0</span> <span class="hljs-keyword">then</span><br>boxes[index].children.last.text = <span class="hljs-built_in">tostring</span>(value)<br><span class="hljs-keyword">else</span> boxes[index].children.last.text = <span class="hljs-string">&#x27;&#x27;</span> <span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span>)<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure></li></ul><h2 id="游戏结束处理"><a href="#游戏结束处理" class="headerlink" title="游戏结束处理"></a>游戏结束处理</h2><ul><li><p>当没有可移动的空格时，<code>empty:size() == 0</code>的判断条件会触发游戏结束逻辑，并通过<code>reStartGame</code>变量标识游戏状态。在游戏结束后，程序会播放一个音效，并重新初始化游戏。</p><figure class="highlight lua"><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><code class="hljs lua">threadLoop(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span><br><span class="hljs-keyword">if</span>(reStartGame == <span class="hljs-literal">true</span>) <span class="hljs-keyword">then</span><br>reStartGame = <span class="hljs-literal">false</span><br>Audio:play(<span class="hljs-string">&quot;sfx/game_over.wav&quot;</span>)<br>init()<br>root:perform(Scale(<span class="hljs-number">2</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>))<br><span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span>)<br></code></pre></td></tr></table></figure></li></ul><h1 id="优化与性能提升"><a href="#优化与性能提升" class="headerlink" title="优化与性能提升"></a>优化与性能提升</h1><h2 id="算法优化"><a href="#算法优化" class="headerlink" title="算法优化"></a>算法优化</h2><ul><li><strong>合并逻辑优化</strong><br><code>merge(newRow)</code>函数使用了一个布尔数组<code>merged</code>来跟踪哪些方块已经合并，避免重复合并。这个设计减少了不必要的计算，提高了算法的效率。</li></ul><h2 id="内存与速度优化"><a href="#内存与速度优化" class="headerlink" title="内存与速度优化"></a>内存与速度优化</h2><ul><li><strong>内存管理</strong><br>游戏中大量使用了局部变量（如<code>newRow</code>和<code>resultRow</code>），这些局部变量在函数执行完毕后自动释放，从而节省内存。</li><li><strong>减少重复操作</strong><br>在移动和合并过程中，代码尽量减少了对<code>data</code>数组的重复遍历，这有助于提升程序的整体速度。</li></ul><h2 id="用户体验优化"><a href="#用户体验优化" class="headerlink" title="用户体验优化"></a>用户体验优化</h2><ul><li><strong>动画效果</strong><br>虽然代码中目前未实现具体的动画效果，但可以通过增加方块移动、合并时的过渡动画（例如使用<code>Scale</code>来缩放方块），使得游戏体验更加流畅和有趣。</li><li><strong>声音反馈</strong><br>在游戏结束时播放音效，为玩家提供了即时的反馈，提高了游戏的沉浸感。</li></ul><h1 id="结论与未来改进"><a href="#结论与未来改进" class="headerlink" title="结论与未来改进"></a>结论与未来改进</h1><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>本文详细介绍了如何从零开始编写一个2048游戏程序，并讲解了程序的整体架构设计、核心功能实现、以及优化策略。通过模块化设计和合理的算法优化，这个程序不仅具备较高的可维护性，还能在多种平台上流畅运行。</li><li>在开发过程中，我们通过单元测试和集成测试确保了程序的稳定性，并在性能和用户体验方面进行了优化，使得游戏更加流畅和有趣。</li></ul><h2 id="未来改进方向"><a href="#未来改进方向" class="headerlink" title="未来改进方向"></a>未来改进方向</h2><ul><li><strong>增加难度模式</strong><br>可以在未来的版本中增加不同的难度模式，例如更大的网格、更高的初始数字或增加障碍物，以增强游戏的挑战性。</li><li><strong>添加AI对手</strong><br>可以考虑为游戏添加一个AI对手，让玩家与AI竞赛，看谁能先达到2048。</li><li><strong>改进用户界面和动画效果</strong><br>在未来的版本中，可以进一步改进游戏的用户界面，使之更具吸引力。同时，增加方块移动和合并时的动画效果，提高游戏的视觉体验。</li><li><strong>增加排行榜功能</strong><br>通过添加在线或本地的排行榜功能，玩家可以与朋友或全球的玩家一较高下，增加游戏的社交性和竞争性。</li></ul><h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1><h2 id="MyMethod-lua"><a href="#MyMethod-lua" class="headerlink" title="MyMethod.lua"></a>MyMethod.lua</h2><figure class="highlight lua"><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><code class="hljs lua"><span class="hljs-keyword">local</span> M = &#123;&#125;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">M.foreach</span><span class="hljs-params">(tbl, func)</span></span><br>    <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> <span class="hljs-built_in">pairs</span>(tbl) <span class="hljs-keyword">do</span> func(key, value) <span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">M.findElement</span><span class="hljs-params">(tbl, element)</span></span><br>    <span class="hljs-keyword">for</span> i, value <span class="hljs-keyword">in</span> <span class="hljs-built_in">ipairs</span>(tbl) <span class="hljs-keyword">do</span><br>        <span class="hljs-keyword">if</span> value == element <span class="hljs-keyword">then</span> <span class="hljs-keyword">return</span> i <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span><br><span class="hljs-keyword">end</span><br><span class="hljs-keyword">return</span> M<br></code></pre></td></tr></table></figure><h2 id="Set-lua"><a href="#Set-lua" class="headerlink" title="Set.lua"></a>Set.lua</h2><figure class="highlight lua"><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></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-keyword">local</span> Set = &#123;&#125;<br>Set.<span class="hljs-built_in">__index</span> = Set<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set.new</span><span class="hljs-params">()</span></span><br>    <span class="hljs-keyword">return</span> <span class="hljs-built_in">setmetatable</span>(&#123; _items = &#123;&#125; &#125;, Set)<br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:add</span><span class="hljs-params">(item)</span></span><br>    <span class="hljs-built_in">self</span>._items[item] = <span class="hljs-literal">true</span><br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:remove</span><span class="hljs-params">(item)</span></span><br>    <span class="hljs-built_in">self</span>._items[item] = <span class="hljs-literal">nil</span><br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:contains</span><span class="hljs-params">(item)</span></span><br>    <span class="hljs-keyword">return</span> <span class="hljs-built_in">self</span>._items[item] ~= <span class="hljs-literal">nil</span><br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:size</span><span class="hljs-params">()</span></span><br>    <span class="hljs-keyword">local</span> count = <span class="hljs-number">0</span><br>    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">pairs</span>(<span class="hljs-built_in">self</span>._items) <span class="hljs-keyword">do</span><br>        count = count + <span class="hljs-number">1</span><br>    <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">return</span> count<br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:iterate</span><span class="hljs-params">()</span></span><br>    <span class="hljs-keyword">local</span> items = &#123;&#125;<br>    <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> <span class="hljs-built_in">pairs</span>(<span class="hljs-built_in">self</span>._items) <span class="hljs-keyword">do</span><br>        <span class="hljs-built_in">table</span>.<span class="hljs-built_in">insert</span>(items, item)<br>    <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">return</span> items<br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:print</span><span class="hljs-params">()</span></span><br><span class="hljs-keyword">for</span> _, item <span class="hljs-keyword">in</span> <span class="hljs-built_in">ipairs</span>(<span class="hljs-built_in">self</span>:iterate()) <span class="hljs-keyword">do</span><br>    <span class="hljs-built_in">print</span>(item)<br><span class="hljs-keyword">end</span><br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:random_element</span><span class="hljs-params">()</span></span><br>    <span class="hljs-keyword">local</span> items = <span class="hljs-built_in">self</span>:iterate()<br>    <span class="hljs-keyword">if</span> #items == <span class="hljs-number">0</span> <span class="hljs-keyword">then</span><br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span><br>    <span class="hljs-keyword">end</span><br>    <span class="hljs-keyword">local</span> index = <span class="hljs-built_in">math</span>.<span class="hljs-built_in">random</span>(<span class="hljs-number">1</span>, #items)<br>    <span class="hljs-keyword">return</span> items[index]<br><span class="hljs-keyword">end</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Set:clear</span><span class="hljs-params">()</span></span><br>    <span class="hljs-built_in">self</span>._items = &#123;&#125;<br><span class="hljs-keyword">end</span><br><span class="hljs-keyword">return</span> Set<br></code></pre></td></tr></table></figure><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><ul><li><a href="https://harker.lanzouw.com/iDjBJ276m0ha">本文工程压缩包</a></li><li><a href="https://atom-l.github.io/lua5.4-manual-zh/1.html">Lua 5.4 中文参考手册</a></li><li><a href="https://dora-ssr.net/zh-Hans/docs/api/intro">Dora SSR API 参考手册</a></li></ul>]]>
    </content>
    <id>https://www.harkerhand.cn/%E4%BD%BF%E7%94%A8DoraSSR%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%912048%E6%B8%B8%E6%88%8FDemo/</id>
    <link href="https://www.harkerhand.cn/%E4%BD%BF%E7%94%A8DoraSSR%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%912048%E6%B8%B8%E6%88%8FDemo/"/>
    <published>2024-08-11T13:32:15.000Z</published>
    <summary>
      <![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="介绍2048游戏"><a href="#介绍2048游戏" class="headerlink"]]>
    </summary>
    <title>使用DoraSSR引擎开发2048游戏Demo</title>
    <updated>2026-04-13T06:42:52.014Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Blog" scheme="https://www.harkerhand.cn/tags/Blog/"/>
    <content>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>个人开设网站已经三年有余了，从最开始的本地部署、Gitee部署到现在的Github + 域名部署，不断遇到各种问题，最大的问题是各种服务的链接各不相同，难以统一记忆与管理，遂需要一个域名，通过二级转发来解决这些问题。</p><p>前几日听俱乐部的小伙伴说首年的域名都很便宜，那么 <strong>域名，启动！</strong></p><h1 id="平台选择"><a href="#平台选择" class="headerlink" title="平台选择"></a>平台选择</h1>    <div class="fold">      <div class="fold-title fold-info collapsed" data-toggle="collapse" href="#collapse-f40a3fac" role="button" aria-expanded="false" aria-controls="collapse-f40a3fac">        <div class="fold-arrow">▶</div>各种平台      </div>      <div class="fold-collapse collapse" id="collapse-f40a3fac">        <div class="fold-content">          <div class="note note-success">            <p><a href="https://cloud.tencent.com/">腾讯云 产业智变·云启未来 - 腾讯 (tencent.com)</a></p>          </div><div class="note note-success">            <p><a href="https://cn.aliyun.com/">阿里云-计算，为了无法计算的价值 (aliyun.com)</a></p>          </div>        </div>      </div>    </div><p><em>不一一列举了，有很多</em></p><p>我选择了腾讯云，国内使用体验较好。<span class="label label-primary">.online</span>的顶级域名首年只需要不到10元。</p><p>购买后及时完成认证，备案与否自由选择。</p><h1 id="配置DNS解析"><a href="#配置DNS解析" class="headerlink" title="配置DNS解析"></a>配置DNS解析</h1><p>可配置值有一下几种</p><table><thead><tr><th align="center">主机记录</th><th align="center">记录类型</th><th align="center">线路类型</th><th align="center">记录值</th><th align="center">权重</th><th align="center">TTL</th><th align="center">备注</th></tr></thead></table><h2 id="主机记录"><a href="#主机记录" class="headerlink" title="主机记录"></a>主机记录</h2><p>即二级域名，比如<code>www</code>.baidu.com与<code>pan</code>.baidu.com的区别就是前缀的二级域名，这样可以使一个域名被多个服务使用。</p><h2 id="记录类型"><a href="#记录类型" class="headerlink" title="记录类型"></a>记录类型</h2><p>常用的只有以下三种</p><ul><li><span class="label label-primary">A</span> ：指向一个IPv4地址</li><li><span class="label label-primary">CNAME</span> ：指向一个域名</li><li><span class="label label-primary">AAAA</span> ：指向一个IPv6地址</li></ul><h2 id="线路类型"><a href="#线路类型" class="headerlink" title="线路类型"></a>线路类型</h2><p>与转发路径与转发服务器偏好有关，一般默认即可。</p><h2 id="记录值"><a href="#记录值" class="headerlink" title="记录值"></a>记录值</h2><p><code>主机记录</code>.<code>域名</code>指向的地址，类型由<code>记录类型</code>决定。</p><h2 id="权重"><a href="#权重" class="headerlink" title="权重"></a>权重</h2><p>范围1-100，数字越大优先级越高。</p><h2 id="TTL"><a href="#TTL" class="headerlink" title="TTL"></a>TTL</h2><p>生存时间，一般设置为600。</p><h2 id="备注"><a href="#备注" class="headerlink" title="备注"></a>备注</h2><p>字面意思，就是备注，似乎没有字数限制，也就是说你可以在这里发癫！</p><h2 id="本人DNS备份"><a href="#本人DNS备份" class="headerlink" title="本人DNS备份"></a>本人DNS备份</h2><table><thead><tr><th align="center">主机记录</th><th align="center">记录类型</th><th align="center">线路类型</th><th align="center">记录值</th><th align="center">权重</th><th align="center">TTL</th><th align="center">备注</th></tr></thead><tbody><tr><td align="center">@</td><td align="center">CNAME</td><td align="center">默认</td><td align="center">harkerhand.github.io.</td><td align="center">99</td><td align="center">600</td><td align="center"></td></tr><tr><td align="center">www</td><td align="center">CNAME</td><td align="center">默认</td><td align="center">harkerhand.github.io.</td><td align="center">100</td><td align="center">600</td><td align="center"></td></tr><tr><td align="center">waline</td><td align="center">CNAME</td><td align="center">默认</td><td align="center">cname.vercel-dns.com.</td><td align="center">3</td><td align="center">600</td><td align="center">waline评论</td></tr><tr><td align="center">image</td><td align="center">CNAME</td><td align="center">默认</td><td align="center">telegraph-image-bhq.pages.dev.</td><td align="center">4</td><td align="center">600</td><td align="center">tele图床</td></tr><tr><td align="center"><code>Bing认证</code></td><td align="center">CNAME</td><td align="center">默认</td><td align="center">verify.bing.com.</td><td align="center">1</td><td align="center">600</td><td align="center"></td></tr></tbody></table><p><a href="https://www.harkerhand.online/">Where Is Mountain</a></p><p><a href="https://waline.harkerhand.online/">Waline Example</a></p><p><a href="https://image.harkerhand.online/">Telegraph-Image|免费图床</a></p><p><em>所有服务均无内容审查，请勿传播非法内容</em></p><h1 id="一些问题的解决"><a href="#一些问题的解决" class="headerlink" title="一些问题的解决"></a>一些问题的解决</h1><h2 id="waline评论"><a href="#waline评论" class="headerlink" title="waline评论"></a>waline评论</h2><p>服务运行在Vercel，其域名<code>vercel.app</code>在国内无法访问，所以在前文中没有使用这个评论服务。</p><p>然而，通过自己的域名对其服务进行转发，即可通过DNS服务商来绕过流量屏蔽，大大提高了访问速度。</p><h2 id="基于Telegraph的图床"><a href="#基于Telegraph的图床" class="headerlink" title="基于Telegraph的图床"></a>基于Telegraph的图床</h2><p>服务运行在CloudFlare，国内虽可以访问，但速度并不理想，同理可使用域名转发进行加速。</p>]]>
    </content>
    <id>https://www.harkerhand.cn/%E7%94%B3%E8%AF%B7%E5%9F%9F%E5%90%8D%E7%9B%B8%E5%85%B3/</id>
    <link href="https://www.harkerhand.cn/%E7%94%B3%E8%AF%B7%E5%9F%9F%E5%90%8D%E7%9B%B8%E5%85%B3/"/>
    <published>2024-04-24T06:44:03.000Z</published>
    <summary>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>个人开设网站已经三年有余了，从最开始的本地部署、Gitee部署到现在的Github +]]>
    </summary>
    <title>申请域名相关</title>
    <updated>2026-04-13T06:42:52.038Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Blog" scheme="https://www.harkerhand.cn/tags/Blog/"/>
    <content>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>昨晚偶然打开了 package.json，发现 hexo 版本为 6.3.0，遂决定升至最新 v7.1.1。</p><h1 id="遭遇错误"><a href="#遭遇错误" class="headerlink" title="遭遇错误"></a>遭遇错误</h1><p>执行报错</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm update hexo-cli -g<br></code></pre></td></tr></table></figure><blockquote><hr><p>重启解决 99% 的问题，剩下 1% 用重装</p><hr></blockquote><p>秉持这个思想，我选择重装</p><h1 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h1><h2 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h2><p>备份旧版本的文章，格式，Hexo及Fluid的配置文件</p><h2 id="卸载"><a href="#卸载" class="headerlink" title="卸载"></a>卸载</h2><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm uninstall hexo-cli -g<br></code></pre></td></tr></table></figure><p>并进入 C:\Users\UserName\AppData\Roaming\npm 路径检查卸载是否彻底</p><h2 id="安装本体"><a href="#安装本体" class="headerlink" title="安装本体"></a>安装本体</h2><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo -g<br></code></pre></td></tr></table></figure><p>并进入 C:\Users\HarkerHand\AppData\Roaming\npm\node_modules\hexo\package.json 查找 <code>version</code> 字段，版本为 v7.1.1 即为安装成功</p><p>Git Bash cd 进入博客文件夹，需要清空</p><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">hexo init<br></code></pre></td></tr></table></figure><h2 id="模块及主题恢复"><a href="#模块及主题恢复" class="headerlink" title="模块及主题恢复"></a>模块及主题恢复</h2><p><em>以下在 init 的文件夹中操作</em></p><ul><li><p><a href="https://github.com/xcodebuild/hexo-asset-image">hexo-asset-image</a> </p><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-asset-image --save<br></code></pre></td></tr></table></figure><p>在 <code>_config.yml</code> 中设置 <code>post_asset_folder: true</code></p></li><li><p><a href="https://github.com/hexojs/hexo-deployer-git">hexo-deployer-git</a></p><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-deployer-git --save<br></code></pre></td></tr></table></figure></li><li><p><a href="https://github.com/hexojs/hexo-server">hexo-server</a></p><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-server --save<br></code></pre></td></tr></table></figure></li><li><p><a href="https://github.com/fluid-dev/hexo-theme-fluid">hexo-theme-fluid</a></p><p>执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-theme-fluid --save<br></code></pre></td></tr></table></figure></li></ul><h1 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h1><p>在更新主题时原配置被完全覆盖，提醒大家做好备份</p><p><strong>数据无价！！！</strong></p>]]>
    </content>
    <id>https://www.harkerhand.cn/%E8%BF%9B%E8%A1%8CHexo%E5%A4%A7%E7%89%88%E6%9C%AC%E6%9B%B4%E6%96%B0/</id>
    <link href="https://www.harkerhand.cn/%E8%BF%9B%E8%A1%8CHexo%E5%A4%A7%E7%89%88%E6%9C%AC%E6%9B%B4%E6%96%B0/"/>
    <published>2024-03-03T17:15:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>昨晚偶然打开了 package.json，发现 hexo 版本为 6.3.0，遂决定升至最新 v7.1.1。</p>
<h1]]>
    </summary>
    <title>进行Hexo大版本更新</title>
    <updated>2026-04-13T06:42:52.038Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Blog" scheme="https://www.harkerhand.cn/tags/Blog/"/>
    <category term="图床" scheme="https://www.harkerhand.cn/tags/%E5%9B%BE%E5%BA%8A/"/>
    <content>
      <![CDATA[<h1 id="提醒"><a href="#提醒" class="headerlink" title="提醒"></a>提醒</h1><p>本文封面及所涉及的所有图片均来自云端图床，故随时存在无法访问的风险</p><h1 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h1><h2 id="Fork-项目"><a href="#Fork-项目" class="headerlink" title="Fork 项目"></a>Fork 项目</h2><p>将此项目Fork进自己的仓库 <a href="https://github.com/cf-pages/Telegraph-Image">Telegraph-Image</a></p><h2 id="部署CloudFlare"><a href="#部署CloudFlare" class="headerlink" title="部署CloudFlare"></a>部署CloudFlare</h2><p>使用 Pages 服务部署该仓库</p><p>创建KV : img_url</p><p>命名空间 img_url &#x3D; img_url</p><p>环境变量 BASIC_USER BASIC_PASS 后台管理用户名，密码</p><p>部署后得到前端网址</p><p>后缀 &#x2F;admin 进入后台管理</p><h1 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h1><p>公开使用：</p><p><a href="https://telegraph-image-bhq.pages.dev/">Pic-Url</a> <em>源链</em></p><p><a href="https://image.harkerhand.online/">Image</a> <em>转发链</em></p><table><thead><tr><th align="center"><img src="https://image.harkerhand.online/file/d75a74b5acb3470accf7c.jpg"  /><strong>Knife is short, I use python</strong></th><th align="center"><img src="https://image.harkerhand.online/file/1e6d8b6f0f776fb6d77c2.jpg" style="zoom:90%;" /></th></tr></thead></table>]]>
    </content>
    <id>https://www.harkerhand.cn/CloudFlare-telegraph%E5%9B%BE%E5%BA%8A%E9%83%A8%E7%BD%B2/</id>
    <link href="https://www.harkerhand.cn/CloudFlare-telegraph%E5%9B%BE%E5%BA%8A%E9%83%A8%E7%BD%B2/"/>
    <published>2024-03-03T10:59:43.000Z</published>
    <summary>
      <![CDATA[<h1 id="提醒"><a href="#提醒" class="headerlink" title="提醒"></a>提醒</h1><p>本文封面及所涉及的所有图片均来自云端图床，故随时存在无法访问的风险</p>
<h1 id="部署"><a href="#部署"]]>
    </summary>
    <title>CloudFlare+telegraph图床部署</title>
    <updated>2026-04-13T06:42:51.993Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="Blog" scheme="https://www.harkerhand.cn/tags/Blog/"/>
    <content>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>寒假期间，好友章隐基于<a href="https://cn.wordpress.org/">WordPress</a>搭建了自己的博客<a href="http://117.72.35.48/">彰隐的个人博客</a>，包含有评论系统，十分有趣，故决定给自己的站点也增加这一服务。</p><p>本站基于<a href="https://hexo.io/zh-cn/index.html">Hexo</a>搭建，在GitHub Pages和Gitee Pages双端部署，使用<a href="https://fluid-dev.github.io/hexo-fluid-docs/">Fluid</a>主题进行美化，功能丰富，内置了许多主题插件使用。</p><p>打开 node_modules&#x2F;hexo-theme-fluid&#x2F;_config.yml 并查找 comment 字段，可以看到</p><figure class="highlight yaml"><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><code class="hljs yaml"><span class="hljs-comment"># 评论插件</span><br><span class="hljs-comment"># Comment plugin</span><br><span class="hljs-attr">comments:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-comment"># 指定的插件，需要同时设置对应插件的必要参数</span><br>  <span class="hljs-comment"># Options: utterances | disqus | gitalk | valine | waline | changyan | livere | remark42 | twikoo | cusdis | giscus | discuss</span><br>  <span class="hljs-attr">type:</span> <span class="hljs-string">twikoo</span><br></code></pre></td></tr></table></figure><p>将<code>enable</code>字段改为<code>true</code>并将<code>type</code>字段改为对应的评论服务即可</p><h1 id="评论系统的挑选"><a href="#评论系统的挑选" class="headerlink" title="评论系统的挑选"></a>评论系统的挑选</h1><h2 id="Valine"><a href="#Valine" class="headerlink" title="Valine"></a>Valine</h2><p><a href="https://valine.js.org/">Valine</a>诞生于2017年8月7日，是一款基于<a href="https://leancloud.cn/">LeanCloud</a>的快速、简洁且高效的无后端评论系统。</p><p>其无后端的性质导致了评论难以管理，故放弃</p><h2 id="Waline"><a href="#Waline" class="headerlink" title="Waline"></a>Waline</h2><p>显然，<a href="https://waline.js.org/">Waline</a>是对Valine的一个继承，它具有后端。</p><p>推荐的后端部署渠道有Vercel，CloudeBase等，在我的账号与设备上出现了不可预知的错误，另外vecel.app域名在国内无法正常访问，会出现Failed to fetch错误。</p><p>较为繁琐但免费高效的Netlify部署，以我无法注册账号宣告失败。</p><p>很可惜，Waline也无法使用。</p><h2 id="Twikoo"><a href="#Twikoo" class="headerlink" title="Twikoo"></a>Twikoo</h2><p>这便是本文的主角，<a href="https://twikoo.js.org/">Twikoo | 一个简洁、安全、免费的静态网站评论系统</a>。</p><p>我选择了 <a href="https://www.hugging-face.org/">Hugging Face</a> 部署，与Netlify类似，完全免费并且在国内拥有不错的访问速度，其数据库位于 <a href="https://www.mongodb.com/zh-cn">MongoDB</a> ，可以使用 Google 账号快速登陆，不存在注册账号问题，并且同样免费。</p><p>获取环境ID后填入_config.yml中的</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">twikoo:</span><br>  <span class="hljs-attr">envID:</span> <span class="hljs-string">Here!!!</span><br></code></pre></td></tr></table></figure><h1 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h1><p><img src="/./%E5%9F%BA%E4%BA%8ETwikoo%E7%9A%84%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E6%90%AD%E5%BB%BA/comment.png" alt="comment"></p><p>本文下方的评论区同理，如果你使用的是移动设备，布局会略有不同</p>]]>
    </content>
    <id>https://www.harkerhand.cn/%E5%9F%BA%E4%BA%8ETwikoo%E7%9A%84%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E6%90%AD%E5%BB%BA/</id>
    <link href="https://www.harkerhand.cn/%E5%9F%BA%E4%BA%8ETwikoo%E7%9A%84%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E6%90%AD%E5%BB%BA/"/>
    <published>2024-03-03T07:06:11.000Z</published>
    <summary>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>寒假期间，好友章隐基于<a]]>
    </summary>
    <title>基于Twikoo的博客评论搭建</title>
    <updated>2026-04-13T06:42:52.021Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="git" scheme="https://www.harkerhand.cn/tags/git/"/>
    <category term="github" scheme="https://www.harkerhand.cn/tags/github/"/>
    <content>
      <![CDATA[<h1 id="前情提要"><a href="#前情提要" class="headerlink" title="前情提要"></a>前情提要</h1><p>本人正在进行C++课程的学习，每次作业组成为若干编程题目，每个题目需要包括测试文件.cpp，类的声明头文件.h，类的实现文件.cpp</p><p>大致的文件树在此列出</p><ul><li>001<ul><li>exp01<ul><li>ex1.cpp</li><li>Complex.h</li><li>Complex.cpp</li></ul></li><li>exp02</li><li>…</li></ul></li><li>002<ul><li>exp01</li><li>…</li></ul></li><li>…</li></ul><p><strong>作业提交格式 :</strong> 将上述文件全部打包为.zip文件发送至指定邮箱</p><p><strong>需求 :</strong> 排除本地调试生成的.exe文件，自动打包</p><p><em>注 : 作者使用git来进行本地与<a href="https://gitee.com/harkerhand/homework2">云端仓库</a>的链接，.gitignore文件包含了*.exe</em></p><h1 id="Github-Actions-简介"><a href="#Github-Actions-简介" class="headerlink" title="Github Actions 简介"></a>Github Actions 简介</h1><p>GitHub Actions 是一种持续集成和持续交付 (CI&#x2F;CD) 平台，可用于自动执行生成、测试和部署管道。 您可以创建工作流程来构建和测试存储库的每个拉取请求，或将合并的拉取请求部署到生产环境。</p><p>GitHub Actions 不仅仅是 DevOps，还允许您在存储库中发生其他事件时运行工作流程。 例如，您可以运行工作流程，以便在有人在您的存储库中创建新问题时自动添加相应的标签。</p><p>GitHub 提供 Linux、Windows 和 macOS 虚拟机来运行工作流程，或者您可以在自己的数据中心或云基础架构中托管自己的自托管运行器。</p><h1 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h1><ul><li>创建GitHub仓库</li><li>在 <strong>Settings–Actions–General</strong> 中更改 <strong>Actions permissions</strong> 为 <strong>Allow all actions and reusable workflows</strong>，点击 <strong>Save</strong> 保存</li><li>更改相同路径下的 <strong>Workflow permissions</strong> 为 <strong>Read and write permissions</strong>，并勾选 <strong>Allow GitHub Actions to create and approve pull requests</strong>，点击 <strong>Save</strong> 保存</li></ul><h1 id="编写Actions文件"><a href="#编写Actions文件" class="headerlink" title="编写Actions文件"></a>编写Actions文件</h1><p>在Actions选项卡中，选择 <strong>set up a workflow yourself</strong></p><p>进入编辑界面，文件路径为 &#x2F;.github&#x2F;workflows&#x2F;main.yml，文件名可自定义</p><h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><figure class="highlight yaml"><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><code class="hljs yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">zip</span><br><span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]<br><span class="hljs-attr">jobs:</span><br>  <span class="hljs-attr">build:</span><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br>    <span class="hljs-attr">steps:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v1</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">zip</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        for file in ./*</span><br><span class="hljs-string">        do</span><br><span class="hljs-string">          zip -r ./$&#123;file&#125;/$&#123;file&#125;.zip $&#123;file&#125;</span><br><span class="hljs-string">        done</span><br><span class="hljs-string"></span>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">list</span> <span class="hljs-string">files</span><br>      <span class="hljs-attr">run:</span><br>        <span class="hljs-string">ls</span> <span class="hljs-string">-R</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">commit</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        git config --global user.name &quot;harkerhand&quot;</span><br><span class="hljs-string">        git config --global user.email &quot;harkerhand@outlook.com&quot;</span><br><span class="hljs-string">        git add .</span><br><span class="hljs-string">        git commit -m &quot;zip&quot; -a</span><br><span class="hljs-string"></span>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">push</span><br>      <span class="hljs-attr">uses:</span> <span class="hljs-string">ad-m/github-push-action@master</span><br>      <span class="hljs-attr">with:</span><br>        <span class="hljs-attr">github_token:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">&#125;&#125;</span><br></code></pre></td></tr></table></figure><ul><li>name 为流程名</li><li>on 代表触发条件</li><li>jobs 为流程动作</li><li>build 为子流程名，可以有多个</li><li>runs-on 指定了脚本运行的环境，这是一个枚举类型</li><li>steps 为具体流程，数组类型</li><li>uses 使用打包好的流程，这里是从远程仓库拉取至虚拟机</li><li>name 为步骤名，可省略</li><li>run 内为执行命令，多行脚本需要添加 |</li><li>使用shell命令打包并列出文件（便于后期维护）</li><li>在push之前设置虚拟机的git用户名和邮箱</li><li>uses 使用打包好的push流程</li></ul><p><em>Tips : 需要在个人设置中生成名为 GITHUB_TOKEN 的 token</em></p><h1 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h1><p>直接在GitHub更改.yml文件即被视为push，触发Actions</p><p>转到Actions选项卡，出现类似<img src="/./%E6%B5%85%E5%B0%9DGithub-Actions/actions_result.png" alt="actions result"></p><p>即为成功，库文件夹中出现对应.zip文件</p><h1 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h1><p><a href="https://docs.github.com/zh/actions">GitHub Actions 文档 - GitHub 文档</a></p>]]>
    </content>
    <id>https://www.harkerhand.cn/%E6%B5%85%E5%B0%9DGithub-Actions/</id>
    <link href="https://www.harkerhand.cn/%E6%B5%85%E5%B0%9DGithub-Actions/"/>
    <published>2024-03-02T09:03:45.000Z</published>
    <summary>
      <![CDATA[<h1 id="前情提要"><a href="#前情提要" class="headerlink"]]>
    </summary>
    <title>浅尝Github Actions</title>
    <updated>2026-04-13T06:42:52.034Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="OpenJudge" scheme="https://www.harkerhand.cn/categories/OpenJudge/"/>
    <category term="C++" scheme="https://www.harkerhand.cn/tags/C/"/>
    <category term="OpenJudge" scheme="https://www.harkerhand.cn/tags/OpenJudge/"/>
    <content>
      <![CDATA[<h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><blockquote><p><strong>描述</strong>  马在中国象棋以日字形规则移动。</p><p>请编写一段程序，给定n*m大小的棋盘，以及马的初始位置(x，y)，要求不能重复经过棋盘上的同一个点，计算马可以有多少途径遍历棋盘上的所有点。</p><p><strong>输入</strong>  第一行为整数T(T &lt; 10)，表示测试数据组数。<br>每一组测试数据包含一行，为四个整数，分别为棋盘的大小以及初始位置坐标n,m,x,y。(0&lt;&#x3D;x&lt;&#x3D;n-1,0&lt;&#x3D;y&lt;&#x3D;m-1, m &lt; 10, n &lt; 10)</p><p><strong>输出</strong>  每组测试数据包含一行，为一个整数，表示马能遍历棋盘的途径总数，0为无法遍历一次。</p><p><strong>样例输入</strong></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><code class="hljs text">1<br>5 4 0 0<br></code></pre></td></tr></table></figure><p><strong>样例输出</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">32<br></code></pre></td></tr></table></figure></blockquote><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><ol><li>有八步走法, 列出来</li><li>数组存储走过的状态</li><li>判断下一步能不能走, 跳到下一步</li><li>总步数为棋盘面积时, 全部走完, 计数器加一</li></ol><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><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><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;cstring&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-type">int</span> step[<span class="hljs-number">8</span>][<span class="hljs-number">2</span>] = &#123;&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;, &#123;<span class="hljs-number">2</span>, <span class="hljs-number">1</span>&#125;, &#123;<span class="hljs-number">1</span>, <span class="hljs-number">-2</span>&#125;, &#123;<span class="hljs-number">2</span>, <span class="hljs-number">-1</span>&#125;, &#123;<span class="hljs-number">-1</span>, <span class="hljs-number">2</span>&#125;, &#123;<span class="hljs-number">-2</span>, <span class="hljs-number">1</span>&#125;, &#123;<span class="hljs-number">-1</span>, <span class="hljs-number">-2</span>&#125;, &#123;<span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>&#125;&#125;;<br><span class="hljs-comment">// 写出所有可能的走法</span><br><span class="hljs-type">int</span> whe[<span class="hljs-number">11</span>][<span class="hljs-number">11</span>]; <span class="hljs-comment">// 判断</span><br><br><span class="hljs-type">int</span> n, m;        <span class="hljs-comment">// 棋盘大小</span><br><span class="hljs-type">int</span> map[<span class="hljs-number">11</span>][<span class="hljs-number">11</span>]; <span class="hljs-comment">// 棋盘</span><br><br><span class="hljs-type">int</span> count; <span class="hljs-comment">// 计数器</span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">dfs</span><span class="hljs-params">(<span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y, <span class="hljs-type">int</span> steps)</span></span><br><span class="hljs-function"></span>&#123;<br><br>    <span class="hljs-type">int</span> i, nx, ny;<br>    <span class="hljs-keyword">if</span> (steps == n * m)<br>    &#123;<br>        count++;<br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">8</span>; i++)<br>    &#123;<br>        nx = x + step[i][<span class="hljs-number">0</span>]; <span class="hljs-comment">// 判断能否走这一步 </span><br>        ny = y + step[i][<span class="hljs-number">1</span>];<br>        <span class="hljs-keyword">if</span> (nx &lt; n &amp;&amp; nx &gt;= <span class="hljs-number">0</span> &amp;&amp; ny &lt; m &amp;&amp; ny &gt;= <span class="hljs-number">0</span> &amp;&amp; !whe[nx][ny])<br>        &#123;<br>            whe[nx][ny] = <span class="hljs-number">1</span>;<br>            <span class="hljs-built_in">dfs</span>(nx, ny, steps + <span class="hljs-number">1</span>);<br>            whe[nx][ny] = <span class="hljs-number">0</span>; <span class="hljs-comment">// 检测一次后重置</span><br>        &#125;<br>    &#125;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-type">int</span> x, y;<br>    <span class="hljs-type">int</span> k;<br>    cin &gt;&gt; k;<br>    <span class="hljs-keyword">while</span> (k--)<br>    &#123;<br>        count = <span class="hljs-number">0</span>;<br>        <span class="hljs-built_in">memset</span>(whe, <span class="hljs-number">0</span>, <span class="hljs-built_in">sizeof</span>(whe));<br>        cin &gt;&gt; n &gt;&gt; m &gt;&gt; x &gt;&gt; y;<br>        whe[x][y] = <span class="hljs-number">1</span>; <span class="hljs-comment">// 初始坐标</span><br>        <span class="hljs-built_in">dfs</span>(x, y, <span class="hljs-number">1</span>);<br>        cout &lt;&lt; count &lt;&lt; endl;<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.harkerhand.cn/OJ/8465/</id>
    <link href="https://www.harkerhand.cn/OJ/8465/"/>
    <published>2023-03-15T15:23:56.000Z</published>
    <summary>
      <![CDATA[<h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><blockquote>
<p><strong>描述</strong>]]>
    </summary>
    <title>8465.马走日</title>
    <updated>2026-04-13T06:42:52.004Z</updated>
  </entry>
  <entry>
    <author>
      <name>harkerhand</name>
    </author>
    <category term="经验分享" scheme="https://www.harkerhand.cn/categories/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    <category term="C++" scheme="https://www.harkerhand.cn/tags/C/"/>
    <content>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>下文代码中会用到一些函数</p><p>在此处做出声明</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><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">swap01</span><span class="hljs-params">(<span class="hljs-type">int</span> *a, <span class="hljs-type">int</span> *b)</span> <span class="hljs-comment">// 传入地址</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-type">int</span> temp = *a; <span class="hljs-comment">// *解引用</span><br>    *a = *b;<br>    *b = temp;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">bubble</span><span class="hljs-params">(<span class="hljs-type">int</span> *arr, <span class="hljs-type">int</span> len)</span> <span class="hljs-comment">// 对数组进行冒泡排序</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; len - <span class="hljs-number">1</span>; i++)<br>    &#123;<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j &lt; len - i - <span class="hljs-number">1</span>; j++)<br>        &#123;<br>            <span class="hljs-keyword">if</span> (arr[j] &gt; arr[j + <span class="hljs-number">1</span>])<br>            &#123;<br>                <span class="hljs-built_in">swap01</span>(arr + j, arr + j + <span class="hljs-number">1</span>);<br>                <span class="hljs-comment">// 数组的变量名可以直接加法表示后面的某一位</span><br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">person</span><br>&#123;<br>    <span class="hljs-comment">// 结构体 自定义数据类型</span><br>    <span class="hljs-comment">// 例子</span><br>    string name;<br>    <span class="hljs-type">short</span> age;<br>    <span class="hljs-type">bool</span> sex;<br>&#125;;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">swap02</span><span class="hljs-params">(person *a, person *b)</span> <span class="hljs-comment">// 交换结构体</span></span><br><span class="hljs-function"></span>&#123;<br>    person temp = *a;<br>    *a = *b;<br>    *b = temp;<br>&#125;<br><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">person</span> people[<span class="hljs-number">2</span>] = &#123;<br>    &#123;<span class="hljs-string">&quot;boy&quot;</span>, <span class="hljs-number">16</span>, <span class="hljs-number">1</span>&#125;,<br>    &#123;<span class="hljs-string">&quot;girl&quot;</span>, <span class="hljs-number">14</span>, <span class="hljs-number">0</span>&#125;&#125;;<br></code></pre></td></tr></table></figure><h1 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h1><h2 id="一般"><a href="#一般" class="headerlink" title="一般"></a>一般</h2><p>int *p;</p><p>声明一个指针, 指针保存的是地址</p><p>声明时 * 表示指针, 引用时 * 表示解引用</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> a = <span class="hljs-number">1</span>, b = <span class="hljs-number">2</span>;<br><span class="hljs-built_in">swap01</span>(&amp;a, &amp;b); <span class="hljs-comment">// &amp; 表示取地址</span><br>cout &lt;&lt; <span class="hljs-string">&quot;a = &quot;</span> &lt;&lt; a &lt;&lt; <span class="hljs-string">&quot; b = &quot;</span> &lt;&lt; b &lt;&lt; endl;<br></code></pre></td></tr></table></figure><h2 id="const修饰"><a href="#const修饰" class="headerlink" title="const修饰"></a>const修饰</h2><p>  const修饰指针, 指向可以改, 值不能改</p><p>  const修饰常量, 指向不可改, 值可以改</p><p>  const也可以两者都修饰</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><code class="hljs cpp"><span class="hljs-type">int</span> a = <span class="hljs-number">1</span>, b = <span class="hljs-number">2</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">int</span> *p1 = &amp;a;<br>p1 = &amp;b;<br><span class="hljs-type">int</span> *<span class="hljs-type">const</span> p2 = &amp;b;<br>*p2 = a;<br></code></pre></td></tr></table></figure><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>指针和数组</p><p>int arr[4] &#x3D; {1, 2, 3, 4};</p><p>arr即为首个元素的地址</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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> arr[<span class="hljs-number">4</span>] = &#123;<span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>&#125;;<br><span class="hljs-type">int</span> len = <span class="hljs-built_in">sizeof</span>(arr) / <span class="hljs-built_in">sizeof</span>(arr[<span class="hljs-number">0</span>]);<br><span class="hljs-built_in">bubble</span>(arr, len);<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; len; i++)<br>&#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;arr[&quot;</span> &lt;&lt; i &lt;&lt; <span class="hljs-string">&quot;] = &quot;</span> &lt;&lt; arr[i] &lt;&lt; <span class="hljs-string">&quot; &quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="结构体"><a href="#结构体" class="headerlink" title="结构体"></a>结构体</h2><p>创建一个函数,  交换两个人的信息</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">swap02</span>(people, people + <span class="hljs-number">1</span>); <span class="hljs-comment">// 传入地址 people是数组</span><br>cout &lt;&lt; <span class="hljs-string">&quot;people[0].name = &quot;</span> &lt;&lt; people[<span class="hljs-number">0</span>].name &lt;&lt; <span class="hljs-string">&quot; people[1].name = &quot;</span> &lt;&lt; people[<span class="hljs-number">1</span>].name &lt;&lt; endl;<br></code></pre></td></tr></table></figure><p>指针使用 -&gt; 访问结构体中的元素</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">cout &lt;&lt; <span class="hljs-string">&quot;people[0].name = &quot;</span> &lt;&lt; people-&gt;name &lt;&lt; <span class="hljs-string">&quot; people[1].name = &quot;</span> &lt;&lt; (people + <span class="hljs-number">1</span>)-&gt;name &lt;&lt; endl;<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.harkerhand.cn/C-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83/</id>
    <link href="https://www.harkerhand.cn/C-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83/"/>
    <published>2023-03-14T14:44:37.000Z</published>
    <summary>
      <![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>下文代码中会用到一些函数</p>
<p>在此处做出声明</p>
<figure class="highlight]]>
    </summary>
    <title>C++从入门到放弃</title>
    <updated>2026-04-13T06:42:51.988Z</updated>
  </entry>
</feed>
