<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>M81&#39;s Website</title>
    <link>https://zzhx.cc/</link>
    <description>Recent content from M81&#39;s Website</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    
    <managingEditor>zzhx2006@outlook.com (M81)</managingEditor>
    <webMaster>zzhx2006@outlook.com (M81)</webMaster>
    
    <copyright>本博客所有文章除特别声明外，均采用 BY-SA 许可协议。转载请注明出处！</copyright>
    
    <lastBuildDate>Thu, 29 Jan 2026 00:00:00 +0000</lastBuildDate>
    
    
    <atom:link href="https://zzhx.cc/index.xml" rel="self" type="application/rss&#43;xml" />
    

    
    

    <item>
      <title>Anki 助力业余无线电等级考试刷题</title>
      <link>https://zzhx.cc/post/anki-helps-amateur-radio-exam-practice-questions/</link>
      <pubDate>Thu, 29 Jan 2026 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/anki-helps-amateur-radio-exam-practice-questions/</guid>
      <description>
        <![CDATA[<h1>Anki 助力业余无线电等级考试刷题</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>题库版本：<code>v20250809</code></p>
<p>资源链接：<a href="../../amateur_radio_class_b_exam_question_bank.7z">点击此处下载</a>。含有：</p>
<ul>
<li>Anki 备份文件 (<code>amateur_radio_class_b_exam_question_bank.apkg</code>)：可在 Anki 中直接导入，内置一个品相稍差的选择题模板，可按需替换调整；</li>
<li>2025 年 B 类考试题库 txt 版 (<code>amateur_radio_class_b_exam_question_bank.txt</code>)：从官方 pdf 题库中直接提取；</li>
<li>2025 年 B 类考试题库 csv 版 (<code>amateur_radio_class_b_exam_question_bank.csv</code>)：遵循标准 csv 格式，从 txt 版处理得到；</li>
</ul>
<p>三者皆不含图片。图片题仅数道，建议单独学习。</p>
<p>使用时既可以直接导入 Anki 备份文件版，也可以按需自行从 txt 或 csv 定制化。</p>
<p>合理安排每天学习和复习的卡片数量，最短 4 天即可拿下。</p>
<p>祝考试通过！73～</p>

        
        <hr><p>本文2026-01-29首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2026-01-29</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>让 Clangd 支持 GCC 的 C&#43;&#43;23 Std Module</title>
      <link>https://zzhx.cc/post/enable-clangd-cpp23-modules-with-gcc/</link>
      <pubDate>Sat, 22 Nov 2025 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/enable-clangd-cpp23-modules-with-gcc/</guid>
      <description>
        <![CDATA[<h1>让 Clangd 支持 GCC 的 C++23 Std Module</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>本文主要记录在 Arch Linux 上使用 GCC 15.2.1 编译器编译包含 <code>import std;</code> 的 C++23 标准模块（std module）代码时，如何让 Clangd 正常工作——即 <code>import std;</code> 不再被标记为红色波浪线提示“Module &lsquo;std&rsquo; not found”，并且能够正常提供其中符号的悬停（Hover）提示。</p>
<p>使用的工具版本如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> - GCC: 15.2.1
</span></span><span class="line"><span class="cl"> - Clangd: 22.0.0git def8ecbda9f146d5ba5bbc8c92f7d5ccd242ad2b
</span></span><span class="line"><span class="cl"> - VS Code: 1.106.1
</span></span><span class="line"><span class="cl"> - VS Code 中的 Clangd 插件: 0.2.0
</span></span><span class="line"><span class="cl"> - Bear: 3.1.6
</span></span></code></pre></div><p>如你所见，Clangd 官方目前最新的稳定版本是 21.1.6，但本次实验需自行从 Git 仓库编译开发中的 22.0.0 版本。实测表明，21.x 版本容易出现部分源代码文件中的符号无法正常解析的问题。</p>
<p>VS Code 的 C++ 插件使用的是 Clangd（LLVM）。Microsoft 官方的 C/C++ 插件表现如何，可自行尝试。</p>
<p>首先，我们创建一个最小可行项目。运行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cp /usr/include/c++/15.2.1/bits/std.cc .
</span></span><span class="line"><span class="cl">cp /usr/include/c++/15.2.1/bits/std.compat.cc .
</span></span></code></pre></div><p>将系统中 GCC 提供的两个用于 C++23 标准模块的关键文件复制到当前目录。</p>
<p>接着新建一个 <code>main.cpp</code> 文件，内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="n">import</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">import</span> <span class="n">std</span><span class="p">.</span><span class="n">compat</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello C++23 Std Module in GCC!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>并编写对应的 Makefile：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">CXX</span> <span class="o">:=</span> /usr/bin/g++
</span></span><span class="line"><span class="cl"><span class="nv">CXXFLAGS</span> <span class="o">:=</span> -std<span class="o">=</span>c++26 -fmodules
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">STDCC</span> <span class="o">:=</span> std.cc
</span></span><span class="line"><span class="cl"><span class="nv">STDCOMPATCC</span> <span class="o">:=</span> std.compat.cc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="o">:</span> <span class="n">gcm</span>.<span class="n">cache</span>/<span class="n">std</span>.<span class="n">gcm</span> <span class="n">gcm</span>.<span class="n">cache</span>/<span class="n">std</span>.<span class="n">compat</span>.<span class="n">gcm</span> <span class="n">main</span>.<span class="n">cpp</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>CXX<span class="k">)</span> <span class="k">$(</span>CXXFLAGS<span class="k">)</span>    -o main           main.cpp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">gcm.cache/std.gcm</span><span class="o">:</span> <span class="k">$(</span><span class="nv">STDCC</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>CXX<span class="k">)</span> <span class="k">$(</span>CXXFLAGS<span class="k">)</span> -c -o std.o          <span class="k">$(</span>STDCC<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">gcm.cache/std.compat.gcm</span><span class="o">:</span> <span class="k">$(</span><span class="nv">STDCOMPATCC</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>CXX<span class="k">)</span> <span class="k">$(</span>CXXFLAGS<span class="k">)</span> -c -o std.compat.o   <span class="k">$(</span>STDCOMPATCC<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">clean</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">clean</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	rm -rf .cache gcm.cache main *.o compile_commands.json
</span></span></code></pre></div><p>下一步，使用 Bear 生成 <code>compile_commands.json</code>，同时尝试编译，运行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">bear -- make
</span></span></code></pre></div><p>这会在当前目录下生成 <code>compile_commands.json</code> 文件，该文件将被 Clangd 识别并使用。同时会编译源文件。正常情况下应无报错，当前目录下会生成 <code>std.o</code>、<code>std.compat.o</code>、<code>main</code> 和 <code>gcm.cache/</code> 目录。Make 实际执行了以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">/usr/bin/g++ -std<span class="o">=</span>c++26 -fmodules -c -o std.o          std.cc
</span></span><span class="line"><span class="cl">/usr/bin/g++ -std<span class="o">=</span>c++26 -fmodules -c -o std.compat.o   std.compat.cc
</span></span><span class="line"><span class="cl">/usr/bin/g++ -std<span class="o">=</span>c++26 -fmodules    -o main           main.cpp
</span></span></code></pre></div><p>接着，新建 <code>.clangd</code> 配置文件，内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">If</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">PathMatch</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">.*\.h, .*\.cpp]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">CompileFlags</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Add</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>-<span class="l">std=c++26, -fmodules, -Xclang, -fbuiltin-headers-in-system-modules]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Compiler</span><span class="p">:</span><span class="w"> </span><span class="l">g++</span><span class="w">
</span></span></span></code></pre></div><p>在 VS Code 中按 <code>Ctrl+,</code> 打开设置，搜索 “clangd”，找到 “Clangd: Arguments” 选项，添加以下两条参数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">--enable-config
</span></span><span class="line"><span class="cl">--experimental-modules-support
</span></span></code></pre></div><p>如果你确实自行编译了 Clangd 22.0.0，可能还需要修改 “Clangd: Path” 设置，将其指向你编译出的 Clangd 可执行文件路径。</p>
<p>此时，用 VS Code 打开 <code>main.cpp</code>，应该会看到 <code>import std;</code> 下的红色波浪线已经消失，将光标悬停在 <code>cout</code> 上时，也能正常显示悬停提示框。</p>
<p>以下是一些可能出现的常见问题：</p>
<ol>
<li>请确保 <code>compile_commands.json</code> 中包含正确的内容，而不是只有 <code>[]</code>。这可以通过执行 <code>make clean; make</code> 来解决。</li>
<li>请确保确实将系统目录中的 <code>std.cc</code> 和 <code>std.compat.cc</code> 这两个文件复制到了当前目录中，而不是在 Makefile 中直接引用系统路径下的文件，否则可能导致 Clangd 解析失败。</li>
<li>请确保 Clangd 能读取到的 <code>.clangd</code> 配置文件中，<code>CompileFlags</code> 已正确添加了 <code>-fmodules</code>、<code>-Xclang</code> 和 <code>-fbuiltin-headers-in-system-modules</code> 这几个选项，特别是最后一个。否则可能在 Clangd 日志中看到与 <code>inttypes.h</code> 相关的报错。</li>
<li>请确保在 VS Code 的 Clangd 插件设置中已添加 <code>--experimental-modules-support</code> 参数，否则 Clangd 可能在多次重启失败后自动停止运行。</li>
</ol>
<p>希望这篇文章对你有所帮助！如果你仍有疑问，欢迎通过电子邮件等方式与我分享你的实验经历。</p>

        
        <hr><p>本文2025-11-22首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2025-11-22</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>C&#43;&#43; 标准中值类别的中文翻译</title>
      <link>https://zzhx.cc/post/translation-of-value-categories-in-cpp-standard/</link>
      <pubDate>Thu, 05 Jun 2025 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/translation-of-value-categories-in-cpp-standard/</guid>
      <description>
        <![CDATA[<h1>C++ 标准中值类别的中文翻译</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>以下内容是《ISO/IEC 14882:2024》<em>[7.2.1]</em> <strong>Value category</strong> <em>[basic.lval]</em> 部分的中文翻译。标准文件可从<a href="https://github.com/zzhx2006/CPP_Standard">此仓库</a>下载。</p>
<hr>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">          expression         
</span></span><span class="line"><span class="cl">           /     \           
</span></span><span class="line"><span class="cl">          /       \          
</span></span><span class="line"><span class="cl">     glvalue      rvalue     
</span></span><span class="line"><span class="cl">      /    \      /   \      
</span></span><span class="line"><span class="cl">     /      \    /     \     
</span></span><span class="line"><span class="cl"> lvalue     xvalue    prvalue
</span></span></code></pre></div><ul>
<li>glvalue（泛左值）是一个表达式，其求值结果决定了某个对象或函数的身份（identity）。</li>
<li>prvalue（纯右值）是一个表达式，其求值用于初始化一个对象、或在特定上下文中计算某个运算符的操作数，或者是一个类型为 cv void 的表达式。</li>
</ul>
<ul>
<li>xvalue（亡值）是一个 glvalue，它表示一个其资源可以被重用的对象（通常是因为该对象即将结束其生命周期）。</li>
<li>lvalue（左值）是一个不是 xvalue 的 glvalue。</li>
<li>rvalue（右值）是一个 prvalue 或者 xvalue。</li>
</ul>
<p>每一条表达式恰好属于本分类体系中以下三种基本类别之一：lvalue、xvalue 或 prvalue。这种表达式的属性被称为它的值类别（value category）。</p>
<p><strong>注 1：</strong> 第 <em>[7.6]</em> 节中对每一个内建运算符的讨论，都指明了该运算符所产生的结果的值类别，以及它所期望的操作数的值类别。例如，内建的赋值运算符要求其左侧操作数为 lvalue，右侧操作数为 prvalue，并产生一个 lvalue 类型的结果。用户定义的运算符本质上是函数，其所接受的操作数和所产生的结果的值类别由其参数类型与返回类型决定。</p>
<p><strong>注 2：</strong> 历史上，lvalue 和 rvalue 这两个术语来源于它们通常出现在赋值表达式的左边或右边（尽管这一规则在现代语言规范中已不再普遍适用）；glvalue 是 “广义的” lvalue，prvalue 是 “纯正的” rvalue，而 xvalue 是 “将亡的” lvalue。尽管这些术语中包含 “值（value）”，但它们实际上是对表达式的分类，而非对实际数据值的描述。</p>
<p><strong>注 3：</strong> 一个表达式是 xvalue（将亡值），如果它满足以下任一条件：</p>
<ul>
<li>它是一个可移动的合格 id 表达式（id-expression）（参见 <em>[7.5.5.2]</em> 节）；</li>
<li>它是调用一个返回类型为对象类型的右值引用（rvalue reference）的函数所得到的结果，无论该函数调用是显式的还是隐式的（参见 <em>[7.6.1.3]</em> 节）；</li>
<li>它是一个到对象类型的右值引用的显式类型转换（cast）的结果（参见 <em>[7.6.1.4]</em>、<em>[7.6.1.7]</em>、<em>[7.6.1.9]</em>、<em>[7.6.1.10]</em>、<em>[7.6.1.11]</em>、<em>[7.6.3]</em> 节）；</li>
<li>它是一个使用 xvalue 类型数组操作数进行下标访问（subscripting）所得的结果（参见 <em>[7.6.1.2]</em> 节）；</li>
<li>它是一个类成员访问表达式，用于指代一个非静态数据成员，且该成员的类型不是引用类型，同时其对象表达式（object expression）本身是一个 xvalue（参见 <em>[7.6.1.5]</em> 节）；</li>
<li>它是一个指向成员的 <code>.*</code> 操作符表达式，其中第一个操作数是 xvalue，第二个操作数是指向数据成员的指针（pointer to data member）（参见 <em>[7.6.4]</em> 节）。</li>
</ul>
<p>一般而言，此规则的效果是：命名的右值引用（named rvalue references）被视为左值（lvalues），而未命名的右值引用到对象（unnamed rvalue references to objects）被视为将亡值（xvalues）；对于右值引用到函数（rvalue references to functions），无论是否命名，均被视为左值（lvalues）。</p>
<p>举例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">A</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">A</span> <span class="o">&amp;&amp;</span><span class="k">operator</span><span class="o">+</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">A</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">A</span> <span class="o">&amp;&amp;</span><span class="n">f</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">A</span> <span class="n">a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">A</span> <span class="o">&amp;&amp;</span><span class="n">ar</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">A</span> <span class="o">&amp;&amp;&gt;</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
</span></span></code></pre></div><p>表达式 <code>f()</code>、<code>f().m</code>、<code>static_cast&lt;A&amp;&amp;&gt;(a)</code> 和 <code>a + a</code> 是 xvalue。表达式 <code>ar</code> 是一个 lvalue。</p>
<p>一个 glvalue（“广义左值”）的结果是该表达式所表示的实体（entity）。一个 prvalue（“纯右值”）的结果是该表达式在其求值上下文中所存储的值；类型为 cv void 的 prvalue 没有结果。一个结果为值 V 的 prvalue 有时被称为 “拥有” 或 “命名” 该值 V。prvalue 的结果对象（result object）是指由该 prvalue 初始化的对象；如果某个非被丢弃的 prvalue 被用于计算某个内建运算符的操作数，或者其类型为 cv void，则它没有结果对象。</p>
<p><strong>注 4：</strong> 除非该纯右值（prvalue）是 decltype-specifier 的操作数，否则类型为类类型或数组类型的纯右值总是具有一个结果对象（result object）。对于类型不是 cv void 的被丢弃纯右值（discarded prvalue），会物化一个临时对象（temporary object）；参见 <em>[7.2.3]</em>。</p>
<p><strong>注 5：</strong> 试图将右值引用绑定到左值（lvalue）的情形不属于此类上下文；参见 <em>[9.4.4]</em>。</p>
<p><strong>注 6：</strong> 因为当非类类型的表达式被转换为纯右值时，其类型中的 cv 限定符会被移除，例如，类型为 const int 的左值可以在需要类型为 int 的纯右值的上下文中使用。</p>
<p><strong>注 7：</strong> 不存在作为纯右值的位域（bit-field）；如果一个位域被转换为纯右值（参见 <em>[7.3.2]</em>），则会创建一个其位域类型的纯右值，该纯右值随后可能进行整数提升（integer promotion）（参见 <em>[7.3.7]</em>）。</p>
<p>每当一个纯右值（prvalue）作为某个操作符的操作数出现，而该操作符期望的是一个广义左值（glvalue）时，则会应用临时对象物化转换（见 <em>[7.3.5]</em>）将该表达式转换为一个亡值（xvalue）。</p>
<p>在第 <em>[9.4.4]</em> 节关于引用初始化的讨论以及第 <em>[6.7.7]</em> 节关于临时对象的内容中，描述了左值和右值在其他重要上下文中的行为。</p>
<p>除非另有说明（见 <em>[9.2.9.5]</em>），一个纯右值的类型应当始终是完整类型（complete type）或 void 类型；如果其类型是一个类类型，或者是一个（可能是多维的）类类型的数组，则该类不得为抽象类（见 <em>[11.7.4]</em>）。广义左值（glvalue）不得具有 cv 限定的 void 类型。</p>
<p><strong>注 8：</strong> 广义左值可以具有完整的或不完整的非 void 类型。类类型和数组类型的纯右值可以具有 cv 限定类型；其他类型的纯右值总是具有非 cv 限定类型。参见 <em>[7.2.2]</em>。</p>
<p>一个左值是可修改的，除非其类型是 const 限定的，或者是一个函数类型。</p>
<p><strong>注 9：</strong> 若程序试图通过一个不可修改的左值或通过一个右值来修改一个对象，则该程序格式错误（ill-formed）（见 <em>[7.6.19]</em>、<em>[7.6.1.6]</em>、<em>[7.6.2.3]</em>）。</p>
<p>若一程序试图通过一个泛左值（glvalue）访问某一对象的存储值，而该泛左值的类型与下列类型之一不相似（not similar，见 <em>[7.3.6]</em>），则行为未定义（undefined behavior）：</p>
<ul>
<li>该对象的动态类型（dynamic type）；</li>
<li>与该对象的动态类型相对应的有符号或无符号类型；</li>
<li>char、unsigned char 或 <code>std::byte</code> 类型。</li>
</ul>
<p>若一程序对某一联合体（union）类型 U 的默认生成的拷贝 / 移动构造函数或拷贝 / 移动赋值运算符进行调用时，使用了一个泛左值实参，而该实参并不表示在其生存期内的一个 cv U 类型的对象，则行为未定义。</p>
<p><strong>注 10：</strong> 在 C 语言中，可以通过例如赋值操作来访问整个结构体类型的对象。相比之下，C++ 中并不存在通过类类型的左值来访问类类型对象的概念。</p>

        
        <hr><p>本文2025-06-05首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2025-06-05</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>核心 Linux Socket API 用法释义</title>
      <link>https://zzhx.cc/post/core-linux-socket-api-usage-explained/</link>
      <pubDate>Wed, 05 Mar 2025 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/core-linux-socket-api-usage-explained/</guid>
      <description>
        <![CDATA[<h1>核心 Linux Socket API 用法释义</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <h3 id="1-socket">
<a class="header-anchor" href="#1-socket"></a>
1. socket()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">socket</span><span class="p">(</span><span class="kt">int</span> <span class="err">协议族</span><span class="p">,</span> <span class="kt">int</span> <span class="err">套接字类型</span><span class="p">,</span> <span class="kt">int</span> <span class="err">协议</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：创建一个新的套接字，返回一个文件描述符用于后续网络通信.</p>
<p><strong>第 1 个参数 🔑</strong>：协议族，指定通信协议类型.</p>
<ul>
<li>示例：<code>AF_INET</code>（IPv4协议）、<code>AF_INET6</code>（IPv6协议）、<code>AF_UNIX</code>（本地进程间通信）.</li>
</ul>
<p><strong>第 2 个参数 🔑</strong>：套接字类型，指定通信语义.</p>
<ul>
<li>示例：<code>SOCK_STREAM</code>（TCP流式套接字）、<code>SOCK_DGRAM</code>（UDP数据报套接字）.</li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：具体协议，通常为0（使用默认协议）.</p>
<ul>
<li>示例：若类型是<code>SOCK_STREAM</code>，则默认协议是<code>IPPROTO_TCP</code>；若类型是<code>SOCK_DGRAM</code>，默认协议是<code>IPPROTO_UDP</code>.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回非负整数（套接字文件描述符）.</p>
</li>
<li>
<p>❎ 失败时：返回-1，并设置<code>errno</code>（如<code>EACCES</code>权限不足、<code>EINVAL</code>参数无效）.</p>
</li>
</ul>
<hr>
<h3 id="2-bind">
<a class="header-anchor" href="#2-bind"></a>
2. bind()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">bind</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：将套接字与特定IP地址和端口绑定.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（通过<code>socket()</code>创建的文件描述符）.</p>
<p><strong>第 2 个参数 🔑</strong>：地址（指向<code>sockaddr</code>结构的指针），包含IP和端口.</p>
<ul>
<li>示例：<code>struct sockaddr_in server_addr</code>（IPv4地址结构），需填充<code>sin_family</code>（<code>AF_INET</code>）、<code>sin_port</code>（<code>htons(8080)</code>）、<code>sin_addr</code>（<code>INADDR_ANY</code> 表示监听所有接口）.</li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：地址长度（地址结构的大小）.</p>
<ul>
<li>示例：<code>sizeof(struct sockaddr_in)</code>.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如端口已被占用，<code>errno=EADDRINUSE</code>）.</p>
</li>
</ul>
<hr>
<h3 id="3-listen">
<a class="header-anchor" href="#3-listen"></a>
3. listen()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">listen</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">积压连接数</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：将套接字设为监听模式，等待客户端连接请求.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（已绑定的套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：积压连接数（等待队列的最大长度）.</p>
<ul>
<li>示例：5（表示最多允许5个未处理的连接请求）.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如套接字未绑定，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="4-accept">
<a class="header-anchor" href="#4-accept"></a>
4. accept()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">accept</span><span class="p">(</span><span class="kt">int</span> <span class="err">监听套接字描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">客户端地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="o">*</span><span class="err">客户端地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：接受客户端的连接请求，返回新的套接字用于通信.</p>
<p><strong>第 1 个参数 🔑</strong>：监听套接字描述符（通过<code>listen()</code>设置的套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：客户端地址（输出参数，保存客户端地址信息）.</p>
<ul>
<li>示例：<code>struct sockaddr_in client_addr</code>，调用后填充客户端IP和端口.</li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：客户端地址长度（输入输出参数，传入地址结构大小，返回实际大小）.</p>
<ul>
<li>示例：<code>socklen_t len = sizeof(client_addr);</code></li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回新的套接字描述符（用于与客户端通信）.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如系统资源不足，<code>errno=ENOMEM</code>）.</p>
</li>
</ul>
<hr>
<h3 id="5-connect">
<a class="header-anchor" href="#5-connect"></a>
5. connect()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">connect</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">服务器地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：客户端主动连接服务器.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（客户端通过<code>socket()</code>创建的套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：服务器地址（服务器的IP和端口）.</p>
<ul>
<li>示例：<code>struct sockaddr_in server_addr</code>，填充<code>sin_family=AF_INET</code>、<code>sin_port=htons(80)</code>、<code>sin_addr.s_addr=inet_addr(&quot;192.168.1.1&quot;)</code>.</li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：地址长度（地址结构的大小）.</p>
<ul>
<li>示例：<code>sizeof(struct sockaddr_in)</code>.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如服务器不可达，<code>errno=ECONNREFUSED</code>）.</p>
</li>
</ul>
<hr>
<h3 id="6-send">
<a class="header-anchor" href="#6-send"></a>
6. send()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">send</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="err">缓冲区</span><span class="p">,</span> <span class="kt">size_t</span> <span class="err">长度</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：通过TCP套接字发送数据.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（已连接的套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：缓冲区（发送数据的内存地址）.</p>
<ul>
<li>示例：<code>char *msg = &quot;Hello&quot;;</code></li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：长度（要发送的字节数）.</p>
<ul>
<li>示例：<code>strlen(msg)</code>.</li>
</ul>
<p><strong>第 4 个参数 🔑</strong>：标志（控制行为，通常为0）.</p>
<ul>
<li>示例：0（默认阻塞发送）.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际发送的字节数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如连接断开，<code>errno=EPIPE</code>）.</p>
</li>
</ul>
<hr>
<h3 id="7-recv">
<a class="header-anchor" href="#7-recv"></a>
7. recv()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">recv</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="err">缓冲区</span><span class="p">,</span> <span class="kt">size_t</span> <span class="err">长度</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：通过TCP套接字接收数据.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（已连接的套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：缓冲区（接收数据的内存地址）.</p>
<ul>
<li>示例：<code>char buffer[1024];</code></li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：长度（缓冲区最大容量）.</p>
<ul>
<li>示例：<code>sizeof(buffer)</code>.</li>
</ul>
<p><strong>第 4 个参数 🔑</strong>：标志（控制行为，通常为0）.</p>
<ul>
<li>示例：0（默认阻塞接收，直到有数据到达）.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际接收的字节数（若返回0，表示对端关闭连接）.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如套接字无效，<code>errno=ENOTCONN</code>）.</p>
</li>
</ul>
<hr>
<h3 id="8-sendto">
<a class="header-anchor" href="#8-sendto"></a>
8. sendto()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">sendto</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="err">缓冲区</span><span class="p">,</span> <span class="kt">size_t</span> <span class="err">长度</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">目标地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：通过UDP套接字发送数据到指定地址.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（UDP套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：缓冲区（发送数据的内存地址）.</p>
<ul>
<li>示例：<code>char *msg = &quot;UDP Message&quot;;</code></li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：长度（发送数据的字节数）.</p>
<ul>
<li>示例：<code>strlen(msg)</code>.</li>
</ul>
<p><strong>第 4 个参数 🔑</strong>：标志（通常为0）.</p>
<p><strong>第 5 个参数 🔑</strong>：目标地址（目标IP和端口）.</p>
<ul>
<li>示例：<code>struct sockaddr_in server_addr</code>，填充服务器信息.</li>
</ul>
<p><strong>第 6 个参数 🔑</strong>：地址长度（目标地址结构的大小）.</p>
<ul>
<li>示例：<code>sizeof(struct sockaddr_in)</code>.</li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际发送的字节数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如网络不可达，<code>errno=ENETUNREACH</code>）.</p>
</li>
</ul>
<hr>
<h3 id="9-recvfrom">
<a class="header-anchor" href="#9-recvfrom"></a>
9. recvfrom()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">recvfrom</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="err">缓冲区</span><span class="p">,</span> <span class="kt">size_t</span> <span class="err">缓冲区长度</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">,</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">源地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="o">*</span><span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：从套接字接收数据，支持获取发送方地址信息（常用于UDP）.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（已绑定的UDP或TCP套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：接收缓冲区指针（存储接收到的数据）.</p>
<p><strong>第 3 个参数 🔑</strong>：缓冲区最大容量（字节）.</p>
<p><strong>第 4 个参数 🔑</strong>：接收标志（如<code>MSG_PEEK</code>预览数据、<code>MSG_WAITALL</code>等待完整数据）.</p>
<p><strong>第 5 个参数 🔑</strong>：源地址（输出参数，保存发送方地址信息）.</p>
<p><strong>第 6 个参数 🔑</strong>：地址长度（输入输出参数，传入地址结构大小，返回实际地址长度）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际接收的字节数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如缓冲区不足，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="10-sendmsg">
<a class="header-anchor" href="#10-sendmsg"></a>
10. sendmsg()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">sendmsg</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">msghdr</span> <span class="o">*</span><span class="err">消息结构</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：发送复杂消息（支持多缓冲区和辅助数据，如文件描述符传递）.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符（已连接的TCP或绑定的UDP套接字）.</p>
<p><strong>第 2 个参数 🔑</strong>：消息结构指针（包含数据缓冲区、地址、控制信息）.</p>
<p><strong>第 3 个参数 🔑</strong>：发送标志（如<code>MSG_DONTWAIT</code>非阻塞发送）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际发送的字节数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效参数，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="11-recvmsg">
<a class="header-anchor" href="#11-recvmsg"></a>
11. recvmsg()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span> <span class="nf">recvmsg</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">msghdr</span> <span class="o">*</span><span class="err">消息结构</span><span class="p">,</span> <span class="kt">int</span> <span class="err">标志</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：接收复杂消息（支持多缓冲区和辅助数据）.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符.</p>
<p><strong>第 2 个参数 🔑</strong>：消息结构指针（输出参数，存储接收的数据和控制信息）.</p>
<p><strong>第 3 个参数 🔑</strong>：接收标志（如<code>MSG_TRUNC</code>返回完整数据长度即使截断）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回实际接收的字节数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如套接字未连接，<code>errno=ENOTCONN</code>）.</p>
</li>
</ul>
<hr>
<h3 id="12-close">
<a class="header-anchor" href="#12-close"></a>
12. close()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">close</span><span class="p">(</span><span class="kt">int</span> <span class="err">文件描述符</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：关闭套接字描述符，释放资源.</p>
<p><strong>第 1 个参数 🔑</strong>：要关闭的套接字描述符.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效描述符，<code>errno=EBADF</code>）.</p>
</li>
</ul>
<hr>
<h3 id="13-shutdown">
<a class="header-anchor" href="#13-shutdown"></a>
13. shutdown()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">shutdown</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">关闭方式</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：关闭套接字的读/写通道或全部通道.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符.</p>
<p><strong>第 2 个参数 🔑</strong>：关闭方式（<code>SHUT_RD</code>停读，<code>SHUT_WR</code>停写，<code>SHUT_RDWR</code>全停）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效方式，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="14-setsockopt">
<a class="header-anchor" href="#14-setsockopt"></a>
14. setsockopt()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">setsockopt</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">协议层</span><span class="p">,</span> <span class="kt">int</span> <span class="err">选项名</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="err">选项值</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="err">选项长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：设置套接字选项（如超时、重用地址）.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符.</p>
<p><strong>第 2 个参数 🔑</strong>：协议层（如<code>SOL_SOCKET</code>通用选项，<code>IPPROTO_TCP</code> TCP选项）.</p>
<p><strong>第 3 个参数 🔑</strong>：选项名（如<code>SO_REUSEADDR</code>地址重用，<code>SO_RCVTIMEO</code>接收超时）.</p>
<p><strong>第 4 个参数 🔑</strong>：选项值指针（如<code>int reuse = 1</code>）.</p>
<p><strong>第 5 个参数 🔑</strong>：选项值长度（如<code>sizeof(reuse)</code>）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效选项，<code>errno=ENOPROTOOPT</code>）.</p>
</li>
</ul>
<hr>
<h3 id="15-getsockopt">
<a class="header-anchor" href="#15-getsockopt"></a>
15. getsockopt()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">getsockopt</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">协议层</span><span class="p">,</span> <span class="kt">int</span> <span class="err">选项名</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="err">选项值</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="o">*</span><span class="err">选项长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：获取套接字选项的当前值.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符.</p>
<p><strong>第 2 个参数 🔑</strong>：协议层（同<code>setsockopt</code>）.</p>
<p><strong>第 3 个参数 🔑</strong>：选项名（如<code>SO_ERROR</code>获取错误状态）.</p>
<p><strong>第 4 个参数 🔑</strong>：选项值指针（输出参数，存储选项值）.</p>
<p><strong>第 5 个参数 🔑</strong>：选项长度（输入输出参数，传入指针大小，返回实际大小）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效选项，<code>errno=ENOPROTOOPT</code>）.</p>
</li>
</ul>
<hr>
<h3 id="16-getsockname">
<a class="header-anchor" href="#16-getsockname"></a>
16. getsockname()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">getsockname</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">地址</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="o">*</span><span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：获取套接字绑定的本地地址.</p>
<p><strong>第 1 个参数 🔑</strong>：套接字描述符.</p>
<p><strong>第 2 个参数 🔑</strong>：地址结构指针（输出参数，保存本地IP和端口）.</p>
<p><strong>第 3 个参数 🔑</strong>：地址长度（输入输出参数，传入结构大小，返回实际大小）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如未绑定地址，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="17-getpeername">
<a class="header-anchor" href="#17-getpeername"></a>
17. getpeername()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">getpeername</span><span class="p">(</span><span class="kt">int</span> <span class="err">套接字描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="err">地址结构</span><span class="p">,</span> <span class="kt">socklen_t</span> <span class="o">*</span><span class="err">地址长度</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：获取已连接套接字对端的地址信息.</p>
<p><strong>第 1 个参数 🔑</strong>：已连接的套接字描述符（如accept返回的描述符）.</p>
<p><strong>第 2 个参数 🔑</strong>：地址结构（输出参数，保存对端IP和端口）.</p>
<ul>
<li>示例：<code>struct sockaddr_in peer_addr;</code></li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：地址长度（输入输出参数，传入结构大小，返回实际大小）.</p>
<ul>
<li>示例：<code>socklen_t len = sizeof(peer_addr);</code></li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如未连接套接字，<code>errno=ENOTCONN</code>）.</p>
</li>
</ul>
<hr>
<h3 id="18-ioctl">
<a class="header-anchor" href="#18-ioctl"></a>
18. ioctl()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">ioctl</span><span class="p">(</span><span class="kt">int</span> <span class="err">文件描述符</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="err">请求码</span><span class="p">,</span> <span class="p">...</span> <span class="cm">/* 参数 */</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：对文件描述符执行设备输入/输出操作（常用于设置套接字选项）.</p>
<p><strong>第 1 个参数 🔑</strong>：文件描述符（如套接字描述符）.</p>
<p><strong>第 2 个参数 🔑</strong>：请求码（指定操作类型，如<code>FIONBIO</code>设置非阻塞模式）.</p>
<p><strong>第 3 个参数 🔑</strong>：可变参数（根据请求码传入参数，如<code>int *非阻塞标志</code>）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">flags</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">ioctl</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="n">FIONBIO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">flags</span><span class="p">);</span> <span class="c1">// 设置非阻塞
</span></span></span></code></pre></div></li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效请求码，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="19-fcntl">
<a class="header-anchor" href="#19-fcntl"></a>
19. fcntl()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fcntl</span><span class="p">(</span><span class="kt">int</span> <span class="err">文件描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">命令</span><span class="p">,</span> <span class="p">...</span> <span class="cm">/* 可选参数 */</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：对文件描述符执行控制操作（如设置非阻塞模式）.</p>
<p><strong>第 1 个参数 🔑</strong>：文件描述符（如套接字描述符）.</p>
<p><strong>第 2 个参数 🔑</strong>：命令（如<code>F_GETFL</code>获取标志，<code>F_SETFL</code>设置标志）.</p>
<p><strong>第 3 个参数 🔑</strong>：可选参数（如标志位<code>O_NONBLOCK</code>）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">flags</span> <span class="o">=</span> <span class="nf">fcntl</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="n">F_GETFL</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">fcntl</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="n">F_SETFL</span><span class="p">,</span> <span class="n">flags</span> <span class="o">|</span> <span class="n">O_NONBLOCK</span><span class="p">);</span> <span class="c1">// 设置非阻塞
</span></span></span></code></pre></div></li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回依赖命令（如<code>F_GETFL</code>返回标志值）.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效命令，<code>errno=EINVAL</code>）.</p>
</li>
</ul>
<hr>
<h3 id="20-select">
<a class="header-anchor" href="#20-select"></a>
20. select()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">select</span><span class="p">(</span><span class="kt">int</span> <span class="err">最大描述符</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">fd_set</span> <span class="o">*</span><span class="err">读集合</span><span class="p">,</span> <span class="n">fd_set</span> <span class="o">*</span><span class="err">写集合</span><span class="p">,</span> <span class="n">fd_set</span> <span class="o">*</span><span class="err">异常集合</span><span class="p">,</span> <span class="k">struct</span> <span class="n">timeval</span> <span class="o">*</span><span class="err">超时时间</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：同步I/O多路复用，监控多个文件描述符状态.</p>
<p><strong>第 1 个参数 🔑</strong>：监控的最大文件描述符值+1（如<code>max_fd+1</code>）.</p>
<p><strong>第 2-4 个参数 🔑</strong>：读/写/异常集合（使用<code>FD_SET</code>宏设置，返回就绪的描述符）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fd_set</span> <span class="n">read_fds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">FD_ZERO</span><span class="p">(</span><span class="o">&amp;</span><span class="n">read_fds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">FD_SET</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">read_fds</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<p><strong>第 5 个参数 🔑</strong>：超时时间（<code>NULL</code>表示无限等待，<code>timeval</code>结构指定秒/微秒）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回就绪的描述符总数.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如中断，<code>errno=EINTR</code>）.</p>
</li>
</ul>
<hr>
<h3 id="21-poll">
<a class="header-anchor" href="#21-poll"></a>
21. poll()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">poll</span><span class="p">(</span><span class="k">struct</span> <span class="n">pollfd</span> <span class="o">*</span><span class="err">文件描述符数组</span><span class="p">,</span> <span class="kt">nfds_t</span> <span class="err">文件描述符数量</span><span class="p">,</span> <span class="kt">int</span> <span class="err">超时时间</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：类似<code>select</code>，但使用数组结构管理描述符.</p>
<p><strong>第 1 个参数 🔑</strong>：<code>pollfd</code>结构数组（包含描述符、事件、返回事件）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">pollfd</span> <span class="n">fds</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fds</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">fd</span> <span class="o">=</span> <span class="n">sockfd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fds</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">events</span> <span class="o">=</span> <span class="n">POLLIN</span><span class="p">;</span>
</span></span></code></pre></div></li>
</ul>
<p><strong>第 2 个参数 🔑</strong>：数组元素数量（如<code>sizeof(fds)/sizeof(fds[0])</code>）.</p>
<p><strong>第 3 个参数 🔑</strong>：超时时间（毫秒，-1表示无限等待）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回就绪的描述符数量.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如无效参数，<code>errno=EFAULT</code>）.</p>
</li>
</ul>
<hr>
<h3 id="22-epoll_create">
<a class="header-anchor" href="#22-epoll_create"></a>
22. epoll_create()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">epoll_create</span><span class="p">(</span><span class="kt">int</span> <span class="err">大小</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：创建epoll实例（内核事件表）.</p>
<p><strong>第 1 个参数 🔑</strong>：事件表大小（Linux 2.6.8+忽略此值，建议填0）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回epoll文件描述符.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如权限不足，<code>errno=EPERM</code>）.</p>
</li>
</ul>
<hr>
<h3 id="23-epoll_ctl">
<a class="header-anchor" href="#23-epoll_ctl"></a>
23. epoll_ctl()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">epoll_ctl</span><span class="p">(</span><span class="kt">int</span> <span class="n">epoll描述符</span><span class="p">,</span> <span class="kt">int</span> <span class="err">操作类型</span><span class="p">,</span> <span class="kt">int</span> <span class="err">目标描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">epoll_event</span> <span class="o">*</span><span class="err">事件</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：注册/修改/删除epoll事件.</p>
<p><strong>第 1 个参数 🔑</strong>：epoll描述符（由<code>epoll_create</code>返回）.</p>
<p><strong>第 2 个参数 🔑</strong>：操作类型（<code>EPOLL_CTL_ADD</code>添加，<code>EPOLL_CTL_MOD</code>修改，<code>EPOLL_CTL_DEL</code>删除）.</p>
<p><strong>第 3 个参数 🔑</strong>：目标描述符（如套接字描述符）.</p>
<p><strong>第 4 个参数 🔑</strong>：事件结构（指定监听事件如<code>EPOLLIN</code>）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">ev</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ev</span><span class="p">.</span><span class="n">events</span> <span class="o">=</span> <span class="n">EPOLLIN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ev</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">sockfd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">epoll_ctl</span><span class="p">(</span><span class="n">epfd</span><span class="p">,</span> <span class="n">EPOLL_CTL_ADD</span><span class="p">,</span> <span class="n">sockfd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ev</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回0.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如描述符已存在，<code>errno=EEXIST</code>）.</p>
</li>
</ul>
<hr>
<h3 id="24-epoll_wait">
<a class="header-anchor" href="#24-epoll_wait"></a>
24. epoll_wait()
</h3><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">epoll_wait</span><span class="p">(</span><span class="kt">int</span> <span class="n">epoll描述符</span><span class="p">,</span> <span class="k">struct</span> <span class="n">epoll_event</span> <span class="o">*</span><span class="err">事件数组</span><span class="p">,</span> <span class="kt">int</span> <span class="err">最大事件数</span><span class="p">,</span> <span class="kt">int</span> <span class="err">超时时间</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>📖 解释</strong>：等待epoll事件就绪.</p>
<p><strong>第 1 个参数 🔑</strong>：epoll描述符（由<code>epoll_create</code>返回）.</p>
<p><strong>第 2 个参数 🔑</strong>：事件数组（保存就绪事件）.</p>
<ul>
<li>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">events</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="nf">epoll_wait</span><span class="p">(</span><span class="n">epfd</span><span class="p">,</span> <span class="n">events</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<p><strong>第 3 个参数 🔑</strong>：最大事件数（如数组长度10）.</p>
<p><strong>第 4 个参数 🔑</strong>：超时时间（毫秒，-1表示无限等待）.</p>
<p><strong>返回值 📤</strong>：</p>
<ul>
<li>
<p>✅ 成功时：返回就绪事件数量.</p>
</li>
<li>
<p>❎ 失败时：返回-1（如中断，<code>errno=EINTR</code>）.</p>
</li>
</ul>
<hr>
<p>Generated by Qwen2.5-Max + QwQ-32B-Preview.</p>

        
        <hr><p>本文2025-03-05首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2025-03-05</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>C&#43;&#43;变参模板实战：递归继承实现简易元组类</title>
      <link>https://zzhx.cc/post/cpp-variadic-templates-recursive-inheritance-minimal-tuple/</link>
      <pubDate>Thu, 28 Nov 2024 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/cpp-variadic-templates-recursive-inheritance-minimal-tuple/</guid>
      <description>
        <![CDATA[<h1>C++变参模板实战：递归继承实现简易元组类</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>详细的讲解请看 CppMore 里缪大佬的 <a href="https://www.cppmore.com/2020/04/25/understanding-variadic-templates/">这篇文章</a>。</p>
<p>此处学习一下其中的使用 <strong>递归继承</strong> 技巧实现简易元组类这个例子。</p>
<p>直接 &ldquo;Show me the code!&rdquo;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">    先明晰省略运算符的意思：
</span></span></span><span class="line"><span class="cl"><span class="cm">       typename... T   --&gt; 把一堆类型折叠到 T 中
</span></span></span><span class="line"><span class="cl"><span class="cm">       T ...           --&gt; 从 T 中展开之前折叠的变量
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// #1: 先是 Tuple 类
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主模板
</span></span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span><span class="p">...</span> <span class="n">Types</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Tuple</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 全特化：作为终止递归的条件
</span></span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Tuple</span><span class="o">&lt;&gt;</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Head</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Tail</span><span class="o">&gt;</span>  <span class="c1">// 将传递进来的参数分为第 1 个（称之为 Head）和其余个（第 2～N 个，折叠在 Tail 里）
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Tuple</span><span class="o">&lt;</span><span class="n">Head</span><span class="p">,</span> <span class="n">Tail</span><span class="p">...</span><span class="o">&gt;</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">Tail</span><span class="p">...</span><span class="o">&gt;</span> <span class="p">{</span> <span class="c1">// 递归继承（采用公有继承）
</span></span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">Tuple</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">Tuple</span><span class="p">(</span><span class="n">Head</span> <span class="n">v</span><span class="p">,</span> <span class="n">Tail</span><span class="p">...</span> <span class="n">vtails</span><span class="p">)</span> <span class="o">:</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">Tail</span><span class="p">...</span><span class="o">&gt;</span><span class="p">(</span><span class="n">vtails</span><span class="p">...),</span> <span class="n">head_</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 用来返回 Tuple 类的内部成员 head_ 的值，也是这个 Tuple 的第一个值
</span></span></span><span class="line"><span class="cl">  <span class="n">Head</span> <span class="o">&amp;</span><span class="n">head</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">head_</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">protected</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// head_ : 第一个值的意思，比如作为 Tuple&lt;int, float, char&gt; t(5, 2.7, &#39;b&#39;) 其中的 5
</span></span></span><span class="line"><span class="cl">  <span class="n">Head</span> <span class="n">head_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// #2: 然后是 TupleAt 类
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主模板
</span></span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">I</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TList</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">TupleAt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 暂称 Tuple&lt;T, TList...&gt; 叫做“当前元组”
</span></span></span><span class="line"><span class="cl"><span class="c1">// 设计 TupleAt 的目的是获取到指定索引（I）处的元素的类型（称之为ValueType）（不必获取到具体的值，这个任务交给 Tuple 类的 head() 方法解决）
</span></span></span><span class="line"><span class="cl"><span class="c1">// ValueType 是“尾随元组”（意思是当前元组把第一项元素去掉，剩余的元素构成的元组）的第一个元素的类型
</span></span></span><span class="line"><span class="cl"><span class="c1">// TupleType 是“尾随元组”这一整体类型（它是当前元组的父类，因为 Tuple&lt;Head, Tail...&gt; 公有继承了 Tuple&lt;Tail...&gt;）
</span></span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">I</span><span class="p">,</span> <span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TList</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">TupleAt</span><span class="o">&lt;</span><span class="n">I</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="n">ValueType</span> <span class="o">=</span> <span class="k">typename</span> <span class="n">TupleAt</span><span class="o">&lt;</span><span class="n">I</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;::</span><span class="n">ValueType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="n">TupleType</span> <span class="o">=</span> <span class="k">typename</span> <span class="n">TupleAt</span><span class="o">&lt;</span><span class="n">I</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;::</span><span class="n">TupleType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 作为终止递归的条件：I = 0
</span></span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TList</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">TupleAt</span><span class="o">&lt;</span><span class="mi">0</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="n">ValueType</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="n">TupleType</span> <span class="o">=</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">TList</span><span class="p">...</span><span class="o">&gt;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// #3: 最后还剩个 TupleGet 函数模板
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* TupleGet&lt;I&gt;(tuple) 这个函数模板在使用的时候（比如 TupleGet&lt;2&gt;(t)）直观上接受两个参数：
</span></span></span><span class="line"><span class="cl"><span class="cm">                    其一，是模板参数 std::size_t I，表示索引；
</span></span></span><span class="line"><span class="cl"><span class="cm">                    其二，是函数参数 Tuple&lt;TList...&gt;&amp; tuple，表示目标元组
</span></span></span><span class="line"><span class="cl"><span class="cm">    TList 是目标元组 tuple 的模板参数们
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">I</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TList</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">typename</span> <span class="n">TupleAt</span><span class="o">&lt;</span><span class="n">I</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;::</span><span class="n">ValueType</span> <span class="o">&amp;</span>
</span></span><span class="line"><span class="cl"><span class="n">TupleGet</span><span class="p">(</span><span class="n">Tuple</span><span class="o">&lt;</span><span class="n">TList</span><span class="p">...</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="n">tuple</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="n">BaseTupleType</span> <span class="o">=</span> <span class="k">typename</span> <span class="n">TupleAt</span><span class="o">&lt;</span><span class="n">I</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="n">TList</span><span class="p">...</span><span class="o">&gt;&gt;::</span><span class="n">TupleType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">BaseTupleType</span> <span class="o">&amp;&gt;</span><span class="p">(</span><span class="n">tuple</span><span class="p">).</span><span class="n">head</span><span class="p">();</span> <span class="c1">// 把当前元组强制转换其类型为上一层元组（也就是当前元组的尾随元组），.head() 的结果就会变成尾随元组的 head_
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">Tuple</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">float</span><span class="p">,</span> <span class="kt">char</span><span class="o">&gt;</span> <span class="n">t</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">2.7</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">Tuple</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">Tuple</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">float</span><span class="p">,</span> <span class="kt">char</span><span class="o">&gt;</span><span class="p">,</span> <span class="kt">double</span><span class="o">&gt;</span> <span class="n">t2</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="mf">4.7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">TupleGet</span><span class="o">&lt;</span><span class="mi">2</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">TupleGet</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t2</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">TupleGet</span><span class="o">&lt;</span><span class="mi">2</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t2</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">TupleGet</span><span class="o">&lt;</span><span class="mi">2</span><span class="o">&gt;</span><span class="p">(</span><span class="n">TupleGet</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t2</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 嵌套元组也能正常处理哦
</span></span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这个程序实现了一个极简元组类，可以把不同类型的元素绑定在一起，并按下表索引访问元素。</p>
<p>每处所起到的作用已包含在注释里。这里举一个例子，简单看一下效果。</p>
<p>例如对于 <code>Tuple&lt;int, float, char&gt; t(1, 2.7, 'b')</code>，在 <code>TupleGet&lt;2&gt;(t)</code> 时会发生什么。预期的结果应该是返回 <code>char</code> 类型的 <code>'b'</code>。</p>
<p><code>TupleGet</code> 的递归过程可以用下面这张图表示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                                                强制转换
</span></span><span class="line"><span class="cl">  TupleAt&lt;2, &lt;int, float, char&gt;&gt;::TupleType &lt;-----+ 
</span></span><span class="line"><span class="cl">              ^~~  ^~~~~~~~~~~         |          | 
</span></span><span class="line"><span class="cl">               T      TList            |          | 
</span></span><span class="line"><span class="cl">                                       |         (t)
</span></span><span class="line"><span class="cl">                       ----------------+            
</span></span><span class="line"><span class="cl">                       |                            
</span></span><span class="line"><span class="cl">                       v                            
</span></span><span class="line"><span class="cl">     TupleAt&lt;1, &lt;float, char&gt;&gt;::TupleType           
</span></span><span class="line"><span class="cl">                                     |              
</span></span><span class="line"><span class="cl">                                     |              
</span></span><span class="line"><span class="cl">                     ----------------+              
</span></span><span class="line"><span class="cl">                     |                              
</span></span><span class="line"><span class="cl">                     v                              
</span></span><span class="line"><span class="cl">        TupleAt&lt;0, &lt;char&gt;&gt;::TupleType               
</span></span><span class="line"><span class="cl">                                 |                  
</span></span><span class="line"><span class="cl">                                 |                  
</span></span><span class="line"><span class="cl">                      -----------+                  
</span></span><span class="line"><span class="cl">                      |                             
</span></span><span class="line"><span class="cl">                      v                             
</span></span><span class="line"><span class="cl">                  Tuple&lt;char&gt;                       
</span></span></code></pre></div><p>首先在第 71 行处，<code>t</code> 会被强制类型转换成 <code>TupleAt&lt;2, &lt;int, float, char&gt;&gt;::TupleType</code> 类型。那么这一长串类型到底是什么呢？</p>
<p>由第 48 行可以知道，它会变成 <code>TupleAt&lt;1, &lt;float, char&gt;&gt;::TupleType</code>，这一所谓 “变成” 实际上是通过将传递进来的参数分为第 1 个和其余个（第 2～N 个）。这样每次传递进来的参数会依次减少，达到遍历所有参数的效果（引自 <a href="https://www.cppmore.com/2020/04/25/understanding-variadic-templates/">里缪的文章</a>）。递归的每一层，都只保留了 <code>TList</code>，舍弃了 <code>T</code>。</p>
<p>同理，<code>TupleAt&lt;1, &lt;float, char&gt;&gt;::TupleType</code> 会变成 <code>TupleAt&lt;0, &lt;char&gt;&gt;::TupleType</code>，最终通过 <code>I = 0</code> 的偏特化版本变成 <code>Tuple&lt;char&gt;</code>，而此时的 <code>ValueType</code> 是 <code>char</code>，正是我们想要的索引为 2 的那个元素的类型。</p>
<p>同时，<code>Tuple&lt;char&gt;</code> 也是 <code>Tuple&lt;int, float, char&gt;</code> 的基类，因此要通过强制类型转换把 <code>Tuple&lt;int, float, char&gt;</code> 转换成 <code>Tuple&lt;char&gt;</code>，这么做是为了一层一层地舍弃当前元组的 <code>head</code> 而保留基类元组的 <code>head</code>，实现了递归展开的效果。（具体分析和设计思路请看 <a href="https://www.cppmore.com/2020/04/25/understanding-variadic-templates/">里缪的文章</a>）</p>

        
        <hr><p>本文2024-11-28首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2024-11-28</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>在 Arch Linux 上编译 bloomberg/clang-p2996 小记</title>
      <link>https://zzhx.cc/post/compiling-clang-p2996-on-arch-linux/</link>
      <pubDate>Sun, 24 Nov 2024 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/compiling-clang-p2996-on-arch-linux/</guid>
      <description>
        <![CDATA[<h1>在 Arch Linux 上编译 bloomberg/clang-p2996 小记</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>本文简要记录笔者在 Arch Linux 上编译一个部分支持 C++ 反射的 clang 编译器的过程。</p>
<p>期待 Reflection 能正式被编译器实现的那一天。  :)</p>
<p>本次实验受群友 Yiran Wang 启发。</p>
<h2 id="克隆仓库">
<a class="header-anchor" href="#%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93"></a>
克隆仓库
</h2><p>GitHub 仓库在 <a href="https://github.com/bloomberg/clang-p2996/tree/p2996">这里</a>，有关这个编译器的介绍详情请看仓库（默认是 purpose 分支，代码在 p2996 分支上）。</p>
<p>先克隆仓库到本地：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git clone https://github.com/bloomberg/clang-p2996 --depth <span class="m">1</span> --branch<span class="o">=</span>p2996
</span></span></code></pre></div><p>使用 <code>--depth 1</code> 选项只克隆最新版本的 commit，可大大减小仓库体积（好像只有两百多兆）。</p>
<p>有的仓库有很多分支（比如 llvm 或者 gcc 官方仓库），使用 <code>--branch=xxx</code> 指定克隆某一个分支也可以减小体积。本仓库只有两个分支，作用不大。</p>
<p>建议上网搜一下镜像站，在高级搜索中调整搜索范围为近一周左右，基本就可以找到可用的镜像站，实测有助于提高网速。</p>
<h2 id="编译-clang">
<a class="header-anchor" href="#%e7%bc%96%e8%af%91-clang"></a>
编译 Clang
</h2><p>编译选项和步骤可以参考 <a href="https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm">llvm 官网手册中的介绍</a>，此处给出我的生成命令，供参考：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ <span class="nb">cd</span> clang-p2996
</span></span><span class="line"><span class="cl">$ mkdir build
</span></span><span class="line"><span class="cl">$ <span class="nb">cd</span> build
</span></span><span class="line"><span class="cl">$ cmake -DLLVM_ENABLE_PROJECTS<span class="o">=</span>clang <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_C_COMPILER<span class="o">=</span>clang <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_CXX_COMPILER<span class="o">=</span>clang++ <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_ENABLE_RUNTIMES<span class="o">=</span><span class="s2">&#34;libc;libcxx;libcxxabi;libunwind&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_C_FLAGS<span class="o">=</span><span class="s2">&#34;-O3 -march=native -mtune=native&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_CXX_FLAGS<span class="o">=</span><span class="s2">&#34;-O3 -march=native -mtune=native&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_BUILD_TYPE<span class="o">=</span>Release <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_USE_LINKER<span class="o">=</span>lld <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_BUILD_TOOLS<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_BUILD_EXAMPLES<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_BUILD_TESTS<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_INCLUDE_TESTS<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_INCLUDE_DOCS<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DLLVM_TARGETS_TO_BUILD<span class="o">=</span><span class="s2">&#34;X86&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        -DCMAKE_INSTALL_PREFIX<span class="o">=</span>/usr/local/llvm-build <span class="se">\
</span></span></span><span class="line"><span class="cl">        -G <span class="s2">&#34;Unix Makefiles&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        ../llvm
</span></span></code></pre></div><p>具体每一个选项是什么意思以及有哪些可选参数，自己 STFW 或者问 AI。此处仅为精简编译（仅编译 clang），没有添加其他的各种工具。有需者请自选。</p>
<p>值得注意的是，<code>-DLLVM_ENABLE_RUNTIMES</code> 应该至少有 <code>libc;libcxx;libcxxabi;libunwind</code>（感谢群友 Yiran Wang）。</p>
<p>强行添加了 <code>-O3 -march=native -mtune=native</code>，纯属个人喜好。</p>
<p><code>-DCMAKE_INSTALL_PREFIX=/usr/local/llvm-build</code> 的结果是 <code>llvm-build</code> 下有 <code>bin</code>、<code>lib</code> 等等这些。以免有人怀疑是否会出现重叠安装目录的情况。</p>
<p>如果有提示缺少依赖就安装相应的依赖（貌似是不缺）。</p>
<p>编译的时候，需要注意一下编译使用的任务数，因为可能会遇到内存瓶颈。我在 14 核心 20 线程的笔记本 U 上使用 <code>make -j20</code> 编译会在进度到 55% 左右时直接吃满 32G 内存 + 9G 交换内存导致桌面卡死。解决方法之一是改为 <code>make -j14</code>。</p>
<h2 id="测试运行">
<a class="header-anchor" href="#%e6%b5%8b%e8%af%95%e8%bf%90%e8%a1%8c"></a>
测试运行
</h2><p>先贴一个简单的编译命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ /usr/local/llvm-build/bin/clang++ main.cpp <span class="se">\
</span></span></span><span class="line"><span class="cl">                          --std<span class="o">=</span>c++26 <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -I/usr/local/llvm-build/include/c++/v1 <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -I/usr/local/llvm-build/include/x86_64-unknown-linux-gnu/c++/v1 <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -freflection <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -stdlib<span class="o">=</span>libc++ <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -Wl,-rpath,/usr/local/llvm-build/lib/x86_64-unknown-linux-gnu <span class="se">\
</span></span></span><span class="line"><span class="cl">                          -g3 -Og -march<span class="o">=</span>native -mtune<span class="o">=</span>native
</span></span></code></pre></div><p>别忘了使用 <code>-freflection</code>。</p>
<p><code>-stdlib=libc++</code> 必不可少，否则一顿 <code>-I</code> 也会报错。</p>
<p>仅仅 <code>-L/usr/local/llvm-build/lib/x86_64-unknown-linux-gnu</code> 貌似不行。</p>
<p>贴一段简单的测试程序：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;experimental/meta&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;print&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">r</span> <span class="o">=</span> <span class="o">^</span><span class="kt">int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">typename</span><span class="p">[</span><span class="o">:</span><span class="nl">r</span><span class="p">:]</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>      <span class="c1">// Same as: int x = 42;
</span></span></span><span class="line"><span class="cl">    <span class="k">typename</span><span class="p">[</span><span class="o">:^</span><span class="kt">char</span><span class="o">:</span><span class="p">]</span> <span class="n">c</span> <span class="o">=</span> <span class="sc">&#39;*&#39;</span><span class="p">;</span> <span class="c1">// Same as: char c = &#39;*&#39;;
</span></span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;{}&#34;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">same_as</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="kt">int</span><span class="o">&gt;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">println</span><span class="p">(</span><span class="s">&#34;{}&#34;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">same_as</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="n">c</span><span class="p">),</span> <span class="kt">char</span><span class="o">&gt;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">true
</span></span><span class="line"><span class="cl">true
</span></span></code></pre></div>
        
        <hr><p>本文2024-11-24首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2024-11-24</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>C&#43;&#43; &lt;chrono&gt; 库中的高效闰年判断算法解析</title>
      <link>https://zzhx.cc/post/efficient-leap-year-check-cpp-chrono-optimization/</link>
      <pubDate>Tue, 08 Oct 2024 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/efficient-leap-year-check-cpp-chrono-optimization/</guid>
      <description>
        <![CDATA[<h1>C++ <chrono> 库中的高效闰年判断算法解析</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <p>在 C++ <code>&lt;chrono&gt;</code> 库中，有如下判断闰年的算法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">year</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	<span class="kt">short</span> <span class="n">_M_y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	<span class="k">constexpr</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl">	<span class="n">is_leap</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="p">(</span><span class="n">_M_y</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">_M_y</span> <span class="o">%</span> <span class="mi">25</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">15</span> <span class="o">:</span> <span class="mi">3</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>根据源代码的注释，总结一下优化思路：</p>
<p>判断闰年的规则是：</p>
<ul>
<li>如果年份能被 4 整除且不能被 100 整除，则是闰年.</li>
<li>如果年份能被 100 整除，则还必须能被 400 整除，才是闰年.</li>
</ul>
<p>先判断能否被 100 整除比其他思路更快，因为它减少了不必要的计算. 参考<a href="https://github.com/cassioneri/calendar">这里</a>.</p>
<p>所以，逻辑优化成了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">return</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">100</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">400</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><p>当知道一个数字能被 100 整除时，能被 400 整除实际上意味着它也可以被 16 整除.</p>
<p>标准库在第一步判断时没有使用 100 而是 25，因为检查被 25 整除的速度更快（汇编少了一句，参考<a href="https://godbolt.org/z/55G8rn77e">这里</a>），而且仍然管用.</p>
<p>因此，进一步优化为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">return</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">25</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">16</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">_M_y</span> <span class="o">%</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li>
<p>如果一个数不能被 4 整除，那么它肯定也不能被 16 整除，所以在这种情况下，无论能否被 25 整除，都应该返回 <code>false</code>.</p>
</li>
<li>
<p>如果一个数能被 4 整除，那么它能被 25 整除当且仅当它能被 100 整除. 这是因为任何能被 25 整除的数都是以 00、25、50 或 75 结尾的，而只有以 00 结尾的数也能被 100 整除.</p>
</li>
</ul>
<p>因此，优化前后的逻辑是等价的.</p>
<p>对于位运算：</p>
<ul>
<li>
<p>如果年份 y 能被 25 整除，那么 y 的最后两位必须是 00、25、50 或 75，此时 <code>y &amp; 15</code> 将检查 y 的最后四位是否都是 0，这等价于检查 y 能否被 100 整除.</p>
</li>
<li>
<p>如果年份 y 不能被 25 整除，那么 <code>y &amp; 3</code> 将检查 y 的最后两位能否被 4 整除.</p>
</li>
</ul>
<p>最终优化结果为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="nf">is_leap</span><span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="p">(</span><span class="n">y</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">25</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">15</span> <span class="o">:</span> <span class="mi">3</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>你学会了吗.</p>

        
        <hr><p>本文2024-10-08首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2024-10-08</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>如何为 gcc 贡献中文翻译</title>
      <link>https://zzhx.cc/post/how-to-contribute-chinese-translation-for-gcc/</link>
      <pubDate>Sat, 14 Sep 2024 00:00:00 &#43;0000</pubDate>
      <author>zzhx2006@outlook.com (M81)</author>
      <guid>https://zzhx.cc/post/how-to-contribute-chinese-translation-for-gcc/</guid>
      <description>
        <![CDATA[<h1>如何为 gcc 贡献中文翻译</h1><p>作者：M81（zzhx2006@outlook.com）</p>
        
          <h2 id="在本地试验">
<a class="header-anchor" href="#%e5%9c%a8%e6%9c%ac%e5%9c%b0%e8%af%95%e9%aa%8c"></a>
在本地试验
</h2><p>先在本地试一试修改 gcc 的翻译吧. 新建一个用来折腾的文件夹 <code>~/test/</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ mkdir ~/test
</span></span><span class="line"><span class="cl">$ <span class="nb">cd</span> ~/test
</span></span></code></pre></div><h3 id="下载最新翻译">
<a class="header-anchor" href="#%e4%b8%8b%e8%bd%bd%e6%9c%80%e6%96%b0%e7%bf%bb%e8%af%91"></a>
下载最新翻译
</h3><p>下载 Translation Project 上的最新版 gcc 简体中文翻译文件. 截至发稿时，gcc 最新版本为 14.2.0 版.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ wget https://translationproject.org/PO-files/zh_CN/gcc-14.2.0.zh_CN.po
</span></span></code></pre></div><p>下载了一个叫做 gcc-14.2.0.zh_CN.po 的文件.</p>
<p>关于 Translation Project，AI 是这样介绍的：</p>
<blockquote>
<p>GNU 翻译项目（GNU Translation Project）是一项由社区推动的倡议，致力于将 GNU 软件的用户界面、文档和手册翻译成多种语言，以实现软件的国际化和本地化. 该项目欢迎志愿者参与，提供翻译工具和资源，旨在确保自由软件能够跨越语言障碍，惠及全球用户，增强其可及性和用户体验.</p>
</blockquote>
<p>gcc 的最新版翻译文件和其他语言翻译可以在<a href="https://translationproject.org/domain/gcc.html">这里</a>找到；其他软件的翻译项目可以在<a href="https://translationproject.org/domain/index.html">这里</a>找到. 这些都可以在<a href="https://translationproject.org/html/welcome.html">官网</a>找到，我们先暂时不管它们.</p>
<h3 id="编辑翻译文件">
<a class="header-anchor" href="#%e7%bc%96%e8%be%91%e7%bf%bb%e8%af%91%e6%96%87%e4%bb%b6"></a>
编辑翻译文件
</h3><p>接下来需要编辑这个 .po 文件，选择其中的词条（称为 <code>msgid</code>）翻译成中文（在 <code>msgstr</code>）.</p>
<p>当然可以用文本编辑器直接开干. 更推荐的是用专门的编辑 .po 文件的软件来干活更方便啦.</p>
<p>这里推荐使用 Poedit 软件. 下载安装（比如 Arch Linux；其他各发行版请使用各自的工具安装）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ sudo pacman -S poedit
</span></span></code></pre></div><p>然后右键点击 gcc-14.2.0.zh_CN.po，选择“使用 Poedit 打开”.</p>
<p><img src="/post/how-to-contribute-chinese-translation-for-gcc/img1.avif" alt="选择“使用 Poedit 打开”" title="使用 Poedit 打开"></p>
<p>可以看到如下的工作区. 其中没有中文的黑色条目是缺少翻译的；有中文但黄色字体的是“需要处理”的（带“fuzzy”标签），表示中文翻译仍不准确，需要修改.</p>
<p><img src="/post/how-to-contribute-chinese-translation-for-gcc/img2.avif" alt="Poedit 页面" title="Poedit 页面"></p>
<p>黄色的条目即使有中文翻译，但是被标记了 fuzzy 标签（Poedit 显示“需要处理”），最终仍不会显示出来.</p>
<p>我们选择有待处理的条目，在下方翻译区输入自己的中文翻译.</p>
<p>翻译完成后，点击保存. Poedit 会先验证文件是否有问题. 若无任何错误提示，说明已经成功保存了修改. 而且可以在 <code>~/test/</code> 中看到已经自动编译好了的 .mo 文件.</p>
<p>由于 .po 文件以文本格式存储，计算机查找速度慢，故需先将 .po 文件编译为 .mo 二进制文件，才能正常使用.</p>
<p>也可以手动输入命令来编译：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ msgfmt gcc-14.2.0.zh_CN.po -o gcc-14.2.0.zh_CN.mo
</span></span></code></pre></div><p>若提示找不到 <code>msgfmt</code> 命令，需下载安装 <code>gettext</code> 包：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ sudo pacman -S gettext
</span></span></code></pre></div><h3 id="替换系统翻译">
<a class="header-anchor" href="#%e6%9b%bf%e6%8d%a2%e7%b3%bb%e7%bb%9f%e7%bf%bb%e8%af%91"></a>
替换系统翻译
</h3><p>接下来，先备份系统中的 gcc.mo，然后用编译好的 .mo 文件替换系统中预设的 gcc.mo：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ mv /usr/share/locale/zh_CN/LC_MESSAGES/gcc.mo /usr/share/locale/zh_CN/LC_MESSAGES/gcc.mo.old
</span></span><span class="line"><span class="cl">$ cp ~/test/gcc-14.2.0.zh_CN.mo /usr/share/locale/zh_CN/LC_MESSAGES/gcc.mo
</span></span></code></pre></div><p>这样，就成功地将 gcc 的英文提示翻译成中文啦！当然，可能大多数词条不那么容易显示出来. 你可以在使用 gcc 的过程中看到英文提示时，复制英文提示，在 Poedit 中查找到这个未翻译的词条，将它翻译成中文. 然后按照上述操作，就可以看到原来的英文提示变成中文啦.</p>
<h2 id="申请加入翻译团队">
<a class="header-anchor" href="#%e7%94%b3%e8%af%b7%e5%8a%a0%e5%85%a5%e7%bf%bb%e8%af%91%e5%9b%a2%e9%98%9f"></a>
申请加入翻译团队
</h2><p>那么，如何将自己的翻译上传到官方项目中，为翻译工作做贡献呢？</p>
<p>所有信息在<a href="https://translationproject.org/html/translators.html">官网</a>上都可以查到，这里仅总结一下操作流程.</p>
<p>首先加入简体中文翻译团队的<a href="http://groups.google.com/group/i18n-zh">邮件列表</a>来接收成员们发送的邮件. 成员们可能会在邮件列表中讨论翻译项目，协调翻译工作，分享翻译相关的资源，寻求帮助或建议等.</p>
<p>接下来，你需要填写一份免责声明（disclaimer）. 在<a href="https://crm.fsf.org/civicrm/profile/create?gid=91&amp;reset=1">此处</a>填写你的姓、名、填写日期和电子邮件地址，勾选“Yes”，点击“Save”. 以后使用这个电子邮件来提交自己的翻译.</p>
<p>然后分别向 <a href="mailto:coordinator@translationproject.org">Translation Project Coordinator</a> 和简体中文团队负责人 <a href="mailto:073plan@gmail.com">Boyuan Yang</a> 发送邮件，请求他们同意你加入<a href="https://translationproject.org/team/zh_CN.html">简体中文翻译团队</a>. 只有团队成员才有权提交翻译.</p>
<p>大约两三天后，会陆续收到抄送的邮件，是负责人和 Coordinator 沟通同意加入申请.</p>
<p>成功加入后，你就可以在<a href="https://translationproject.org/team/zh_CN.html">简体中文翻译团队</a>的 Translator 列中找到自己的名字啦.</p>
<p>在这个页面中还可以看到各个软件包的简体中文翻译进度. 有的软件包还被指定了译员（Assigned translator）. 点击包名称即可跳转到它的主页面；点击版本号可以查看和下载最新的 .po 翻译.</p>
<h2 id="提交翻译">
<a class="header-anchor" href="#%e6%8f%90%e4%ba%a4%e7%bf%bb%e8%af%91"></a>
提交翻译
</h2><p>现在，你要使用 Translation Project robot (or TP-robot) 来将自己的 .po 文件提交到项目中.</p>
<p>你可以在<a href="https://translationproject.org/html/robot.html">此处</a>查看关于 TP-robot 的说明. 官网是这么介绍的：</p>
<blockquote>
<p>翻译项目机器人（或 TP-robot）是一个处理 PO 文件提交的电子邮件服务. 它检查文件是否可以被接受——也就是说，检查译者是否填写了她的免责声明（在需要的地方）——以及她的团队是否允许她进行这项工作. 它还会调用 <code>msgfmt</code> 来查看 PO 文件是否健全，并检查其他各种问题.</p>
</blockquote>
<h3 id="下载提交脚本">
<a class="header-anchor" href="#%e4%b8%8b%e8%bd%bd%e6%8f%90%e4%ba%a4%e8%84%9a%e6%9c%ac"></a>
下载提交脚本
</h3><p>你需要下载<a href="https://translationproject.org/extra/sendpo.sh">一个 shell 脚本</a>，用来处理你的 .po 文件，并将其发送给 TP-robot：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ wget https://translationproject.org/extra/sendpo.sh
</span></span><span class="line"><span class="cl">$ chmod +x sendpo.sh
</span></span></code></pre></div><p>要想让脚本正确处理 .po 文件，需要让 .po 文件的文件名符合 <code>&lt;软件包名称&gt;-&lt;版本号&gt;.&lt;语言代码&gt;.po</code> 的格式. 例如 <code>gcc-14.2.0.zh_CN.po</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ mv 不规范的文件名.po gcc-14.2.0.zh_CN.po
</span></span></code></pre></div><h3 id="解决一些小问题">
<a class="header-anchor" href="#%e8%a7%a3%e5%86%b3%e4%b8%80%e4%ba%9b%e5%b0%8f%e9%97%ae%e9%a2%98"></a>
解决一些小问题
</h3><p>按照正常流程，现在应该使用 <code>sendpo.sh</code> 发送翻译文件. 但是在使用它之前，需要先处理掉几个小问题（笔者在此处停顿了很长时间）.</p>
<p>首先，按照要求，用编辑器打开 <code>sendpo.sh</code>，更改第 5 行 <code>USERLANG=&quot;&quot;</code> 为 <code>USERLANG=&quot;zh_CN&quot;</code>.</p>
<p>然后，观察文件末尾. 不难发现使用到了 <code>gzip</code>, <code>uuencode</code> 和 <code>mail</code> 命令.</p>
<p>先解决掉前两个：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ sudo pacman -S gzip
</span></span><span class="line"><span class="cl">$ sudo pacman -S sharutils
</span></span></code></pre></div><p>至于最后一个 <code>mail</code> 命令，我们将其替换为更现代的邮件客户端（比如 outlook）.</p>
<p>删除文件末两行，替换为如下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ gzip &lt;<span class="nv">$file</span> <span class="p">|</span> uuencode -m <span class="nv">$name</span>.gz <span class="p">|</span> tee ./output.txt
</span></span></code></pre></div><p><img src="/post/how-to-contribute-chinese-translation-for-gcc/img3.avif" alt="替换为上面的命令" title="修改末尾两行"></p>
<p>执行命令来处理 .po 文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ ./sendpo.sh gcc-14.2.0.zh_CN.po
</span></span></code></pre></div><p>如果没有错误提示，会在目录下生成 <code>messages.mo</code> 和 <code>output.txt</code>.</p>
<p>打开 <code>output.txt</code>，复制文件内容.</p>
<h3 id="使用客户端发送">
<a class="header-anchor" href="#%e4%bd%bf%e7%94%a8%e5%ae%a2%e6%88%b7%e7%ab%af%e5%8f%91%e9%80%81"></a>
使用客户端发送
</h3><p>打开邮件客户端（比如 outlook）. 将邮件正文的样式切换到“纯文本”；发件人需为之前在 Disclaimer 中填写的邮件地址；收件人填 <a href="mailto:robot@translationproject.org">robot@translationproject.org</a>；邮件主题填 TP-robot gcc-14.2.0.zh_CN.po；邮件正文（不是附件）则为刚刚生成的 <code>output.txt</code> 的内容，复制粘贴即可.</p>
<p>成功发送邮件，几分钟后就会收到来自 TP-robot 的邮件反馈.</p>
<h3 id="收到邮件反馈">
<a class="header-anchor" href="#%e6%94%b6%e5%88%b0%e9%82%ae%e4%bb%b6%e5%8f%8d%e9%a6%88"></a>
收到邮件反馈
</h3><p>若提示：</p>
<blockquote>
<p>***&gt; According to my notes, XXX is not a member of the
Chinese (simplified) team.</p>
</blockquote>
<p>说明未成功加入简体中文翻译团队. 只加入邮件列表和提交 Disclaimer 并不足够，还要向 Translation Project Coordinator 和简体中文团队负责人发送邮件，请求他们同意你加入简体中文翻译团队.</p>
<p>若提示：</p>
<blockquote>
<p>***&gt; Your PO file seems to have no entries at all, like if it were
empty.</p>
</blockquote>
<p>请检查 .po 文件是否有问题；注意应该发送的是 .po 的 base64 编码（而不是 .po.gz 的）.</p>
<p>若提示：</p>
<blockquote>
<p>Your file has been accepted and stored in the archives.  Thank you!</p>
</blockquote>
<p>恭喜你成功地为项目贡献了自己的翻译！可以在<a href="https://translationproject.org/domain/gcc.html">此处</a>看到最新的提交者.</p>

        
        <hr><p>本文2024-09-14首发于<a href='https://zzhx.cc/'>M81's Website</a>，最后修改于2024-09-14</p>]]>
      </description>
      
    </item>
    
  </channel>
</rss>
