<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>文章 on T.本秋的自留地</title><link>https://minortex.github.io/posts/</link><description>Recent content in 文章 on T.本秋的自留地</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Copyright© 2026 T.本秋. Licensed under CC BY-NC-SA 4.0.</copyright><lastBuildDate>Tue, 05 May 2026 16:49:51 +0800</lastBuildDate><atom:link href="https://minortex.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>简单了解和手动使用 TPM</title><link>https://minortex.github.io/posts/tpm/</link><pubDate>Tue, 05 May 2026 16:49:51 +0800</pubDate><guid>https://minortex.github.io/posts/tpm/</guid><description>&lt;p&gt;最近疯狂迷恋 tpm，这个小玩意究竟是怎么实现基于设备的安全的？&lt;/p&gt;
&lt;h2 class="relative group"&gt;tpm 的相关属性
&lt;div id="tpm-的相关属性" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tpm-%e7%9a%84%e7%9b%b8%e5%85%b3%e5%b1%9e%e6%80%a7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;第一步，我们先看下 tpm 有什么变量是可以被改变的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2_getcap properties-variable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;第一组：永久属性组 (PT -&amp;gt; Property)
&lt;div id="第一组永久属性组-pt---property" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%ac%e4%b8%80%e7%bb%84%e6%b0%b8%e4%b9%85%e5%b1%9e%e6%80%a7%e7%bb%84-pt---property" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_PERMANENT:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ownerAuthSet: &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 所有者鉴权密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; endorsementAuthSet: &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 背书鉴权密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lockoutAuthSet: &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 锁定鉴权密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reserved1: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; disableClear: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inLockout: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tpmGeneratedEPS: &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reserved2: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;第二组：启动时状态
&lt;div id="第二组启动时状态" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%ac%e4%ba%8c%e7%bb%84%e5%90%af%e5%8a%a8%e6%97%b6%e7%8a%b6%e6%80%81" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_STARTUP_CLEAR:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; phEnable: &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# 平台层级，也就是你可以在系统里面清除tpm所有信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shEnable: &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# 存储层级，其实跟owner是一样的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ehEnable: &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# 背书层级&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; phEnableNV: &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reserved1: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; orderly: &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;第三组：句柄资源 (HR -&amp;gt; Handle Resource)
&lt;div id="第三组句柄资源-hr---handle-resource" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%ac%e4%b8%89%e7%bb%84%e5%8f%a5%e6%9f%84%e8%b5%84%e6%ba%90-hr---handle-resource" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_NV_INDEX: 0x5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_LOADED: 0x0 &lt;span class="c1"&gt;# 当前已加载密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_LOADED_AVAIL: 0x3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_ACTIVE: 0x0 &lt;span class="c1"&gt;# 已激活的会话&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_ACTIVE_AVAIL: 0x40
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_TRANSIENT_AVAIL: 0x3 &lt;span class="c1"&gt;# 临时会话，从存储读入内存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_PERSISTENT: 0x0 &lt;span class="c1"&gt;# 可以永久存储的槽位&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_HR_PERSISTENT_AVAIL: 0x2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;第四组：锁定相关配置
&lt;div id="第四组锁定相关配置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%ac%e5%9b%9b%e7%bb%84%e9%94%81%e5%ae%9a%e7%9b%b8%e5%85%b3%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_LOCKOUT_COUNTER: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_MAX_AUTH_FAIL: 0xC8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_LOCKOUT_INTERVAL: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_LOCKOUT_RECOVERY: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;第五组：杂项
&lt;div id="第五组杂项" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%ac%e4%ba%94%e7%bb%84%e6%9d%82%e9%a1%b9" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_NV_COUNTERS: 0x0 &lt;span class="c1"&gt;# 非易失标志位&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_NV_COUNTERS_AVAIL: 0x8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_ALGORITHM_SET: 0xFFFFFFFF
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_LOADED_CURVES: 0x3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_AUDIT_COUNTER_0: 0x0 &lt;span class="c1"&gt;# 审计日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TPM2_PT_AUDIT_COUNTER_1: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 class="relative group"&gt;不同层级
&lt;div id="不同层级" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%8d%e5%90%8c%e5%b1%82%e7%ba%a7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Owner Hierarchy&lt;/strong&gt;: 你平时用来创建密钥、存储数据的权限。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Endorsement Hierarchy&lt;/strong&gt;: 涉及设备隐私和证书的权限。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lockout Hierarchy&lt;/strong&gt;: “锁定控制”权限，用于控制锁定策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许多少次普通密码尝试后进入锁定模式 (TPM2_PT_MAX_AUTH_FAIL)&lt;/li&gt;
&lt;li&gt;被记录下连续失败后，过多久次数才能减一 (TPM2_PT_LOCKOUT_INTERVAL)&lt;/li&gt;
&lt;li&gt;进入锁定模式后，过多久才能解锁 (TPM2_PT_LOCKOUT_RECOVERY)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️注意：如果你是在验证以上三种层级的管理密码，比如用 tpm2_clear -C o -c -p &lt;password&gt;的时候，只有一次机会，失败立即进入锁定模式。&lt;/p&gt;
&lt;p&gt;你将被限制到 TPM2_PT_LOCKOUT_RECOVERY 的时间秒数之后才能解锁。
这两个时间设成 0，永远解不了，只能清除。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Platform Hierarchy&lt;/strong&gt;: 唯一不受 DA 限制的密码尝试。但是就算你拿到了权限，除了能够清除所有的密钥和密码，没有任何别的权限。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 class="relative group"&gt;初始化自己的 tpm 配置
&lt;div id="初始化自己的-tpm-配置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9d%e5%a7%8b%e5%8c%96%e8%87%aa%e5%b7%b1%e7%9a%84-tpm-%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;清除&lt;/strong&gt;：&lt;code&gt;tpm2_clear&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;有的平台可以直接用&lt;code&gt;tpm2_clear -c p&lt;/code&gt;清掉，有的不行只能到 bios 里面清。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修改锁定设置&lt;/strong&gt;：&lt;code&gt;tpm2_dictionarylockout&lt;/code&gt; (也叫 DA，dictionary attack)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 分别对应上面的“锁定层级”的三个变量，下例为windows标准设置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2_dictionarylockout -s -n &lt;span class="m"&gt;32&lt;/span&gt; -t &lt;span class="m"&gt;7200&lt;/span&gt; -l &lt;span class="m"&gt;86400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;修改密码&lt;/strong&gt;：&lt;code&gt;tpm2_changeauth&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2_changeauth -c lockout &amp;lt;new_password&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里有个彩蛋，windows 一开始看到新的 tpm，跟我们一样改完了，直接把钥匙扔了，惊喜吧？&lt;/p&gt;
&lt;p&gt;所以你只能重置，然后自己设个密码，好在 windows 不会改你的密码回去。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 class="relative group"&gt;开始使用
&lt;div id="开始使用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;创建主密钥，得到一个 tpm 的内存快照，这个主密钥，相比于密钥的说法，其实更是一个 token，因为是由 TPM 这一个生命周期的种子派生的，所以每次用同样的输入得到同样的输出，这里不是指的 ctx 的 hash 相同，而是每次读取的公钥相同。&lt;/p&gt;
&lt;p&gt;如果还原了 tpm，签名会变，ctx 永远不再可用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 创一个新的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_createprimary -C o -c primary2.ctx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name-alg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: sha256
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0xb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;attributes:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: fixedtpm&lt;span class="p"&gt;|&lt;/span&gt;fixedparent&lt;span class="p"&gt;|&lt;/span&gt;sensitivedataorigin&lt;span class="p"&gt;|&lt;/span&gt;userwithauth&lt;span class="p"&gt;|&lt;/span&gt;restricted&lt;span class="p"&gt;|&lt;/span&gt;decrypt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x30072
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;type:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;exponent: &lt;span class="m"&gt;65537&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bits: &lt;span class="m"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scheme:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scheme-halg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: &lt;span class="o"&gt;(&lt;/span&gt;null&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-alg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: aes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-mode:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: cfb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x43
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-keybits: &lt;span class="m"&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 读一个之前创的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_readpublic -c primary.ctx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name: 000bf2c2daef8da6f674f7cdd47edb780e14baec9ed14a212904d79ca92c93e05c7c
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qualified name: 000b8a569c59019bf73a23befe7aeb9e54c74470737c0288f63d2481324388e1edfb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name-alg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: sha256
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0xb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;attributes:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: fixedtpm&lt;span class="p"&gt;|&lt;/span&gt;fixedparent&lt;span class="p"&gt;|&lt;/span&gt;sensitivedataorigin&lt;span class="p"&gt;|&lt;/span&gt;userwithauth&lt;span class="p"&gt;|&lt;/span&gt;restricted&lt;span class="p"&gt;|&lt;/span&gt;decrypt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x30072
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;type:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;exponent: &lt;span class="m"&gt;65537&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bits: &lt;span class="m"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scheme:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scheme-halg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: &lt;span class="o"&gt;(&lt;/span&gt;null&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-alg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: aes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-mode:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; value: cfb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; raw: 0x43
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sym-keybits: &lt;span class="m"&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;加密文字
&lt;div id="加密文字" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8a%a0%e5%af%86%e6%96%87%e5%ad%97" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在主密钥下面创建一个子密钥：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tpm2_create -C primary.ctx &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u key.pub -r key.priv &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -i- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p &lt;span class="s2"&gt;&amp;#34;123456&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;生成一个 tpm 的内存快照：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把它持久化到 tpm 的对象里面：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2_evictcontrol -C o -c key.ctx 0x81010002
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;有点乱？总结一下&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;命令&lt;/strong&gt;说明：
tpm2_create 指的是创建一个&amp;quot;密封信件&amp;quot;,这个信件有公钥来标识身份，有加密 blob 来给 tpm 读。
tpm2_load 则是让 tpm 读取这个密封信件，他生成一个内存快照 (token)，这个东西可以持久化来被 tpm 引用（成为永久句柄）。
tpm2_evictcontrol 如果-c 加的是文件，然后再跟上地址，语义就是把这个文件持久化到 tpm 的一个句柄中；如果-c 加的是一个地址，那么则是把这个持久句柄驱逐。&lt;/p&gt;
&lt;p&gt;对这里出现的&lt;strong&gt;文件&lt;/strong&gt;进行说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key.pub 可以开放给别人看&lt;/li&gt;
&lt;li&gt;key.priv 是被 tpm 加密的对象，只有 tpm 才能解密&lt;/li&gt;
&lt;li&gt;key.ctx 是 tpm 的内存快照，和 0x 开头的是同一个类型的东西，都可以被&lt;code&gt;-c&lt;/code&gt;加载&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;所以我们给他解个密：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_unseal -c key.ctx -p &lt;span class="m"&gt;123456&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_unseal -c 0x81010002 -p &lt;span class="m"&gt;123456&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello%
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;验证签名
&lt;div id="验证签名" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%aa%8c%e8%af%81%e7%ad%be%e5%90%8d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;既然懂了一些，我们就不要用 rsa 了，用 ecc，只需把所有的创建密钥的过程加&lt;code&gt;-G ecc&lt;/code&gt;就行。&lt;/p&gt;
&lt;p&gt;如果要删除之前的，善用 evictcontrol。&lt;/p&gt;
&lt;p&gt;重新创建 ecc 的主密钥：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tpm2_createprimary -C o -G ecc -c ecc_primary.ctx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个签名的对象：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tpm2_create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -C ecc_primary.ctx &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -G ecc256:ecdsa &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u ecc.pub &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -r ecc.priv &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p &lt;span class="m"&gt;123456&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;加载：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tpm2_load &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -C ecc_primary.ctx &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u ecc.pub &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -r ecc.priv &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c ecc.ctx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;持久化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2_evictcontrol -C o -c ecc.ctx 0x81020000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;生成一个固定的挑战，用于验证（其实真正的过程中，每次都是不同的）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;head -c &lt;span class="m"&gt;32&lt;/span&gt; /dev/urandom &amp;gt; challenge.bin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用 tpm 自带的工具验证：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_sign &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c 0x81020000 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -g sha256 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -s ecdsa &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o sig.tss &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p &lt;span class="m"&gt;123456&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; challenge.bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tpm2_verifysignature &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c 0x81020000 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -g sha256 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -s sig.tss &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -m challenge.bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 返回0，通过&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 class="relative group"&gt;整活
&lt;div id="整活" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%95%b4%e6%b4%bb" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;接下来把这个接入 pam。&lt;/p&gt;
&lt;p&gt;导出公钥：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tpm2_readpublic -c 0x81020000 -f pem -o /etc/tpm-ecc.pub.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;644&lt;/span&gt; /etc/tpm-ecc.pub.pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用我这个&lt;a
href="https://github.com/minortex/tpm_auth"
target="_blank"
&gt;项目&lt;/a&gt;试试，仅供娱乐：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#%PAM-1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth required pam_faillock.so preauth
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore&lt;span class="o"&gt;]&lt;/span&gt; pam_tpm_ecc.so &lt;span class="nv"&gt;key_handle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x81020000 &lt;span class="nv"&gt;pubkey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/tpm-ecc.pub.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-auth &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore&lt;span class="o"&gt;]&lt;/span&gt; pam_systemd_home.so
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bad&lt;span class="o"&gt;]&lt;/span&gt; pam_unix.so try_first_pass nullok
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;die&lt;span class="o"&gt;]&lt;/span&gt; pam_faillock.so authfail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth optional pam_permit.so
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth required pam_env.so
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auth required pam_faillock.so authsucc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;polkit
&lt;div id="polkit" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#polkit" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这玩意，搞那么复杂的权限管理，还用模板，没折腾死我。&lt;/p&gt;
&lt;p&gt;他每次授权的时候启动一个 helper，这个 helper 有 root 权限，但是启动的时候被 systemd 限制了一大堆特权，我们得用 drop-in 给他加回来：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl edit polkit-agent-helper@
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Editing /etc/systemd/system/polkit-agent-helper@.service.d/override.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Service] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;DeviceAllow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/dev/tpmrm0 rw&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;BindPaths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/dev/tpmrm0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 class="relative group"&gt;彩蛋
&lt;div id="彩蛋" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bd%a9%e8%9b%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;一发入魄&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ sudo tpm2_dictionarylockout -s -n &lt;span class="m"&gt;10&lt;/span&gt; -t &lt;span class="m"&gt;600&lt;/span&gt; -l &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish&lt;span class="o"&gt;()&lt;/span&gt; Received TPM Error
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters&lt;span class="o"&gt;()&lt;/span&gt; Esys Finish ErrorCode &lt;span class="o"&gt;(&lt;/span&gt;0x0000098e&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Esys_DictionaryAttackParameters&lt;span class="o"&gt;(&lt;/span&gt;0x98E&lt;span class="o"&gt;)&lt;/span&gt; - tpm:session&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;:the authorization HMAC check failed and DA counter incremented
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Failed DictionaryLockout Setup
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Unable to run tpm2_dictionarylockout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ sudo tpm2_dictionarylockout -s -n &lt;span class="m"&gt;10&lt;/span&gt; -t &lt;span class="m"&gt;600&lt;/span&gt; -l &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish&lt;span class="o"&gt;()&lt;/span&gt; Received TPM Error
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters&lt;span class="o"&gt;()&lt;/span&gt; Esys Finish ErrorCode &lt;span class="o"&gt;(&lt;/span&gt;0x00000921&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Esys_DictionaryAttackParameters&lt;span class="o"&gt;(&lt;/span&gt;0x921&lt;span class="o"&gt;)&lt;/span&gt; - tpm:warn&lt;span class="o"&gt;(&lt;/span&gt;2.0&lt;span class="o"&gt;)&lt;/span&gt;: authorizations &lt;span class="k"&gt;for&lt;/span&gt; objects subject to DA protection are not allowed at this &lt;span class="nb"&gt;time&lt;/span&gt; because the TPM is in DA lockout mode
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Failed DictionaryLockout Setup
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: Unable to run tpm2_dictionarylockout
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>UPS 折腾记</title><link>https://minortex.github.io/posts/ups/</link><pubDate>Wed, 22 Apr 2026 23:00:09 +0800</pubDate><guid>https://minortex.github.io/posts/ups/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;最近我突然有了两台 ups 的管理权，一台工作室的 APC Smart-UPS SPRM1K，另一台家里的 APC BACK-UPS BK650。由于手边只有前者，先更新前者的研究成果，后者择日更新。&lt;/p&gt;
&lt;p&gt;管理带通信的 UPS 的配套软件其实就两种，这里主要讲解 NUT，而 apcupsd 配置很简单，而且也可以作为 NUT 的驱动，这里不再详述。&lt;/p&gt;
&lt;h2 class="relative group"&gt;NUT 的配置
&lt;div id="nut-的配置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nut-%e7%9a%84%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;要理解 NUT，首先要明白几个概念：driver、monitor(client)、server（可选），三个服务都要单独启用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;驱动层
&lt;div id="驱动层" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%a9%b1%e5%8a%a8%e5%b1%82" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;driver 其实就是对接各种奇奇怪怪 ups 的，这里我这两台一个用的是串口（apcsmart）,另一个是 usbhid-ups，这里主要是以前者讲解的。&lt;/p&gt;
&lt;p&gt;配置 ups 驱动：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## /etc/nut/ups.conf &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 根据需要填写,可用nut-scanner -U获取驱动&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;ups&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; apcsmart
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; /dev/ups
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;cable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 940-0024
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ignorelb &lt;span class="c1"&gt;# 手动指定阈值&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; override.battery.charge.low &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; override.battery.runtime.low &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动服务：&lt;code&gt;upsdrvctl start&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里有个小插曲，串口可能提示权限不够：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@pve:/etc/nut# upsdrvctl start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Network UPS Tools - UPS driver controller 2.8.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Network UPS Tools - Generic HID driver 0.52 &lt;span class="o"&gt;(&lt;/span&gt;2.8.1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;USB communication driver &lt;span class="o"&gt;(&lt;/span&gt;libusb 1.0&lt;span class="o"&gt;)&lt;/span&gt; 0.46
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;libusb1: Could not open any HID devices: insufficient permissions on everything
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No matching HID UPS found
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upsnotify: notify about state &lt;span class="m"&gt;4&lt;/span&gt; with libsystemd: was requested, but not running as a service unit now, will not spam more about it
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Driver failed to start &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用 udev 规则，给 nut 权限：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat &lt;span class="s"&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39; &amp;gt; /etc/udev/rules.d/99-nut-ups.rules
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;SUBSYSTEM==&amp;#34;usb&amp;#34;, ATTR{idVendor}==&amp;#34;051d&amp;#34;, ATTR{idProduct}==&amp;#34;0002&amp;#34;, MODE=&amp;#34;0660&amp;#34;, GROUP=&amp;#34;nut&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;udevadm control --reload-rules
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;udevadm trigger
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再次启动：&lt;code&gt;upsdrvctl start&lt;/code&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;服务层
&lt;div id="服务层" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9c%8d%e5%8a%a1%e5%b1%82" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;nut 其实 c/s 架构很分明，如果只有一台机子，也得启动一个服务器。但是这也为拓展型带来了便利。&lt;/p&gt;
&lt;p&gt;如果你只有一台机子，用 standalone 即可，作为“吹哨人”，用 netserver，接收命令关闭自己的机器，用 netclient。&lt;/p&gt;
&lt;p&gt;我这里改成服务器：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## /etc/nut/nut.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 服务器模式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;netserver
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## /etc/nut/upsd.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 加上这一行，给局域网里面的设备提供服务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 如果你是pve而且路由器不接ups，建议自己建一个&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 自定义交换机&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;LISTEN 0.0.0.0 &lt;span class="m"&gt;3493&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置用户，monuser 最好有，因为群晖用这个而且不能更改。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## /etc/nut/upsd.users&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;monuser&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; secret
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; upsmon slave
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;admin&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;your_password&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; upsmon master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; SET
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;instcmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ALL
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动服务：&lt;code&gt;systemctl enable --now nut-server&lt;/code&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;客户端/监视层
&lt;div id="客户端监视层" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e7%9b%91%e8%a7%86%e5%b1%82" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;配置 nut-monitor 以便自动关机：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MONITOR ups@localhost &lt;span class="m"&gt;1&lt;/span&gt; monuser secret master &lt;span class="c1"&gt;# slave也可以，后文（关机过程）讲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可以看到，最下面有个&lt;code&gt;shutdown&lt;/code&gt;命令，这个只要 monitor 服务启动了，在遇到低电量阈值的时候，就一定会执行的。&lt;/p&gt;
&lt;p&gt;启动服务：&lt;code&gt;systemctl enable --now nut-monitor&lt;/code&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;关机过程
&lt;div id="关机过程" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;断开电源，nut 检测到电池供电，其他客户端收到信号。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对于群晖这种设置了 upssched 的机子，他可以在检测到断电之后的一定时间开启安全模式（umount 所有分区，只保留必须的 nut 监听服务和关机逻辑），如果在此期间市电恢复，重新检测到后会进行自动重启。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;电池到达所给定的阈值（电量/剩余事件/电池供电时间），发出警告信号。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这时候服务器自己会开始关机（执行 shutdown），其他客户端会收到信号，开始进行关机。&lt;/p&gt;
&lt;p&gt;在关机最后一刻，有一个有意思的脚本想和大家分享。&lt;/p&gt;
&lt;p&gt;这个脚本是 systemd 在关机的最后时刻执行的，位于&lt;code&gt;/usr/lib/systemd/system-shutdown/nutshutdown&lt;/code&gt;。我们来看看他的结构：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This script requires both nut-server (drivers)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and nut-client (upsmon) to be present locally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and on mounted filesystems&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -x &lt;span class="s2"&gt;&amp;#34;/sbin/upsmon&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -x &lt;span class="s2"&gt;&amp;#34;/sbin/upsdrvctl&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; /sbin/upsmon -K &amp;gt;/dev/null 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# The argument may be anything compatible with sleep&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# (not necessarily a non-negative integer)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;wait_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;`/bin/sed -ne &amp;#39;s#^ *POWEROFF_WAIT= *\(.*\)&lt;/span&gt;&lt;span class="nv"&gt;$#&lt;/span&gt;&lt;span class="s2"&gt;\1#p&amp;#39; /etc/nut/nut.conf`&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;wait_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /sbin/upsdrvctl shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$wait_delay&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /bin/sleep &lt;span class="nv"&gt;$wait_delay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# We need to pass --force twice here to bypass systemd and execute the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# reboot directly ourself.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /bin/systemctl reboot --force --force
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以注意到经过一系列的判断，如果是真的断电（发出警告后）&lt;code&gt;upsmon -K&lt;/code&gt;会返回 1，那么就会使用&lt;code&gt;upsdrvctl shutdown&lt;/code&gt;来命令 ups 在一定时间后断电。&lt;/p&gt;
&lt;p&gt;但是这时候，系统已经解除了所有的磁盘 rw 的挂载，执行完脚本的下一步其实就是给 acpi 电源发送 S5 信号并断电，速度很快，其实断不断电也无所谓了。&lt;/p&gt;
&lt;p&gt;一般的断电延时 20s 绰绰有余。至于断电时间的设置，高级的 ups 可以设置（见后文&lt;code&gt;upsrw&lt;/code&gt;），普通的不可以。&lt;/p&gt;
&lt;p&gt;所以如果这是最后一个“吹哨人”执行的 killpower（让 ups 等会断电），最好关闭的时候要尽量比 slave 关得晚，否则就得尽量延长 ups 的延迟时间。&lt;/p&gt;
&lt;h2 class="relative group"&gt;常用命令
&lt;div id="常用命令" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b8%b8%e7%94%a8%e5%91%bd%e4%bb%a4" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;upscmd：执行一些驱动预设的指令
&lt;div id="upscmd执行一些驱动预设的指令" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#upscmd%e6%89%a7%e8%a1%8c%e4%b8%80%e4%ba%9b%e9%a9%b1%e5%8a%a8%e9%a2%84%e8%ae%be%e7%9a%84%e6%8c%87%e4%bb%a4" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@SAST-Docker:~# upscmd -l ups
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Instant commands supported on UPS &lt;span class="o"&gt;[&lt;/span&gt;ups&lt;span class="o"&gt;]&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bypass.start - Put the UPS in bypass mode
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bypass.stop - Take the UPS out of bypass mode
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;load.off - Turn off the load immediately
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;load.on - Turn on the load immediately
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shutdown.return - Turn off the load and &lt;span class="k"&gt;return&lt;/span&gt; when power is back
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shutdown.stayoff - Turn off the load and remain off
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test.battery.start - Start a battery &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test.battery.stop - Stop the battery &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test.failure.start - Start a simulated power failure
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test.panel.start - Start testing the UPS pane
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;upsrw：用于读写 eeprom 的
&lt;div id="upsrw用于读写-eeprom-的" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#upsrw%e7%94%a8%e4%ba%8e%e8%af%bb%e5%86%99-eeprom-%e7%9a%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;使用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upsrw -l &amp;lt;upsname&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;控制响声、复电延迟启动、复电要求电池水平、关电延迟停止 ups。&lt;/p&gt;
&lt;p&gt;比如我们工作室的 SPRM1K 可以设置的东西就很多，还贴心的用了 enum 告诉你：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@SAST-Docker:/etc/nut# upsrw -l ups
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;battery.alarm.threshold&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Battery alarm threshold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;T&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;L&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;N&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;battery.charge.restart&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Minimum battery level &lt;span class="k"&gt;for&lt;/span&gt; restart after power off &lt;span class="o"&gt;(&lt;/span&gt;percent&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;00&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;15&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;50&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;90&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;battery.date&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Battery change date
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: STRING
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Maximum length: &lt;span class="m"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Value: 12/30/25
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;input.transfer.high&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;High voltage transfer point &lt;span class="o"&gt;(&lt;/span&gt;V&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;231&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;242&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;253&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;264&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;input.transfer.low&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Low voltage transfer point &lt;span class="o"&gt;(&lt;/span&gt;V&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;187&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;176&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;165&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;154&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;output.voltage.nominal&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nominal output voltage &lt;span class="o"&gt;(&lt;/span&gt;V&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;220&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;230&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;240&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;ups.delay.shutdown&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Interval to &lt;span class="nb"&gt;wait&lt;/span&gt; after shutdown with delay &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;020&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;180&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;300&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;600&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;ups.delay.start&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Interval to &lt;span class="nb"&gt;wait&lt;/span&gt; before &lt;span class="o"&gt;(&lt;/span&gt;re&lt;span class="o"&gt;)&lt;/span&gt;starting the load &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;060&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;180&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;300&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;ups.id&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UPS system identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: STRING
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Maximum length: &lt;span class="m"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Value: UPS_IDEN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;ups.test.interval&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Interval between self tests &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Type: ENUM NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;1209600&amp;#34;&lt;/span&gt; SELECTED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;604800&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Option: &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我在这里改了&lt;code&gt;ups.delay.shutdown&lt;/code&gt;，原因很尴尬，esxi 只支持客户端模式的 nut，服务端只能用一台虚拟机来实现。那么希望在服务端发出 killpower 后，还能等待 esxi 关闭，这个时间就不可避免的延长。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;我还改了&lt;code&gt;ups.delay.start&lt;/code&gt;，这个参数很有意思，他没有注释，根据字面意思理解，应该是断电之后延迟启动吧？&lt;/p&gt;
&lt;p&gt;但是不是这个意思。他指的是如果你发出了 killpower 的命令后中途来电，他断电后，应该在多少秒后开机？&lt;/p&gt;
&lt;p&gt;我觉得这句话得细品。&lt;/p&gt;
&lt;p&gt;对于我这台 SPRM1K 来说，发送 killpower 指令后，首先是等待&lt;code&gt;ups.delay.shutdown&lt;/code&gt;的时间，然后切断输出，然后过了硬编码的 1 分钟，关闭 ups 自己。&lt;/p&gt;
&lt;p&gt;那么就有几种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在等待&lt;code&gt;ups.delay.shutdown&lt;/code&gt;时，市电恢复。&lt;/li&gt;
&lt;li&gt;在等待硬编码的 1 分钟的时候，市电恢复。&lt;/li&gt;
&lt;li&gt;ups 关机之后，市电恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三种情况中前两者会等待&lt;code&gt;ups.delay.start&lt;/code&gt;的时间，最后一种不会等待，初始化之后直接开机。&lt;/p&gt;
&lt;p&gt;我想应该是&lt;a
href="https://www.chiphell.com/forum.php?mod=viewthread&amp;amp;tid=2605539&amp;amp;extra=page%3D1&amp;amp;mobile=no"
target="_blank"
&gt;这篇文章&lt;/a&gt;里面提到的问题，作者说如果断电的时间太短，那么 After AC Loss 就算设置为 Power On，也不一定能自动开机。所以才有这么一个选项。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;btw: 我并不太赞同链接里面的方案，复杂度太高了很难维护，其实里面很多东西厂家已经做好了。（排除 UPS 不够高端的问题，这个等我回去测试一下我的 BK650 再下定论）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 class="relative group"&gt;奇怪的 bug
&lt;div id="奇怪的-bug" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a5%87%e6%80%aa%e7%9a%84-bug" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;根据 &lt;code&gt;man 8 usbhid-ups&lt;/code&gt; 中的说明，可以直接在 ups.conf 里面配置 &lt;code&gt;allow_killpower&lt;/code&gt; 来在启动的时候生效，但是&lt;a
href="https://github.com/networkupstools/nut/issues/2605"
target="_blank"
&gt;这个 issue&lt;/a&gt; 说 &lt;code&gt;allow_killpower&lt;/code&gt; 在 2.8.3 之后才修复启动的时候硬编码的 0 会覆盖回去的问题，然而 trixie 是 2.8.1，就很难受。&lt;/p&gt;
&lt;p&gt;能用点 hack 的办法，启动的时候修改。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl edit nut-driver@ups.service
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 写入&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;Service&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 延迟几秒确保驱动已经完全初始化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ExecStartPost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/sh -c &lt;span class="s2"&gt;&amp;#34;sleep 5 &amp;amp;&amp;amp; /usr/bin/upsrw -s driver.flag.allow_killpower=1 -u admin -p &amp;lt;你的密码&amp;gt; ups&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是这台设备不再身边，所以我还是选择了保守的 apcupsd 方案关机+nut 桥接给群晖，这篇文章之后还会更新的。&lt;/p&gt;</description></item><item><title>Immortalwrt：多线多网与 Tailscale</title><link>https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/</link><pubDate>Wed, 07 Jan 2026 01:42:21 +0800</pubDate><guid>https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/</guid><description>
&lt;h2 class="relative group"&gt;配置多线多内网
&lt;div id="配置多线多内网" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%85%8d%e7%bd%ae%e5%a4%9a%e7%ba%bf%e5%a4%9a%e5%86%85%e7%bd%91" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;现在我同时有了两个账号的使用权，一个是运营商账号，有 IPv6，但是速度不算快，不过不断网；另一个是运营商自建的网，卖给学生用的，没有 IPv6，晚上 11 点半还断网，真是花钱做大冤种啊哈哈！&lt;/p&gt;
&lt;p&gt;那么就有了配多内网的想法，之前也划过 VLAN 了，这里就不赘述了，提一嘴怎么给电脑网卡变成 trunk 模式，Win 就是在设备管理器，网卡的高级设置里面填入 ID，Linux 先 modprobe 8021q，用 &lt;code&gt;nmcli&lt;/code&gt; 可以添加子接口，具体看一下 manual。&lt;/p&gt;
&lt;p&gt;在接口 - 设备这里，可以创建 MAC VLAN:&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="macvlan"
width="1309"
height="878"
src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image_hu_a8fb5bf96636b438.webp"
srcset="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image_hu_a8fb5bf96636b438.webp 800w, https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image_hu_21ef3c0d1b31a4c8.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;需要注意的是，在这台京东云上面要求多个 MACVLAN 必须是连续的，否则可能出现设备 CLAIM 失败的报错。&lt;/p&gt;
&lt;p&gt;然后使用 &lt;code&gt;curl --interface &lt;/code&gt;分别给每个接口配置 portal 认证。现在应该有两个接口了：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="two_interface"
width="1425"
height="377"
src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_38d63cae2c2a96c7.webp"
srcset="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_38d63cae2c2a96c7.webp 800w, https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_e18c60d1d6de1739.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;这时候会自动把两个 wan 口下发的路由都加到 main 表里面，是什么顺序我没注意，不过，你的设备肯定只走一条出去，另一条是闲置的。&lt;/p&gt;
&lt;p&gt;所以我们要在高级设置里面覆盖 IPv4 路由表，这个的意思是把获取到的路由加到这个表里面。&lt;/p&gt;
&lt;p&gt;表分两种，有名字的和纯数字 ID 的。你这里只能填 ID，填完了可以到 &lt;code&gt;/etc/iproute2/rt_tables&lt;/code&gt; 里面按照 &lt;code&gt;id 名字&lt;/code&gt; 这样写，这时候就更加有可读性。&lt;/p&gt;
&lt;p&gt;/etc/iproute2/rt_tables 必须是连续的，否则可能出现设备 CLAIM 失败的报错。&lt;/p&gt;
&lt;p&gt;然后使用 &lt;code&gt;curl --interface &lt;/code&gt;分别给每个接口配置 portal 认证。现在应该有两个接口了：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="two_interface"
width="1425"
height="377"
src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_38d63cae2c2a96c7.webp"
srcset="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_38d63cae2c2a96c7.webp 800w, https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1_hu_e18c60d1d6de1739.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-1.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;这时候会自动把两个 wan 口下发的路由都加到 main 表里面，是什么顺序我没注意，不过，你的设备肯定只走一条出去，另一条是闲置的。&lt;/p&gt;
&lt;p&gt;所以我们要在高级设置里面覆盖 IPv4 路由表，这个的意思是把获取到的路由加到这个表里面。&lt;/p&gt;
&lt;p&gt;表分两种，有名字的和纯数字 ID 的。你这里只能填 ID，填完了可以到 &lt;code&gt;/etc/iproute2/rt_tables&lt;/code&gt; 里面按照 &lt;code&gt;id 名字&lt;/code&gt; 这样写，这时候就更加有可读性。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="override_ipv4_table"
width="634"
height="392"
src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-2.webp"
srcset="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-2.webp 800w, https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-2.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-2.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;那么我就把两个路由分到了两个路由表里面，一个 cmcc，另一个 cernet。&lt;/p&gt;
&lt;p&gt;这时候问题来了，你多半发现上不了网了，原因是默认只会查主路由表（main），这时候主路由表里面只有你的内网 local 路由，发往别的网段的数据包就被丢弃了。&lt;/p&gt;
&lt;p&gt;所以我们得手动定制路由规则，其实对应的就是 &lt;code&gt;ip rule&lt;/code&gt; 命令了。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="routing_rules"
width="1416"
height="283"
src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-3_hu_3076800415bc1a74.webp"
srcset="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-3_hu_3076800415bc1a74.webp 800w, https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-3_hu_2941a70e83e8ac2a.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-2-multiwan-tailscale/image-3.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;按照不同的网段查不同的表，请注意上面两条规则很重要，要不然内网的流量也被发到 wan 口去了，wrt 系不像 routeros，没有靠 MAC 地址连接的办法，自动回滚居然也不生效，没备份的我至少重新弄了三遍。&lt;/p&gt;
&lt;p&gt;然后就可以爽用了。&lt;/p&gt;
&lt;h2 class="relative group"&gt;tailscale 出现的问题
&lt;div id="tailscale-出现的问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tailscale-%e5%87%ba%e7%8e%b0%e7%9a%84%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;配完过了几天发现外部的 tailscale 没法连到内网的设备了，tcpdump 一看，只有访问的包，没有回去的包。我当时以为是 masquerade 的问题，在 ts 接口上面开了动态伪装，然后 dump br-lan.2，发现其实内网有回复，但是发到路由器后就没下文了。&lt;/p&gt;
&lt;p&gt;我立马就知道肯定是路由规则问题了，防火墙没问题。&lt;/p&gt;
&lt;p&gt;于是添加了一条最优先的规则，让目标为 `100.64.0.0/10&amp;rsquo; 的走主路由表，还是不通。&lt;/p&gt;
&lt;p&gt;其实还是先入为主了！ts 自己弄了个 52 的路由表，他的规则全部在里面，根本不在主路由表。高级设置里面表填上 52，收工！&lt;/p&gt;</description></item><item><title>Dbus 探索</title><link>https://minortex.github.io/posts/dbus_discovering/</link><pubDate>Fri, 21 Nov 2025 17:56:21 +0800</pubDate><guid>https://minortex.github.io/posts/dbus_discovering/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;之前想使用 &lt;code&gt;syfs&lt;/code&gt; 监听电量变化失败，原因是 &lt;code&gt;sysfs&lt;/code&gt; 不支持 &lt;code&gt;epoll&lt;/code&gt; 之类的高级特性，所以还得隔几秒轮询，这对于精确度要求高的我来说是没法接受的，所以我不得不望向一个很熟悉但是陌生的东西————&lt;code&gt;Dbus&lt;/code&gt;。&lt;/p&gt;
&lt;h2 class="relative group"&gt;概念
&lt;div id="概念" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a6%82%e5%bf%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;感觉 &lt;code&gt;Dbus&lt;/code&gt; 是有点点像 &lt;code&gt;MQTT&lt;/code&gt;，你看 &lt;code&gt;broker&lt;/code&gt; 都出来了，都有订阅这一说法，但是又有点不同。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;总线&lt;/strong&gt;: 包括系统总线和用户总线。前者可以报告系统状态，硬件上的状态（正是我们需要的）；后者则在 &lt;code&gt;KDE&lt;/code&gt; 的通知和特效之类上面大量使用，比如录屏的时候静音之类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;总线名称&lt;/strong&gt;: 一个跟 &lt;code&gt;Dbus&lt;/code&gt; 有关的服务启动了，这个服务下面提供的各种对象，全都是挂在这个总线名称下的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象路径&lt;/strong&gt;: 服务提供的某些功能/实例，有点像 &lt;code&gt;unix&lt;/code&gt; 的路径但是完全不一样，组织成面向对象的层次结构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口名称&lt;/strong&gt;: 一个服务可以有多个接口，在不同的接口名称定义对象提供的方法、属性和信号。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方法&lt;/strong&gt;: 客户端向服务端发起的同步请求，要求服务执行请求/获取状态然后返回请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;: 客户端向服务端可以获取到的状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信号&lt;/strong&gt;: 服务向总线上面发送通知，告知感兴趣的客户端某个事件发生了。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;订阅&lt;/strong&gt;: 客户端告诉总线对服务上面的某个对象的某个信号感兴趣。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; 和 &lt;code&gt;Dbus&lt;/code&gt; 集成挺好的，可以监听某个总线上面的连接，如果客户端发出了请求但是服务没有启动，那么就会自动启动服务。这就像普通的服务上面的 &lt;code&gt;socket&lt;/code&gt; 按需启动一样，也是把这套思路应用到了 &lt;code&gt;Dbus&lt;/code&gt; 上面。&lt;/p&gt;
&lt;p&gt;一般来说约定名字和对象路径中间的 &lt;code&gt;.&lt;/code&gt; 和 &lt;code&gt;/&lt;/code&gt; 对应，但是当然可以不遵守。&lt;/p&gt;
&lt;p&gt;客户端可以手动向服务查询，对于用户，可以手动通过命令行获取信息；也可以由服务通过信号推送得到信息，这样就避免轮询造成的性能开销。&lt;/p&gt;
&lt;h2 class="relative group"&gt;命令行使用
&lt;div id="命令行使用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%91%bd%e4%bb%a4%e8%a1%8c%e4%bd%bf%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; 的 &lt;code&gt;busctl&lt;/code&gt; 比较好理解，以这个工具为例。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;busctl&lt;/code&gt; 无非就那几个参数，总线名称，对象路径，接口名称，然后按需调用/获取对应的对象。&lt;/p&gt;
&lt;h3 class="relative group"&gt;自省
&lt;div id="自省" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%87%aa%e7%9c%81" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;busctl introspect &amp;lt;busName&amp;gt; &amp;lt;path&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这个路径下的对象允许被自省的话，比如说 &lt;code&gt;UPower&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ busctl introspect org.freedesktop.UPower /org/freedesktop/UPower
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME TYPE SIGNATURE RESULT/VALUE FLAGS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;org.freedesktop.DBus.Introspectable interface - - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.Introspect method - s -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;org.freedesktop.DBus.Peer interface - - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.GetMachineId method - s -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.Ping method - - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;org.freedesktop.DBus.Properties interface - - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.Get method ss v -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.GetAll method s a&lt;span class="o"&gt;{&lt;/span&gt;sv&lt;span class="o"&gt;}&lt;/span&gt; -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.Set method ssv - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.PropertiesChanged signal sa&lt;span class="o"&gt;{&lt;/span&gt;sv&lt;span class="o"&gt;}&lt;/span&gt;as - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;org.freedesktop.UPower interface - - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.EnumerateDevices method - ao -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.GetCriticalAction method - s -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.GetDisplayDevice method - o -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.DaemonVersion property s &lt;span class="s2"&gt;&amp;#34;1.90.10&amp;#34;&lt;/span&gt; emits-change
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.LidIsClosed property b &lt;span class="nb"&gt;false&lt;/span&gt; emits-change
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.LidIsPresent property b &lt;span class="nb"&gt;true&lt;/span&gt; emits-change
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.OnBattery property b &lt;span class="nb"&gt;false&lt;/span&gt; emits-change
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.DeviceAdded signal o - -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.DeviceRemoved signal o - -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;调用方法
&lt;div id="调用方法" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%b0%83%e7%94%a8%e6%96%b9%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;那么显而易见了，直接调用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;busctl call &amp;lt;busName&amp;gt; &amp;lt;path&amp;gt; &amp;lt;interface&amp;gt; &amp;lt;object&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;param&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;得是 &lt;code&gt;method&lt;/code&gt; 才能被调用，有的方法需要参数才能调用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;属性
&lt;div id="属性" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b1%9e%e6%80%a7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;属性既可以获取，在有权限的情况下可以设置。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;busctl get-property &amp;lt;busName&amp;gt; &amp;lt;path&amp;gt; &amp;lt;interface&amp;gt; &amp;lt;object&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;busctl set-property &amp;lt;busName&amp;gt; &amp;lt;path&amp;gt; &amp;lt;interface&amp;gt; &amp;lt;object&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;签名
&lt;div id="签名" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ad%be%e5%90%8d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;获取对象或者调用方法传参的时候，要求签名匹配。这里其实是一些数据类型，根据对应的英文简称：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt;: 字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt;: 布尔&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i&lt;/code&gt;: 整形&lt;/li&gt;
&lt;li&gt;&lt;code&gt;u&lt;/code&gt;: 无符号整形&lt;/li&gt;
&lt;li&gt;&lt;code&gt;d&lt;/code&gt;: 双精度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(a)o&lt;/code&gt;: （数组式的）对象路径，用于查看其他对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;传入参数是这样的，先输入类型，然后跟上你的数据，比如：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;s &amp;quot;hello&amp;quot; i 32&lt;/code&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;简单的电量检测小工具
&lt;div id="简单的电量检测小工具" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ae%80%e5%8d%95%e7%9a%84%e7%94%b5%e9%87%8f%e6%a3%80%e6%b5%8b%e5%b0%8f%e5%b7%a5%e5%85%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;让 Gemini 用 QT 写了一个简单的电量监测系统，仅用于我这一台 chromebook：&lt;/p&gt;
&lt;p&gt;&lt;a
href="https://github.com/minortex/BatteryService"
target="_blank"
&gt;BatteryService&lt;/a&gt;&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：I/O 流与字符串流</title><link>https://minortex.github.io/posts/modern-cpp-10-io-streams/</link><pubDate>Thu, 13 Nov 2025 13:58:39 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-10-io-streams/</guid><description>
&lt;h2 class="relative group"&gt;流
&lt;div id="流" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%b5%81" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;std::cin &amp;gt;&amp;gt;&lt;/code&gt;
&lt;div id="stdcin-" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdcin-" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;跳过输入流中前导空白符，当遇到下一个空白的时候停止，并把空白&lt;strong&gt;保留&lt;/strong&gt;在输入缓冲区中。&lt;/p&gt;
&lt;p&gt;简单来说，就是获取一个单词。&lt;/p&gt;
&lt;h3 class="relative group"&gt;C-style 的函数
&lt;div id="c-style-的函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#c-style-%e7%9a%84%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;有一些函数是 C 遗留下来的，所以他们不支持 &lt;code&gt;std::string&lt;/code&gt;，只能使用传统的 C 风格字符串。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;std::getline()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;给定一个字符串缓冲的容器：&lt;/p&gt;
&lt;p&gt;获取缓冲区的内容，直到遇到 &lt;code&gt;\n&lt;/code&gt;，把 &lt;code&gt;\n&lt;/code&gt; 以及前面的内容提取，然后&lt;strong&gt;不将&lt;/strong&gt; &lt;code&gt;\n&lt;/code&gt; 放在目标中。&lt;/p&gt;
&lt;p&gt;重载函数 &lt;code&gt;(strBuf, n)&lt;/code&gt; 可以提取指定的长度的字符，获取 &lt;code&gt;n -1&lt;/code&gt; 个字符，因为要有 &lt;code&gt;\0&lt;/code&gt; 结尾。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gcount()&lt;/code&gt; 用于统计提取的部分，包括 &lt;code&gt;\n&lt;/code&gt;，但是不包括限定长度在后面添加的 &lt;code&gt;\0&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：继承、虚函数与异常处理</title><link>https://minortex.github.io/posts/modern-cpp-9-inheritance-virtual-exceptions/</link><pubDate>Thu, 06 Nov 2025 10:23:35 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-9-inheritance-virtual-exceptions/</guid><description>
&lt;h2 class="relative group"&gt;继承，派生
&lt;div id="继承派生" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%a7%e6%89%bf%e6%b4%be%e7%94%9f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;继承初始化
&lt;div id="继承初始化" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%a7%e6%89%bf%e5%88%9d%e5%a7%8b%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在构造函数后面的 &lt;code&gt;:&lt;/code&gt; 加入基类的列表初始化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_baseVar&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;baseVar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_baseVar&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;baseVar&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derive&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_deriveVar&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;baseVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;deriveVar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;baseVar&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;m_deriveVar&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deriveVar&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;修改派生类的访问权限
&lt;div id="修改派生类的访问权限" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%bf%ae%e6%94%b9%e6%b4%be%e7%94%9f%e7%b1%bb%e7%9a%84%e8%ae%bf%e9%97%ae%e6%9d%83%e9%99%90" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;访问修饰符
&lt;div id="访问修饰符" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%ae%bf%e9%97%ae%e4%bf%ae%e9%a5%b0%e7%ac%a6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;定义派生类的时候，&lt;code&gt;:&lt;/code&gt; 后的访问限定符：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public&lt;/code&gt;: 基类所有访问类型，即为派生类的访问类型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protected&lt;/code&gt;: 基类的 &lt;code&gt;public&lt;/code&gt; 在派生类中是 &lt;code&gt;protected&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;private&lt;/code&gt;: 基类的 &lt;code&gt;public&lt;/code&gt; 和 &lt;code&gt;protected&lt;/code&gt; 在派生类中是 &lt;code&gt;private&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基类的 &lt;code&gt;private&lt;/code&gt; 在派生类中默认永远无法访问。&lt;/p&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;using&lt;/code&gt;来强制改变单个成员
&lt;div id="using来强制改变单个成员" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using%e6%9d%a5%e5%bc%ba%e5%88%b6%e6%94%b9%e5%8f%98%e5%8d%95%e4%b8%aa%e6%88%90%e5%91%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;可以单独在派生类中修改基类中可访问的成员在派生类的访问权限。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derive&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 假设这两个在 Base 都是公开的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;m_var&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样就把两个成员在派生类中限制在了 &lt;code&gt;private&lt;/code&gt; 权限，重载函数会全部限制。&lt;/p&gt;
&lt;h3 class="relative group"&gt;隐藏功能
&lt;div id="隐藏功能" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%9a%90%e8%97%8f%e5%8a%9f%e8%83%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;如果基类和派生类存在同名的成员函数，那么派生类会隐藏基类的函数。&lt;/p&gt;
&lt;p&gt;如果向派生类对象传入一个 &lt;code&gt;int&lt;/code&gt;，基类里面的匹配 &lt;code&gt;int&lt;/code&gt;，派生类匹配 &lt;code&gt;double&lt;/code&gt;，还是会优先使用派生类的函数，然后通过数值转化把实参变成 &lt;code&gt;double&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;解决方案是在派生类强制使用 &lt;code&gt;using Base::func&lt;/code&gt; 来使用基类的函数。&lt;/p&gt;
&lt;h3 class="relative group"&gt;删除功能
&lt;div id="删除功能" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%a0%e9%99%a4%e5%8a%9f%e8%83%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;像复制构造函数一样，可以显示删除某些不需要的基类功能，用 &lt;code&gt;= delete&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是仍然可以绕过派生类访问基类的功能：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;m_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;m_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;虚函数
&lt;div id="虚函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%99%9a%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;虚函数是 &lt;code&gt;RTTI&lt;/code&gt;（运行时类型信息）的典型代表。&lt;/p&gt;
&lt;p&gt;在多个派生的类中，如果有签名一致的函数，前面加上 &lt;code&gt;virtual&lt;/code&gt; 关键字，如果有类型为低派生的引用或者指针指向高派生的类，那么使用该指针将会自动调用尽可能高派生的类的函数。&lt;/p&gt;
&lt;p&gt;如果一个派类在其继承链的中只要有一个类的函数是 &lt;code&gt;virtual&lt;/code&gt;，那么其所有相同签名的函数均视为虚函数。&lt;/p&gt;
&lt;h4 class="relative group"&gt;覆盖
&lt;div id="覆盖" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a6%86%e7%9b%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;使用 &lt;code&gt;override&lt;/code&gt; 关键字在派生类中显式的覆盖相同签名的函数，如果无法覆盖会报错，这样避免了签名不一致导致未覆盖还过编译的情况。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;override&lt;/code&gt; 放在成员函数的 &lt;code&gt;const&lt;/code&gt; 后。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;override&lt;/code&gt; 是隐式 &lt;code&gt;virtual&lt;/code&gt; 的，所以只有最底层的基类型需要 &lt;code&gt;virtual&lt;/code&gt; 关键字，其他都可以写 &lt;code&gt;override&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;final&lt;/code&gt; 放在 &lt;code&gt;override&lt;/code&gt; 后，提示这是最后一个覆盖的派生类函数，后续派生的类若再覆盖，则报错。&lt;/p&gt;
&lt;h4 class="relative group"&gt;协变返回类型
&lt;div id="协变返回类型" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8d%8f%e5%8f%98%e8%bf%94%e5%9b%9e%e7%b1%bb%e5%9e%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;当派生类中返回一个 &lt;code&gt;this&lt;/code&gt; 指针的时候，这个指针指向的类型只跟调用它的引用/指针类型有关。&lt;/p&gt;
&lt;h4 class="relative group"&gt;虚析构函数
&lt;div id="虚析构函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%99%9a%e6%9e%90%e6%9e%84%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;如果使用低层的引用/指针指向一个高层的派生类，释放的时候不会正确调用高层的析构函数。此时需要给析构函数添加 &lt;code&gt;virtual&lt;/code&gt; 修饰符。&lt;/p&gt;
&lt;p&gt;不推荐给所有的派生类添加 &lt;code&gt;virtual&lt;/code&gt;，因为虚函数表会造成开销。如果不希望此类被继承，应该在定义类的时候在类名后添加 &lt;code&gt;final&lt;/code&gt; 禁用对此类的继承。&lt;/p&gt;
&lt;h4 class="relative group"&gt;绑定和调度
&lt;div id="绑定和调度" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%91%e5%ae%9a%e5%92%8c%e8%b0%83%e5%ba%a6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;早期绑定/早期调度：在编译的时候就已经确定了调用函数的地址。&lt;/li&gt;
&lt;li&gt;晚期绑定：使用函数指针调用函数&lt;/li&gt;
&lt;li&gt;晚期调度：调用虚函数，通过虚表查询，只有在运行时才知道真正调用的函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;纯虚函数，纯虚类，虚基类
&lt;div id="纯虚函数纯虚类虚基类" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ba%af%e8%99%9a%e5%87%bd%e6%95%b0%e7%ba%af%e8%99%9a%e7%b1%bb%e8%99%9a%e5%9f%ba%e7%b1%bb" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;纯虚函数指的是基类声明了有这个函数，但是实际上无法实例化，只能交由派生类实例化的函数。&lt;/p&gt;
&lt;p&gt;在一个 &lt;code&gt;virtual&lt;/code&gt; 后面加入 &lt;code&gt;= 0&lt;/code&gt; 即可实现，也不需要函数体。&lt;/p&gt;
&lt;p&gt;纯虚类（抽象类）其实相当于一个接口函数，这个类没有成员变量，只有纯虚函数，需要注意必须要有虚析构函数。此类不能被构造，一般在调用的时候，形参为其的引用/指针，并接受其派生类为实参。&lt;/p&gt;
&lt;p&gt;虚基类在继承的 &lt;code&gt;:&lt;/code&gt; 后面加入 &lt;code&gt;virtual&lt;/code&gt;，目的是只有最后一个非虚的派生类创建对象，从而避免菱形继承造成的功能重复。&lt;/p&gt;
&lt;h3 class="relative group"&gt;切片
&lt;div id="切片" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%87%e7%89%87" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;当复制/移动的的目标类型是基类型时，会造成切片，只保留基类实例的部分。&lt;/p&gt;
&lt;h3 class="relative group"&gt;RTTI
&lt;div id="rtti" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#rtti" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;dynamic_cast&lt;/code&gt;
&lt;div id="dynamic_cast" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dynamic_cast" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;用于向下转换（把一个基类型的指针转换成一个派生类型的指针），造成很大的开销，转换后需要判断是否转换成功。&lt;/p&gt;
&lt;p&gt;当然用 &lt;code&gt;static_cast&lt;/code&gt; 也可以转换，但是不会判断是否是否可以成功转换，访问的时候会造成内存问题。所以不推荐。&lt;/p&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;typeid&lt;/code&gt;
&lt;div id="typeid" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#typeid" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;获取表达式和类型的类型信息，用于实现 C++ 中的自省。&lt;/p&gt;
&lt;p&gt;重载的运算符 &lt;code&gt;==&lt;/code&gt;，用于比较两个对象是否完全一致。&lt;/p&gt;
&lt;p&gt;成员函数 &lt;code&gt;.name()&lt;/code&gt;，可被打印的、修饰过的编译器名字。&lt;/p&gt;
&lt;h3 class="relative group"&gt;委派重载函数
&lt;div id="委派重载函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a7%94%e6%b4%be%e9%87%8d%e8%bd%bd%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; 不能够被设置成虚函数，因为不是成员函数。但是可以委派给虚函数来打印，只需要把流对象的引用传递过去即可。&lt;/p&gt;
&lt;h2 class="relative group"&gt;异常处理
&lt;div id="异常处理" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;经典的 &lt;code&gt;try-catch&lt;/code&gt; 块和 &lt;code&gt;throw&lt;/code&gt; 关键字。&lt;/p&gt;
&lt;p&gt;在主函数里面使用 &lt;code&gt;try&lt;/code&gt; 包裹功能调用函数的时候，函数也可以抛出异常，如果函数本身没有处理，会清理本块的资源，然后依次交由上一级调用栈处理，这个过程叫作栈展开。如果一直到主函数都没有处理，那么就会调用 &lt;code&gt;std::terminate()&lt;/code&gt; 终止程序，此时可能未完成清理。&lt;/p&gt;
&lt;p&gt;因此，建议使用 &lt;code&gt;catch(...)&lt;/code&gt; 在最后捕获所有异常，此时可以保证所有资源都被清理。&lt;/p&gt;
&lt;p&gt;析构函数是隐式 &lt;code&gt;noexcept&lt;/code&gt; 的，因为如果析构函数又抛出异常（二次异常），那么程序将会直接终止。&lt;/p&gt;
&lt;p&gt;异常类和重载不同，对于能同时接受派生类和基类的形参，会优先选择第一个 &lt;code&gt;catch&lt;/code&gt;，所以需要把最匹配的放在前面。&lt;/p&gt;
&lt;h3 class="relative group"&gt;异常对象的存储机制
&lt;div id="异常对象的存储机制" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%82%e5%b8%b8%e5%af%b9%e8%b1%a1%e7%9a%84%e5%ad%98%e5%82%a8%e6%9c%ba%e5%88%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;异常对象会在抛出的时候进行复制，所以这个对象的类必须有复制构造函数，才能正常进行异常流程。&lt;/p&gt;
&lt;h3 class="relative group"&gt;在 &lt;code&gt;catch&lt;/code&gt; 中抛出异常
&lt;div id="在-catch-中抛出异常" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9c%a8-catch-%e4%b8%ad%e6%8a%9b%e5%87%ba%e5%bc%82%e5%b8%b8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;可以在 &lt;code&gt;catch&lt;/code&gt; 中抛出异常，如果希望再次抛出相同的异常向上处理，应该直接使用 &lt;code&gt;throw;&lt;/code&gt; 而不是显式指定传入的名字，因为按值复制的过程会造成类型切片。&lt;/p&gt;
&lt;h3 class="relative group"&gt;函数级异常
&lt;div id="函数级异常" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%87%bd%e6%95%b0%e7%ba%a7%e5%bc%82%e5%b8%b8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;严格在函数的 &lt;code&gt;()&lt;/code&gt; 后写 &lt;code&gt;try-catch&lt;/code&gt; 块，此过程有隐式重抛异常，最好自己手动指定。&lt;/p&gt;
&lt;p&gt;一般用于构造函数中。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// note addition of try keyword here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="c1"&gt;// note this is at same level of indentation as the function itself
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Exceptions from member initializer list or
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// from constructor body are caught here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Exception caught&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// throw; // rethrow the existing exception
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;noexcept&lt;/code&gt;
&lt;div id="noexcept" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#noexcept" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;没有带 &lt;code&gt;noexcept&lt;/code&gt; 的函数都是潜在的抛出异常的函数。&lt;/p&gt;
&lt;p&gt;要求带有此标记的函数，必须在这个块前完成异常处理，如果超出了这个块，那么整个程序就被强行终止（使用 &lt;code&gt;std::terminate&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;noexcept&lt;/code&gt; 允许编译器进行深度的优化，因为不需要考虑异常的栈展开机制。&lt;/p&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;std::move_if_noexcept&lt;/code&gt;
&lt;div id="stdmove_if_noexcept" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdmove_if_noexcept" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在编译的时候确定移动构造函数是否有 &lt;code&gt;noexcept&lt;/code&gt; 关键字，如果没有，就 &lt;code&gt;fallback&lt;/code&gt; 到复制。&lt;/p&gt;
&lt;p&gt;其实此函数传入的形参是可变的，如果移动构造保证无异常抛出，那么就传入将亡值，使用移动构造函数；如果没有，那么就传入左值，使用复制构造函数。&lt;/p&gt;</description></item><item><title>Linux Swap：ZRAM、ZSWAP 与内存策略</title><link>https://minortex.github.io/posts/linux-swap-zram-zswap/</link><pubDate>Sat, 01 Nov 2025 21:57:31 +0800</pubDate><guid>https://minortex.github.io/posts/linux-swap-zram-zswap/</guid><description>
&lt;h2 class="relative group"&gt;引子
&lt;div id="引子" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%95%e5%ad%90" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;无论在哪个安装 Arch 的教程里面，你都能看到要求你设置一个 &lt;code&gt;swap&lt;/code&gt; 分区，但是，这个分区真的那么有必要吗？&lt;/p&gt;
&lt;p&gt;我最早听到 &lt;code&gt;swap&lt;/code&gt; 这个字，大概是安卓 2.3-4.0 时代吧。那时候的 CPU 性能不行，存储性能也很差，但是人们却执着于把 &lt;code&gt;swap&lt;/code&gt; 放在存储卡上面，总共 &lt;code&gt;128M&lt;/code&gt; 的内存，要在卡上放 &lt;code&gt;64M&lt;/code&gt; 进行交换。不过对于本来就慢的安卓手机，能够有更大的内存以便运行大型一点的游戏，就算慢一点，也比较值得。&lt;/p&gt;
&lt;p&gt;然而在现代系统上面，手机厂商又把这个“内存拓展”拿出来，许多人对此嗤之以鼻，然而这完全不是一回事。&lt;/p&gt;
&lt;h2 class="relative group"&gt;把内存变小，而不是放到低速存储上面
&lt;div id="把内存变小而不是放到低速存储上面" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8a%8a%e5%86%85%e5%ad%98%e5%8f%98%e5%b0%8f%e8%80%8c%e4%b8%8d%e6%98%af%e6%94%be%e5%88%b0%e4%bd%8e%e9%80%9f%e5%ad%98%e5%82%a8%e4%b8%8a%e9%9d%a2" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;首先，能够实现这一切的前提，是内存虚拟化。&lt;/p&gt;
&lt;p&gt;一个程序看到的大量的地址空间，其实并不是 1:1 的映射到物理内存上面的。通过系统调用 &lt;code&gt;mmap&lt;/code&gt;，操作系统记录了一个程序看到的内存是怎么对应到物理内存上去，但是，为什么一定是物理内存上面呢？&lt;/p&gt;
&lt;p&gt;于是有了 &lt;code&gt;swap&lt;/code&gt;，当我内存不够的时候，直接把程序运行时的内存（匿名页）驱逐到 &lt;code&gt;swap&lt;/code&gt; 上面，然后往映射表记录，当再次需要访问他的时候，触发一个“缺页中断”，就把 &lt;code&gt;swap&lt;/code&gt; 的内存重新搬回物理内存上面，这一切对于程序来说都是透明的。&lt;/p&gt;
&lt;p&gt;随着性能的发展，人们发现，这个把匿名页搬回物理内存的过程实在是太慢了，浪费了大量的 CPU 时间，那么为什么不让做点事情？于是，内存压缩出现了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;ZRAM
&lt;div id="zram" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#zram" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ZRAM&lt;/code&gt;，顾名思义，就是把内存压缩。&lt;/p&gt;
&lt;p&gt;不过，人们用的还是 &lt;code&gt;swap&lt;/code&gt; 的那套思路，把内存中的一部分划分出来，然后创建一个块设备，然后需要的时候，把内存压缩进去，这就是 &lt;code&gt;ZRAM&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们可以通过 &lt;code&gt;swappiness&lt;/code&gt; 参数，控制操作系统内核把内存压缩进入 &lt;code&gt;swap&lt;/code&gt; 的激进性，现在的值取 &lt;code&gt;0-200&lt;/code&gt; 了，值越高，越倾向于把内存放入交换分区。如果是传统的 &lt;code&gt;swap&lt;/code&gt;，那么我们希望不要那么激进，因为磁盘总归是慢的；但是对于一个压缩的内存块来说，应该把这个值取很高，因为对于 &lt;code&gt;zstd&lt;/code&gt; 和 &lt;code&gt;lz4&lt;/code&gt; 这样的算法来说，压缩/解压的速度非常快。&lt;/p&gt;
&lt;h4 class="relative group"&gt;存到极限
&lt;div id="存到极限" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ad%98%e5%88%b0%e6%9e%81%e9%99%90" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;如果内存的压力持续增大，&lt;code&gt;ZRAM&lt;/code&gt; 会有用完的时候。这又分为两种情况：&lt;/p&gt;
&lt;h4 class="relative group"&gt;1. &lt;code&gt;ZRAM&lt;/code&gt; 达到了设置的压缩前的数据的最大值。
&lt;div id="1-zram-达到了设置的压缩前的数据的最大值" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-zram-%e8%be%be%e5%88%b0%e4%ba%86%e8%ae%be%e7%bd%ae%e7%9a%84%e5%8e%8b%e7%bc%a9%e5%89%8d%e7%9a%84%e6%95%b0%e6%8d%ae%e7%9a%84%e6%9c%80%e5%a4%a7%e5%80%bc" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;这时候如果没有配置回写块的时候，系统就会去寻找一个优先级更低的 &lt;code&gt;swap&lt;/code&gt; 了。如果没有，那么就很可能触发 &lt;code&gt;OOM&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;配置了回写块的话，&lt;code&gt;ZRAM&lt;/code&gt; 会把其中最少使用的数据驱逐到回写块中。这个回写块跟 &lt;code&gt;swap&lt;/code&gt; 是不相容的，只能用于一种用途。&lt;/p&gt;
&lt;p&gt;如果这时候需要提取写入到回写块中的数据，那么就直接从磁盘解压到物理内存中，因为是压缩的数据，不仅减少了对磁盘的读写，还不让 CPU 闲着。&lt;/p&gt;
&lt;h4 class="relative group"&gt;2. &lt;code&gt;ZRAM&lt;/code&gt; 还没满，但是物理内存就要满了
&lt;div id="2-zram-还没满但是物理内存就要满了" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-zram-%e8%bf%98%e6%b2%a1%e6%bb%a1%e4%bd%86%e6%98%af%e7%89%a9%e7%90%86%e5%86%85%e5%ad%98%e5%b0%b1%e8%a6%81%e6%bb%a1%e4%ba%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;这种情况一般是 &lt;code&gt;ZRAM&lt;/code&gt; 块最大值太高了。&lt;/p&gt;
&lt;p&gt;同样也会造成 &lt;code&gt;ZRAM&lt;/code&gt; 往回写块驱逐匿名页，但是如果回写块写入很慢造成了阻塞，同时内存占用还在增加，那么操作系统可能就触发 &lt;code&gt;OOM&lt;/code&gt; 开始杀进程了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;ZSWAP
&lt;div id="zswap" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#zswap" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;还有另外一种技术，但是我之前都没听说过。&lt;/p&gt;
&lt;p&gt;这项技术配置起来感觉更加 native 一点，因为是直接往内核参数里面写。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ZSWAP&lt;/code&gt; 其实不会创建一个块，但是需要 &lt;code&gt;swap&lt;/code&gt; 的存在，当系统打算往 &lt;code&gt;swap&lt;/code&gt; 里面写入数据的时候，&lt;code&gt;ZSWAP&lt;/code&gt; 会拦截写入，然后进行压缩到内存中的一部分区域。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ZSWAP&lt;/code&gt; 同样会在内存压力高的时候驱逐匿名页到 &lt;code&gt;swap&lt;/code&gt; 中，这一点跟 &lt;code&gt;ZRAM&lt;/code&gt; 不一样，因为如果长期 &lt;code&gt;ZRAM&lt;/code&gt; 占满，那么系统就会转而使用磁盘 &lt;code&gt;SWAP&lt;/code&gt; 交换，此时变慢会变得很明显（尤其是磁盘速度较慢的时候）。&lt;/p&gt;
&lt;p&gt;从 &lt;code&gt;Linux 6.8&lt;/code&gt; 开始，支持禁用回写块，仅仅用作内存压缩。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;所以你会发现，&lt;code&gt;ZRAM&lt;/code&gt; 加上了回写块，那么就是普通的 &lt;code&gt;ZSWAP&lt;/code&gt;；如果 &lt;code&gt;ZSWAP&lt;/code&gt; 禁用了回写块，那么就相当于一个普通的 &lt;code&gt;ZRAM&lt;/code&gt;，从使用的角度，确实是这样的。&lt;/p&gt;
&lt;h2 class="relative group"&gt;如何选择
&lt;div id="如何选择" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a6%82%e4%bd%95%e9%80%89%e6%8b%a9" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;既然这两者都可以在使用时相互替代，那么究竟如何选择呢？&lt;/p&gt;
&lt;h3 class="relative group"&gt;桌面环境
&lt;div id="桌面环境" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a1%8c%e9%9d%a2%e7%8e%af%e5%a2%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;如果内存足够，更推荐使用 &lt;code&gt;ZSWAP&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如要使用休眠，可以考虑禁用回写块。这样 &lt;code&gt;swap&lt;/code&gt; 就专注于配置休眠，但这样负载高了就更容易出现 &lt;code&gt;OOM&lt;/code&gt;，这点需要权衡，一般也不会跑大型任务的时候考虑休眠吧？&lt;/p&gt;
&lt;h3 class="relative group"&gt;其他环境
&lt;div id="其他环境" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b6%e4%bb%96%e7%8e%af%e5%a2%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;小内存的设备，比如安卓，更加倾向于 &lt;code&gt;ZRAM&lt;/code&gt;。原因是 &lt;code&gt;ZSWAP&lt;/code&gt; 要求真实的 &lt;code&gt;SWAP&lt;/code&gt; 设备，但是闪存一般来说没那么耐写。&lt;/p&gt;
&lt;p&gt;服务器可以使用 &lt;code&gt;ZRAM&lt;/code&gt; 加上一个小 &lt;code&gt;SWAP&lt;/code&gt;，这样可以很好的避免了内存使用的尖锋，也享受了内存压缩的红利。&lt;/p&gt;
&lt;p&gt;不推荐同时使用 &lt;code&gt;ZSWAP&lt;/code&gt; 和 &lt;code&gt;ZRAM&lt;/code&gt;，这样会造成冲突。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;两者有什么优势呢？&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ZSWAP&lt;/code&gt;：管理起来更直观，对于桌面体验来说更加友好。因为是钩子，所以不会出现一个 &lt;code&gt;swap&lt;/code&gt; 块。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ZRAM&lt;/code&gt;：更适合小内存闪存不可多写的设备。不仅仅是内存压缩，实际上块设备可以格式化来存东西。&lt;/p&gt;
&lt;h2 class="relative group"&gt;观察
&lt;div id="观察" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a7%82%e5%af%9f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="zswap"
width="1137"
height="58"
src="https://minortex.github.io/posts/linux-swap-zram-zswap/zswap_hu_bfe93907a86b0fb5.webp"
srcset="https://minortex.github.io/posts/linux-swap-zram-zswap/zswap_hu_bfe93907a86b0fb5.webp 800w, https://minortex.github.io/posts/linux-swap-zram-zswap/zswap.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/linux-swap-zram-zswap/zswap.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;这是一个 &lt;code&gt;ZSWAP&lt;/code&gt; 的 &lt;code&gt;htop&lt;/code&gt; 图，&lt;code&gt;compressed&lt;/code&gt; 和 &lt;code&gt;frontswap&lt;/code&gt; 就是 &lt;code&gt;ZSWAP&lt;/code&gt; 工作的事实。&lt;/p&gt;
&lt;p&gt;通过简单的计算可以得到压缩率是 26.3%，这也是配置 &lt;code&gt;zstd&lt;/code&gt; 的典型压缩率范围。&lt;/p&gt;
&lt;p&gt;前置压缩虽然其实占用的是内存中的空间，但是这里算在 &lt;code&gt;swap&lt;/code&gt; 里面了， &lt;code&gt;used&lt;/code&gt; 里面的 &lt;code&gt;457M&lt;/code&gt; 不包括前置交换，由于使用 &lt;code&gt;systemd&lt;/code&gt; 禁用了 &lt;code&gt;ZSWAP&lt;/code&gt; 使用系统交换空间，所以这里面的内容其实是内核使用的交换空间。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;如果是一个 &lt;code&gt;ZRAM&lt;/code&gt; 的设备，那么 &lt;code&gt;htop&lt;/code&gt; 甚至可以单独调出一条来显示它，从观察角度来说，&lt;code&gt;ZRAM&lt;/code&gt; 相对方便一些。&lt;/p&gt;
&lt;h2 class="relative group"&gt;参考
&lt;div id="参考" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%82%e8%80%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;(电源管理/挂起与休眠 - Arch Linux 中文维基)[https://wiki.archlinuxcn.org/wiki/%E7%94%B5%E6%BA%90%E7%AE%A1%E7%90%86/%E6%8C%82%E8%B5%B7%E4%B8%8E%E4%BC%91%E7%9C%A0#Disable_zswap_writeback_to_use_the_swap_space_only_for_hibernation]&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：移动语义、智能指针与值类别</title><link>https://minortex.github.io/posts/modern-cpp-8-move-semantics-smartptr/</link><pubDate>Thu, 30 Oct 2025 14:34:07 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-8-move-semantics-smartptr/</guid><description>
&lt;h2 class="relative group"&gt;左右值、&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;、移动语义、智能指针
&lt;div id="左右值移动语义智能指针" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b7%a6%e5%8f%b3%e5%80%bc%e7%a7%bb%e5%8a%a8%e8%af%ad%e4%b9%89%e6%99%ba%e8%83%bd%e6%8c%87%e9%92%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;有两个重要概念需要区分：&lt;/p&gt;
&lt;h3 class="relative group"&gt;类型 (Type)
&lt;div id="类型-type" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b1%bb%e5%9e%8b-type" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;表示一段内存究竟是怎么样的性质。对于一段内存，它内部的结构应该是怎么样被解析的，使用什么样的运算符。&lt;/p&gt;
&lt;p&gt;其实就是一开始的类型系统，包括基本类型的 &lt;code&gt;int&lt;/code&gt;、&lt;code&gt;double&lt;/code&gt;，也可以是标准库中的 &lt;code&gt;std::string&lt;/code&gt;，用户在 &lt;code&gt;class&lt;/code&gt; 定义类型。&lt;/p&gt;
&lt;h3 class="relative group"&gt;值类别 (Value Category)
&lt;div id="值类别-value-category" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%80%bc%e7%b1%bb%e5%88%ab-value-category" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在移动语义后完善的概念，重点关注此表达式本身的性质，具有什么身份，是否可以被移动。&lt;/p&gt;
&lt;p&gt;在 C++98，只有&lt;strong&gt;左值&lt;/strong&gt;和&lt;strong&gt;右值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但是在 C++11，我们引入了移动语义，出现了一个&lt;strong&gt;将亡值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;将亡值有身份可以取地址，但是马上被移动也就消失了，所以我们扩大了左值的定义，认为他是&lt;strong&gt;广义左值&lt;/strong&gt;；&lt;/p&gt;
&lt;p&gt;同时，因为他即将消失，所以认为是右值，那么就把以前的右值定义为&lt;strong&gt;纯右值&lt;/strong&gt;，这样五种值类别就出来了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;/&lt;code&gt;&amp;amp;&lt;/code&gt; 作为函数形参
&lt;div id="-作为函数形参" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#-%e4%bd%9c%e4%b8%ba%e5%87%bd%e6%95%b0%e5%bd%a2%e5%8f%82" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;考虑下面这个函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;给出两个 &lt;code&gt;fun&lt;/code&gt; 的函数声明：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事实上，只有第二个能够匹配传入的参数，而不是第一个。&lt;/p&gt;
&lt;p&gt;在没有引用的时候，我们的函数形参其实是匹配对象的类型的，因为只有确定了类型，函数内部才知道这个对象使用哪一种操作符，如何解释这一段内存。&lt;/p&gt;
&lt;p&gt;引入引用之后，带 &lt;code&gt;&amp;amp;&lt;/code&gt; 的参数会解释这个对象如何绑定到实参：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;左值引用&lt;/strong&gt;：只能绑定到左值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常量左值引用&lt;/strong&gt;：能绑定到左值和右值，如果&lt;strong&gt;直接&lt;/strong&gt;绑定到右值，会延长该对象的生命周期。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;右值引用&lt;/strong&gt;：绑定到右值时，在函数内部转换成一个左值；绑定到左值时，是一个左值引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是需要注意一点，虽然 &lt;code&gt;ref&lt;/code&gt; 是一个 &lt;code&gt;int&amp;amp;&amp;amp;&lt;/code&gt; 类型的对象，但是他的值类别其实是一个左值，所以形参可以是一个 (常量) 左值引用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 在模板参数的使用
&lt;div id="-在模板参数的使用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#-%e5%9c%a8%e6%a8%a1%e6%9d%bf%e5%8f%82%e6%95%b0%e7%9a%84%e4%bd%bf%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;仅在模板以及泛型中，&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 才有引用折叠作用，常用于完美转发。&lt;/p&gt;
&lt;p&gt;最常见的在于这样一个泛型函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;传入左值的时候，会被折叠成 &lt;code&gt;T&amp;amp;&lt;/code&gt;，而传入右值的时候，保留 &lt;code&gt;T&amp;amp;&amp;amp;&lt;/code&gt; 的类型，&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;非常智能，自动判断左右值。&lt;/p&gt;
&lt;h3 class="relative group"&gt;实现移动
&lt;div id="实现移动" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%9e%e7%8e%b0%e7%a7%bb%e5%8a%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;std::move()&lt;/code&gt; 返回的值是一个将亡值 (xvalue)，至于如何把这个值移动，是类的移动构造函数/移动赋值运算符实现的。&lt;/p&gt;
&lt;p&gt;所以如果手动编写的移动构造函数/移动赋值运算符，需要注意：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;加上 &lt;code&gt;noexcept&lt;/code&gt; 关键字，避免抛出异常（要么成功，要么终止，避免移动一半），避免回退到复制。&lt;/li&gt;
&lt;li&gt;手动把地址给到目标，然后把原来的指针指向空指针。&lt;/li&gt;
&lt;li&gt;对于运算符，可以加上判断一下不是自己给自己，如果是就返回 &lt;code&gt;*this&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;至于源对象如何释放，是取决它本身的生命周期的。此时我们已经完成了所有权的转移。&lt;/p&gt;
&lt;h3 class="relative group"&gt;智能指针
&lt;div id="智能指针" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%99%ba%e8%83%bd%e6%8c%87%e9%92%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;unique_ptr&lt;/code&gt;
&lt;div id="unique_ptr" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#unique_ptr" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;这个指针指向的堆内存必须是它独占的，如果使用移动语义来赋值的话，会自动把上一个指针置空。&lt;/p&gt;
&lt;p&gt;不要用一个裸指针来初始化一个智能指针，虽然语法上成立，但是语义上会破坏 &lt;code&gt;unique&lt;/code&gt; 的独占性，仍然会发生双重释放的问题。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;使用 &lt;code&gt;make_unique&lt;/code&gt; 将一个纯右值的对象变成独占的指针，不用手动 &lt;code&gt;new&lt;/code&gt; 故比较推荐。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make_unique&lt;/code&gt; 其实是一个函数，为一个纯右值返回一个独占指针，所以返回值一定会有一个指针接收，一般用 &lt;code&gt;auto&lt;/code&gt; 配合效果很好：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fraction&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fraction&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;如果要求传入一个类型的裸指针，但是手上只有智能指针，可以使用 &lt;code&gt;.get()&lt;/code&gt; 来得到一个底层中使用的裸指针的副本。&lt;/p&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;shared_ptr&lt;/code&gt; 和 &lt;code&gt;weak_ptr&lt;/code&gt;
&lt;div id="shared_ptr-和-weak_ptr" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#shared_ptr-%e5%92%8c-weak_ptr" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;共享指针允许多个指针同时指向一块内存，同时其内部会保存指向这段内存的计数。当最后一个共享指针销毁的时候，内存将会被自动释放。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;不过 &lt;code&gt;shared_ptr&lt;/code&gt; 会导致两个共享指针相互依赖的问题，这样在释放的时候认为是互相依赖使得最终内存无法释放导致内存泄漏。&lt;/p&gt;
&lt;p&gt;最典型的例子就是，在一个类中有 &lt;code&gt;shared_ptr&lt;/code&gt; 的成员变量，然后两个实例化的对象的共享指针指向对方，同时具有相同的生命周期，这样释放的时候互相认为对方存在，从而都不释放内存。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;因此，&lt;code&gt;weak_ptr&lt;/code&gt; 出现了。弱指针不增加引用计数，因此可以很好的解决上面的问题。&lt;/p&gt;
&lt;p&gt;弱指针其实有点像 &lt;code&gt;string_view&lt;/code&gt;，他们都只是一个视图（观察者）而已。&lt;/p&gt;
&lt;p&gt;使用弱指针：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.expired()&lt;/code&gt; 可以检测弱指针是否指向无效的内存。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.lock()&lt;/code&gt; 锁定指向的内容，把引用计数 +1，同时返回一个 &lt;code&gt;shared_ptr&lt;/code&gt;；如果已过期，那么会返回 &lt;code&gt;nullptr&lt;/code&gt;（这样甚至不需要 &lt;code&gt;std::optional&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;关系
&lt;div id="关系" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b3%e7%b3%bb" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;unique_ptr&lt;/code&gt; 可以转化为 &lt;code&gt;shared_ptr&lt;/code&gt;，反之不能转化。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;unique_ptr&lt;/code&gt; 只能移动不能拷贝，&lt;code&gt;shared_ptr&lt;/code&gt; 和 &lt;code&gt;weak_ptr&lt;/code&gt; 可以拷贝。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shared_ptr&lt;/code&gt; 可以被 &lt;code&gt;weak_ptr&lt;/code&gt; 创建一个视图。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;weak_ptr&lt;/code&gt; 的成员函数 &lt;code&gt;.lock()&lt;/code&gt; 有效时会返回一个 &lt;code&gt;shared_ptr&lt;/code&gt;，否则会返回一个 &lt;code&gt;nullptr&lt;/code&gt;。&lt;/p&gt;
&lt;h2 class="relative group"&gt;组合、聚合和关联
&lt;div id="组合聚合和关联" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%84%e5%90%88%e8%81%9a%e5%90%88%e5%92%8c%e5%85%b3%e8%81%94" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left"&gt;属性 (Property)&lt;/th&gt;
&lt;th style="text-align: left"&gt;组合 (Composition)&lt;/th&gt;
&lt;th style="text-align: left"&gt;聚合 (Aggregation)&lt;/th&gt;
&lt;th style="text-align: left"&gt;关联 (Association)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;关系类型&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left"&gt;整体/部分&lt;/td&gt;
&lt;td style="text-align: left"&gt;整体/部分&lt;/td&gt;
&lt;td style="text-align: left"&gt;其他不相关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;部分可属于多个整体&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left"&gt;否&lt;/td&gt;
&lt;td style="text-align: left"&gt;是&lt;/td&gt;
&lt;td style="text-align: left"&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;部分的存在由整体管理&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left"&gt;是&lt;/td&gt;
&lt;td style="text-align: left"&gt;否&lt;/td&gt;
&lt;td style="text-align: left"&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;方向性&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left"&gt;单向&lt;/td&gt;
&lt;td style="text-align: left"&gt;单向&lt;/td&gt;
&lt;td style="text-align: left"&gt;单向或双向&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;关系动词&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left"&gt;是&amp;hellip;的一部分 (Part-of)&lt;/td&gt;
&lt;td style="text-align: left"&gt;拥有 (Has-a)&lt;/td&gt;
&lt;td style="text-align: left"&gt;使用 (Uses-a)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;**方式&lt;/td&gt;
&lt;td style="text-align: left"&gt;类成员&lt;/td&gt;
&lt;td style="text-align: left"&gt;引用 (数组)&lt;/td&gt;
&lt;td style="text-align: left"&gt;引用 (数组)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 class="relative group"&gt;初始化列表 &lt;code&gt;initializer_list&lt;/code&gt;
&lt;div id="初始化列表-initializer_list" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9d%e5%a7%8b%e5%8c%96%e5%88%97%e8%a1%a8-initializer_list" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;如果想要让自己的类使用一个初始化列表来初始化，需要手动添加一个重载的构造函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;m_size&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;m_data&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_size&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;m_data&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)]}{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;initializer_list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 用委托构造函数来创建一个
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;m_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 3/5/0 法则的其他函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>现代 C++ 笔记：Lambda、运算符重载与闭包</title><link>https://minortex.github.io/posts/modern-cpp-7-lambda-operator-overload/</link><pubDate>Wed, 22 Oct 2025 16:29:09 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-7-lambda-operator-overload/</guid><description>
&lt;h2 class="relative group"&gt;命令行参数
&lt;div id="命令行参数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%8f%82%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;早就知道在 &lt;code&gt;c&lt;/code&gt; 中是可以在 &lt;code&gt;main&lt;/code&gt; 中获取从命令行传入的参数的。但是对于解析是一窍不通，这里简单说一下如何获取整形。&lt;/p&gt;
&lt;p&gt;不过，这些方法都仅仅适用于简单的字符串处理，而一个简单的带参 &lt;code&gt;cli&lt;/code&gt; 程序，基本都要求参数能够无视顺序，可选参数，这些统统都不能实现&amp;hellip;&lt;/p&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;std::stringstream&lt;/code&gt;
&lt;div id="stdstringstream" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdstringstream" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这个函数用于把字符串转换成流，然后再把流输出到整形。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stringstream&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;arcv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;std::stoi
&lt;div id="stdstoi" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdstoi" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这个就是现代 C++ 的方法了。&lt;/p&gt;
&lt;p&gt;直接转换就行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 class="relative group"&gt;lambda 表达式
&lt;div id="lambda-表达式" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lambda-%e8%a1%a8%e8%be%be%e5%bc%8f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;lambda 表达式看起来像这样子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;parameter&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;参数列表可以省略，返回值也可以省略，会自动推断。&lt;/p&gt;
&lt;p&gt;所以说，最简的 &lt;code&gt;lambda&lt;/code&gt; 表达式：&lt;code&gt;[]{}&lt;/code&gt;。&lt;/p&gt;
&lt;h3 class="relative group"&gt;捕获
&lt;div id="捕获" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8d%95%e8%8e%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;对于以下变量来说，不需要捕获也可使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全局变量，静态局部变量，线程局部变量&lt;/li&gt;
&lt;li&gt;常量表达式（包括被编译器视为常量表达式的变量）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;捕获语法是 &lt;code&gt;lambda&lt;/code&gt; 表达式中一个不可缺少的部分，词法分析就是通过 &lt;code&gt;[]&lt;/code&gt; 来发现这是一个表达式的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;lambda&lt;/code&gt; 表达式不和 &lt;code&gt;{}&lt;/code&gt; 一样，不能访问上级大括号中的局部变量。它只能访问静态和全局的变量，以及 &lt;code&gt;constexpr&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而捕获其实就是把原来应该能让 &lt;code&gt;lambda&lt;/code&gt; 能够使用的局部变量让它使用。&lt;/p&gt;
&lt;p&gt;捕获的时候可以声明新变量的，在 &lt;code&gt;[]&lt;/code&gt; 内，可以使用当前块内能访问的变量来参与运算。&lt;/p&gt;
&lt;p&gt;捕获有两种类型：默认捕获和单个捕获。&lt;/p&gt;
&lt;h3 class="relative group"&gt;按对象捕获
&lt;div id="按对象捕获" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8c%89%e5%af%b9%e8%b1%a1%e6%8d%95%e8%8e%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;假设有变量 &lt;code&gt;a&lt;/code&gt; &lt;code&gt;b&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传值：&lt;code&gt;[a,b]&lt;/code&gt;。默认传入变量的&lt;strong&gt;常量&lt;/strong&gt;值，如果需要修改，需要在大括号前加上 &lt;code&gt;mutable&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;传引用：&lt;code&gt;[&amp;amp;a,&amp;amp;b]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;默认捕获
&lt;div id="默认捕获" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%bb%98%e8%ae%a4%e6%8d%95%e8%8e%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;默认捕获会捕获 &lt;code&gt;lambda&lt;/code&gt; 中提到的所有变量。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[=]&lt;/code&gt;：传入值&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[&amp;amp;]&lt;/code&gt;：传入引用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不允许同时使用默认捕获和按对象的捕获。&lt;/p&gt;
&lt;h3 class="relative group"&gt;创建闭包
&lt;div id="创建闭包" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9b%e5%bb%ba%e9%97%ad%e5%8c%85" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;提到闭包这个词，就不得不提函数式编程了。简单来说，函数是“一等公民”，就像我们传统的命令式编程的变量（对象）一样，可以作为参数传递，可以被返回，可以运行时创建等等。&lt;/p&gt;
&lt;p&gt;那么闭包就是保存了当前上下文中某些状态的函数。&lt;/p&gt;
&lt;p&gt;在 C++ 中，想实现一个闭包，还是得定义一个普通函数（虽然说跟一般的匿名函数印象不太一样）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;multiply_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;multiply_4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;multiply_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;multiply_4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;multiply_3&lt;/code&gt; 和 &lt;code&gt;multiply_4&lt;/code&gt; 就是一个闭包。&lt;/p&gt;
&lt;h3 class="relative group"&gt;实现一个简单的函数
&lt;div id="实现一个简单的函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e7%ae%80%e5%8d%95%e7%9a%84%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;&lt;code&gt;lambda&lt;/code&gt; 表达式可以作为 &lt;code&gt;lambda&lt;/code&gt; 类型的变量存储。
&lt;div id="lambda-表达式可以作为-lambda-类型的变量存储" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lambda-%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8f%af%e4%bb%a5%e4%bd%9c%e4%b8%ba-lambda-%e7%b1%bb%e5%9e%8b%e7%9a%84%e5%8f%98%e9%87%8f%e5%ad%98%e5%82%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;{[](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 class="relative group"&gt;也可以声明的时候就调用：
&lt;div id="也可以声明的时候就调用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b9%9f%e5%8f%af%e4%bb%a5%e5%a3%b0%e6%98%8e%e7%9a%84%e6%97%b6%e5%80%99%e5%b0%b1%e8%b0%83%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Hello world!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 前一个括号是形参列表，后一个括号表示调用函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;值得提的一点是，这其实不是真正的函数，实际上只是重载了 &lt;code&gt;()&lt;/code&gt; 实现像函数一样的调用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;在算法库的应用
&lt;div id="在算法库的应用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9c%a8%e7%ae%97%e6%b3%95%e5%ba%93%e7%9a%84%e5%ba%94%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;前面在 &lt;code&gt;&amp;lt;algorithm&amp;gt;&lt;/code&gt; 中，最后一个参数是比较函数的地址，其实我们可以直接在这里写 &lt;code&gt;lambda&lt;/code&gt; 函数。&lt;/p&gt;
&lt;h3 class="relative group"&gt;lambda 推断
&lt;div id="lambda-推断" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lambda-%e6%8e%a8%e6%96%ad" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;对于实参推断（给出 &lt;code&gt;(auto a, auto b)&lt;/code&gt; 的情况，只要函数体里面的能匹配，那么没有问题，因为编译器会自动生成重载的 &lt;code&gt;lambda&lt;/code&gt; 表达式。&lt;/p&gt;
&lt;p&gt;对于返回值，要求所有返回的地方一致才能推断，否则，请使用 &lt;code&gt;-&amp;gt;&lt;/code&gt; 手动指定返回值类型。&lt;/p&gt;
&lt;h3 class="relative group"&gt;带捕获的 &lt;code&gt;lambda&lt;/code&gt; 拷贝
&lt;div id="带捕获的-lambda-拷贝" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b8%a6%e6%8d%95%e8%8e%b7%e7%9a%84-lambda-%e6%8b%b7%e8%b4%9d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;如果对带捕获的 &lt;code&gt;lambda&lt;/code&gt; 表达式操作的时候，目标的类型是 &lt;code&gt;std::function&lt;/code&gt;，那么会发生隐式类型转换，将 &lt;code&gt;lambda&lt;/code&gt; 闭包进行深拷贝，使得多次使用 &lt;code&gt;std::function&lt;/code&gt; 构造的多个对象出现独立的拥有原始闭包状态的副本。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;lambda&lt;/code&gt; 构造的时候使用了 &lt;code&gt;mutable&lt;/code&gt; 的值捕获，那么每次捕获的变量都是原始的捕获值。&lt;/p&gt;
&lt;p&gt;解决方法是使用 &lt;code&gt;std::ref&lt;/code&gt; 来包装 &lt;code&gt;lambda&lt;/code&gt; 表达式。对于传入参数不一致的情况，它会返回一个 &lt;code&gt;std::reference_wrapper&lt;/code&gt; 对象，保证内部存储的是对原始 &lt;code&gt;lambda&lt;/code&gt; 对象的引用，从而保证每次调用的是同一个 &lt;code&gt;lambda&lt;/code&gt; 表达式对象。&lt;/p&gt;
&lt;h2 class="relative group"&gt;运算符重载
&lt;div id="运算符重载" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bf%90%e7%ae%97%e7%ac%a6%e9%87%8d%e8%bd%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;有三种办法重载：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;友元函数&lt;/strong&gt;：如果想要重载操作数是类对象的运算符，为了获取类内成员的访问权限，可以把此重载函数以友元的形式声明在类内。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;普通函数&lt;/strong&gt;：同时可以使用 &lt;code&gt;getter&lt;/code&gt; 和 &lt;code&gt;setter&lt;/code&gt; 来避免直接访问内部的成员变量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;类成员函数&lt;/strong&gt;：仅适用于左侧操作数是类成员的情况，此时右操作数就作为参数。&lt;/p&gt;
&lt;p&gt;一些选择建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重载赋值、下标 (&lt;code&gt;[]&lt;/code&gt;)、函数调用、成员选择 (&lt;code&gt;-&amp;gt;&lt;/code&gt;) -&amp;gt; 类成员函数&lt;/li&gt;
&lt;li&gt;重载一元运算符 -&amp;gt; 类成员函数&lt;/li&gt;
&lt;li&gt;不修改左操作数 -&amp;gt; 普通/友元函数&lt;/li&gt;
&lt;li&gt;左操作数是内置类型无法修改 -&amp;gt; 普通/友元函数&lt;/li&gt;
&lt;li&gt;左操作数可以修改 -&amp;gt; 类成员函数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;重载函数一般返回左侧的对象&lt;strong&gt;的引用&lt;/strong&gt;（以便可以连续用 &lt;code&gt;.&lt;/code&gt; 调用）或者返回一个同类型的对象（用于连续使用重载运算符比如 &lt;code&gt;+=&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 class="relative group"&gt;重载一元+/-
&lt;div id="重载一元-" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%87%8d%e8%bd%bd%e4%b8%80%e5%85%83-" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;由于一元的 &lt;code&gt;+/-&lt;/code&gt; 是类成员函数，而且没有参数，所以不会和二元的混淆。&lt;/p&gt;
&lt;h3 class="relative group"&gt;重载自增/减
&lt;div id="重载自增减" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%87%8d%e8%bd%bd%e8%87%aa%e5%a2%9e%e5%87%8f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;前自增/减参数列表没有参数，而后自增/减有一个 &lt;code&gt;int&lt;/code&gt; 用于占位，从而将两者区分。&lt;/p&gt;
&lt;p&gt;由于后自增/减要返回原来的值，所以需要一个临时变量用于返回，相比前自增/减多了开销，更倾向使用前自增/减。&lt;/p&gt;
&lt;h3 class="relative group"&gt;下标访问重载
&lt;div id="下标访问重载" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%8b%e6%a0%87%e8%ae%bf%e9%97%ae%e9%87%8d%e8%bd%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;下标访问的重载返回值一般使用 &lt;code&gt;auto&amp;amp;&lt;/code&gt;，从而作为左值被修改。&lt;/p&gt;
&lt;p&gt;在 C++23 之前，如果定义类的时候使用 &lt;code&gt;const&lt;/code&gt;，那么就需要添加一个成员重载函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个函数被常量对象优先使用，被非常量对象次优先使用。&lt;/p&gt;
&lt;p&gt;在 C++23，用右值引用解决此问题。&lt;/p&gt;
&lt;h3 class="relative group"&gt;飞船运算符
&lt;div id="飞船运算符" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%a3%9e%e8%88%b9%e8%bf%90%e7%ae%97%e7%ac%a6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;外来语言的符号，用于比较两个数大小关系。&lt;/p&gt;
&lt;p&gt;返回下面类型：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;strong_ordering&lt;/code&gt;&lt;/strong&gt;: 有&lt;code&gt;equivalent&lt;/code&gt;、&lt;code&gt;equal&lt;/code&gt;、&lt;code&gt;greater&lt;/code&gt; 和 &lt;code&gt;less&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;weak_ordering&lt;/code&gt;&lt;/strong&gt;: 比 &lt;code&gt;strong&lt;/code&gt; 少了相等，因为是弱，只有等效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;partial_ordering&lt;/code&gt;&lt;/strong&gt;: 相比 &lt;code&gt;weak&lt;/code&gt; 多了 &lt;code&gt;unordered&lt;/code&gt;，用于表示无法比较的情况。&lt;/p&gt;
&lt;p&gt;从上往下可以隐式转换。&lt;/p&gt;
&lt;h3 class="relative group"&gt;类型转换重载
&lt;div id="类型转换重载" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b1%bb%e5%9e%8b%e8%bd%ac%e6%8d%a2%e9%87%8d%e8%bd%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;类型转换重载也是 &lt;code&gt;operator&lt;/code&gt; 操作符，不过重载的目标其实有点像 C 的强制转换：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// conversion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;intType_obj&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;无须返回值，也无须参数，因为显而易见的返回目标类型，传入的是这个类对象，这也必须是一个类成员函数。&lt;/p&gt;
&lt;p&gt;建议加上 &lt;code&gt;explicit&lt;/code&gt; 关键字，因为我们不希望隐式转换带来的不确定性。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何平衡类型转换重载和转换构造函数？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;假设要把 A 转换成 B：&lt;/p&gt;
&lt;p&gt;A 可改，B 不可改。那么就定义一个类型转换的重载。&lt;/p&gt;
&lt;p&gt;A 不可改，B 可改。那么就定义一个参数只含 A 的转换构造函数，让 B 去接收 A。&lt;/p&gt;
&lt;p&gt;A、B 都不可改。那么定义一个普通函数实现转换吧。&lt;/p&gt;
&lt;h3 class="relative group"&gt;复制运算符重载
&lt;div id="复制运算符重载" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a4%8d%e5%88%b6%e8%bf%90%e7%ae%97%e7%ac%a6%e9%87%8d%e8%bd%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在动态分配内存中，有时为了避免自己赋值给自己，用一个 &lt;code&gt;if&lt;/code&gt; 来设置保护防止出现空指针问题。&lt;/p&gt;
&lt;p&gt;在三/五/零原则中，如果不带指针，那么应该不应该定义这个复制运算符重载。（其他：析构、复制构造，移动运算符，移动构造函数）&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：容器、迭代器与算法库</title><link>https://minortex.github.io/posts/modern-cpp-6-containers-iterators-algorithms/</link><pubDate>Mon, 13 Oct 2025 08:25:04 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-6-containers-iterators-algorithms/</guid><description>
&lt;h2 class="relative group"&gt;std::vector
&lt;div id="stdvector" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdvector" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;此向量非彼向量。&lt;/p&gt;
&lt;h3 class="relative group"&gt;对于字符串
&lt;div id="对于字符串" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%af%b9%e4%ba%8e%e5%ad%97%e7%ac%a6%e4%b8%b2" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;推荐使用&lt;code&gt;string_view&lt;/code&gt;配合 C 风格的字符串，这样效率最高，只发生一次堆内存分配。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Alex&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Betty&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Caroline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Dave&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Emily&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Fred&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Greg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Holly&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;列表初始化器
&lt;div id="列表初始化器" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%97%e8%a1%a8%e5%88%9d%e5%a7%8b%e5%8c%96%e5%99%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;std::vector vec(3)&lt;/code&gt;会初始化一个全 0，长度为 3 的向量；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;std::vecotr vec(2,3)&lt;/code&gt;会初始化一个长度 2，值都为 3 的向量。&lt;/li&gt;
&lt;li&gt;但是&lt;code&gt;std::vector vec({3})&lt;/code&gt;会初始化一个长度 1 值为 3 的向量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为给出单参数的时候，会优先调用列表初始化器，这个初始化器的行为是初始化长度为参数的向量。&lt;/p&gt;
&lt;h3 class="relative group"&gt;下标
&lt;div id="下标" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%8b%e6%a0%87" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;当时设计让&lt;code&gt;size_t&lt;/code&gt;是一个无符号的整数，从现在看来是一个错误，因为下标访问最容易导致环绕，最好的办法是不使用下标访问而是迭代器，次选是使用 C 风格的访问&lt;code&gt;.data()&lt;/code&gt;。然后全部使用有符号整数。&lt;/p&gt;
&lt;h3 class="relative group"&gt;for-each
&lt;div id="for-each" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#for-each" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;推荐使用这些：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;修改元素副本&lt;/strong&gt;：&lt;code&gt;auto&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;修改原始元素&lt;/strong&gt;：&lt;code&gt;auto&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只需要查看&lt;/strong&gt;：&lt;code&gt;const auto&amp;amp;&lt;/code&gt;（大多数情况）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;反向查看：使用&lt;code&gt;std::view::reverse()&lt;/code&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;容量和长度
&lt;div id="容量和长度" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%b9%e9%87%8f%e5%92%8c%e9%95%bf%e5%ba%a6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;容量是分配给向量的内存，长度是实际使用的部分。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pop&lt;/code&gt;的时候长度减少容量不变；&lt;code&gt;push&lt;/code&gt;时如果遇到容量瓶颈，会自动复制扩容（2/1.5）倍。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;resize&lt;/code&gt;成员函数会同时改变容量和长度，&lt;code&gt;reserve&lt;/code&gt;只改变容量。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;emplace_back&lt;/code&gt;会显示调用构造函数，直接在堆上预留的内存构建对象，如果是是临时对象，那么要注意如果有&lt;code&gt;explicit&lt;/code&gt;那么不能执行隐式转换。而&lt;code&gt;push_back&lt;/code&gt;会发生一次拷贝。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;make_unique&lt;/code&gt; 下，始终会产生一次拷贝，此时两者表现一致。&lt;/p&gt;
&lt;h2 class="relative group"&gt;std::array
&lt;div id="stdarray" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdarray" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;array&lt;/code&gt; 大部分功能都与 &lt;code&gt;constexpr&lt;/code&gt; 兼容。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;constexpr&lt;/code&gt; 表达式执行的从 &lt;code&gt;unsign&lt;/code&gt; 到 &lt;code&gt;sign&lt;/code&gt; 的转换是允许的，因为编译器对此信任程度很高。&lt;/p&gt;
&lt;h3 class="relative group"&gt;.at()、[]和 std::get()
&lt;div id="at和-stdget" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#at%e5%92%8c-stdget" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.at()&lt;/code&gt;&lt;/strong&gt;: 运行时边界检查。(C++17 后编译时检查)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::get&amp;lt;index&amp;gt;(arr)&lt;/code&gt;&lt;/strong&gt;: 编译时边界检查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/strong&gt;: 不进行边界检查，C++11 后可为 &lt;code&gt;constexpr&lt;/code&gt; 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于现代 C++ 来说， &lt;code&gt;.at&lt;/code&gt; 是更安全的选择。&lt;/p&gt;
&lt;p&gt;因为会在越界访问的时候抛出异常。而 &lt;code&gt;[]&lt;/code&gt; 不会进行越界检查。&lt;/p&gt;
&lt;h3 class="relative group"&gt;用模板创建数组
&lt;div id="用模板创建数组" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%94%a8%e6%a8%a1%e6%9d%bf%e5%88%9b%e5%bb%ba%e6%95%b0%e7%bb%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;passByRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;static_assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于模板参数是编译时常量，所以可以用 &lt;code&gt;static_assert&lt;/code&gt; ，同时，更加推荐把这两句用 &lt;code&gt;std::get&amp;lt;N&amp;gt;(arr)&lt;/code&gt; 替代。&lt;/p&gt;
&lt;p&gt;在 C++20，可以把 &lt;code&gt;size_t&lt;/code&gt; 替换成 &lt;code&gt;auto&lt;/code&gt; 。&lt;/p&gt;
&lt;h3 class="relative group"&gt;在函数内返回 array
&lt;div id="在函数内返回-array" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9c%a8%e5%87%bd%e6%95%b0%e5%86%85%e8%bf%94%e5%9b%9e-array" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;由于 &lt;code&gt;array&lt;/code&gt; 是在栈上分配的，那么一般来说会执行返回值优化，但是移动语义就没有意义（因为不是在堆上分配的就不能仅转移指针）。如果在 C++17 之前为了避免不确定性，那用 &lt;code&gt;vector&lt;/code&gt; ？但是怎么感觉开销在堆上更大了说&amp;hellip;&lt;/p&gt;
&lt;h3 class="relative group"&gt;初始化聚合数组
&lt;div id="初始化聚合数组" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9d%e5%a7%8b%e5%8c%96%e8%81%9a%e5%90%88%e6%95%b0%e7%bb%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;列表初始化，但是兼容 C 带来的技术债。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;House&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;houses&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 1. 初始化 std::array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 2. 初始化底层 C 风格数组 (元素列表)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第一个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第二个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第三个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时不使用 CTAD，但是必须手动指定模板参数，或者，指定每一个元素的类型，此时可以 CTAD：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="n"&gt;houses&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 1. 初始化 std::array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第一个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第二个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Horse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// 3. 初始化第三个 House 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;reference_wrapper
&lt;div id="reference_wrapper" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reference_wrapper" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;容器中不能给模板传入 &lt;code&gt;int&amp;amp;&lt;/code&gt; 但是可以传入一个 &lt;code&gt;std::reference_wrapper&amp;lt;int&amp;gt;&lt;/code&gt; 起到同样的作用，或者直接用 &lt;code&gt;std::ref()&lt;/code&gt; &lt;code&gt;std::cref()&lt;/code&gt; 直接把变量转换成引用。&lt;/p&gt;
&lt;h2 class="relative group"&gt;迭代器
&lt;div id="迭代器" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bf%ad%e4%bb%a3%e5%99%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;迭代器一般使用 &lt;code&gt;!=end&lt;/code&gt; 来判断是否到达末尾，因为不是所有迭代器都可以比较。&lt;/p&gt;
&lt;p&gt;其本身是一个指针，指向当前的容器元素。&lt;/p&gt;
&lt;p&gt;写的 &lt;code&gt;range based for&lt;/code&gt; 实际上就是迭代器的语法糖。&lt;/p&gt;
&lt;h2 class="relative group"&gt;algorithems 库
&lt;div id="algorithems-库" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#algorithems-%e5%ba%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这是一个算法库，用于偷懒（不是&lt;/p&gt;
&lt;h3 class="relative group"&gt;补充知识
&lt;div id="补充知识" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a1%a5%e5%85%85%e7%9f%a5%e8%af%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;二元谓词：就是接受两个参数，返回一个布尔值的函数。&lt;/p&gt;
&lt;p&gt;在算法库中，二元谓词都需要弱序，即非自反性 (自己不能小于自己)，非对称性 (a 小于 b，那么 b 不能小于 a)，传递性。&lt;/p&gt;
&lt;p&gt;对于 &lt;code&gt;Comp&lt;/code&gt; 这个类型，我们假设前一个参数是 &lt;code&gt;a&lt;/code&gt;，后一个是 &lt;code&gt;b&lt;/code&gt;。那么有：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对 &lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt; 进行某种运算，如果希望最后算法需要保持 &lt;code&gt;a&lt;/code&gt; 排在 &lt;code&gt;b&lt;/code&gt; 的前面，那么就返回 &lt;code&gt;true&lt;/code&gt;；如果算法认为 &lt;code&gt;a&lt;/code&gt; &lt;strong&gt;不&lt;/strong&gt;应该排在 &lt;code&gt;b&lt;/code&gt; 的前面，那么就返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最简单的办法是，假设已经按照一个顺序排列，然后取出前两个元素为 &lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt;，将他们放入表达式，如果返回 &lt;code&gt;true&lt;/code&gt;，那么这就是你要的顺序，为 &lt;code&gt;false&lt;/code&gt; 则是另外一个顺序。&lt;/p&gt;
&lt;h3 class="relative group"&gt;std::sort
&lt;div id="stdsort" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdsort" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;sort 有第三个参数，传入一个二元谓词，在 &lt;code&gt;cppreference&lt;/code&gt; 中类型为 &lt;code&gt;Comp&lt;/code&gt; ,如果是一个函数的话，只需要传地址所以不用 &lt;code&gt;()&lt;/code&gt;。这个函数必须严格弱序的。&lt;/p&gt;
&lt;h3 class="relative group"&gt;std::for_each
&lt;div id="stdfor_each" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdfor_each" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;foreach&lt;/code&gt; 可以用于方便的遍历可以迭代的容器。给定开始和结束的迭代器，和要调用的函数地址（这个函数只能传入一个参数，可用 &lt;code&gt;auto&lt;/code&gt; ），就可以自动迭代。&lt;/p&gt;
&lt;p&gt;同时和 &lt;code&gt;std::next&lt;/code&gt; 配合使用，可以跳过一部分迭代器，获得很高的灵活性。这时候就有了比基于范围的 &lt;code&gt;for&lt;/code&gt; 循环更加灵活。&lt;/p&gt;
&lt;h3 class="relative group"&gt;ranges (C++20)
&lt;div id="ranges-c20" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ranges-c20" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;从 C++20 开始，很多算法添加了对应的重载，直接传入容器的地址就可以实现操作，即 &lt;code&gt;std::ranges::for_each(arr)&lt;/code&gt;。&lt;/p&gt;
&lt;h2 class="relative group"&gt;函数指针
&lt;div id="函数指针" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;C 的函数指针如果不用 &lt;code&gt;typedef&lt;/code&gt;将会非常复杂，而 C++ 提供了一个模板，以一个传入 &lt;code&gt;int, int&lt;/code&gt; 返回 &lt;code&gt;int&lt;/code&gt; 的函数指针为例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&amp;lt;functional&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// some implementation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>搭建基于 VSCode 的跨平台 C++ 学习环境</title><link>https://minortex.github.io/posts/vscode_cpp_environment/</link><pubDate>Sat, 04 Oct 2025 16:45:12 +0800</pubDate><guid>https://minortex.github.io/posts/vscode_cpp_environment/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;早期学习 C/C++ 是非常痛苦的，不仅被老师要求下载 20G 的宇宙 IDE VS，还得自己格式化代码，这就是我一开始对此不感冒的原因。&lt;/p&gt;
&lt;p&gt;但是随着学的东西多了，不写代码也不行啊，我才渐渐发现有那么多强大的工具我不知道。&lt;/p&gt;
&lt;p&gt;所以这里记录一下自己的折腾过程，希望能帮到初学 C++ 的朋友们。&lt;/p&gt;
&lt;h2 class="relative group"&gt;准备
&lt;div id="准备" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%87%86%e5%a4%87" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;由于我主要是在 ArchLinux 上面学习，偶尔会在 Windows 上面测试，这里大部分内容都会在 Linux 上面完成，不过别担心，这些工具都是跨平台的。&lt;/p&gt;
&lt;h3 class="relative group"&gt;Linux
&lt;div id="linux" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#linux" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;很简单，直接安装这些软件包：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clang&lt;/strong&gt; LLVM 的前端，包括了 Clangd 即语言服务器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCC/LLVM&lt;/strong&gt; 编译器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CMake&lt;/strong&gt; 依赖管理/构建系统&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ninja&lt;/strong&gt; 构建器&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Windows
&lt;div id="windows" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#windows" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;推荐用&lt;code&gt;scoop&lt;/code&gt;一键解决，对于这些命令行工具可以说非常方便了。&lt;/p&gt;
&lt;p&gt;需要安装的包：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;llvm-mingw&lt;/strong&gt; 包含了 clangd/clang 了&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CMake&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ninja&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果想&lt;code&gt;GCC&lt;/code&gt;的话，可以到&lt;a
href="https://winlibs.com/"
target="_blank"
&gt;WinLibs&lt;/a&gt;下载：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="winlibs"
width="1178"
height="175"
src="https://minortex.github.io/posts/vscode_cpp_environment/winlibs_hu_631ab03401f3c031.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/winlibs_hu_631ab03401f3c031.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/winlibs.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/winlibs.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;一般来说下&lt;code&gt;UCRT&lt;/code&gt;就行了，从&lt;code&gt;win10&lt;/code&gt;之后都支持&lt;code&gt;UCRT&lt;/code&gt;的。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;安装 VSCode 不用多说了吧？&lt;/p&gt;
&lt;p&gt;安装这些拓展：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CMake Tools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Clangd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CodeLLDB&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="extensions"
width="526"
height="460"
src="https://minortex.github.io/posts/vscode_cpp_environment/extensions.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/extensions.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/extensions.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/extensions.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;CMake 拓展设置
&lt;div id="cmake-拓展设置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cmake-%e6%8b%93%e5%b1%95%e8%ae%be%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;按照如图显示的配置&lt;code&gt;CMake&lt;/code&gt;生成的数据库文件，以便后续的&lt;code&gt;Clangd&lt;/code&gt;查找。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="cmake_tools"
width="2340"
height="1094"
src="https://minortex.github.io/posts/vscode_cpp_environment/cmake_tools_hu_85a62d68cb7e9971.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/cmake_tools_hu_85a62d68cb7e9971.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/cmake_tools_hu_241d9a9da34b0c4d.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/cmake_tools.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Clangd 拓展设置
&lt;div id="clangd-拓展设置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#clangd-%e6%8b%93%e5%b1%95%e8%ae%be%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;填入&lt;code&gt;Clangd&lt;/code&gt;寻找数据库的位置&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="clangd"
width="2289"
height="515"
src="https://minortex.github.io/posts/vscode_cpp_environment/clangd_hu_828056955515cc85.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/clangd_hu_828056955515cc85.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/clangd_hu_72983cb27b02052f.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/clangd.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;CodeLLDB 拓展设置
&lt;div id="codelldb-拓展设置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#codelldb-%e6%8b%93%e5%b1%95%e8%ae%be%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;安装完可能会下载一个调试包，对网络环境要求较高，如果遇到问题可以参考&lt;a
href="https://zhuanlan.zhihu.com/p/566365173"
target="_blank"
&gt;这篇&lt;/a&gt;文章。&lt;/p&gt;
&lt;h2 class="relative group"&gt;创建项目
&lt;div id="创建项目" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9b%e5%bb%ba%e9%a1%b9%e7%9b%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;配置 CMake
&lt;div id="配置-cmake" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%85%8d%e7%bd%ae-cmake" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在你的项目根文件夹建立一个主&lt;code&gt;CMakeLists.txt&lt;/code&gt;,内容可以这么写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmake" data-lang="cmake"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_EXPORT_COMPILE_COMMANDS&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 最低 CMake 版本要求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cmake_minimum_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;VERSION&lt;/span&gt; &lt;span class="s"&gt;3.31&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 项目名称和语言
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;learnproj&lt;/span&gt; &lt;span class="s"&gt;VERSION&lt;/span&gt; &lt;span class="s"&gt;1.0&lt;/span&gt; &lt;span class="s"&gt;LANGUAGES&lt;/span&gt; &lt;span class="s"&gt;CXX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 设置 C++ 标准
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_CXX_STANDARD&lt;/span&gt; &lt;span class="s"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_CXX_STANDARD_REQUIRED&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_CXX_EXTENSIONS&lt;/span&gt; &lt;span class="s"&gt;OFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;GLOB&lt;/span&gt; &lt;span class="s"&gt;SUB_PROJECT_DIRS&lt;/span&gt; &lt;span class="s"&gt;LIST_DIRECTORIES&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/src/*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 遍历所有找到的目录
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;SUB_DIR&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUB_PROJECT_DIRS&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# 检查该目录下是否存在 CMakeLists.txt 文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;IS_DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUB_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;AND&lt;/span&gt; &lt;span class="s"&gt;EXISTS&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${SUB_DIR}/CMakeLists.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# 将该目录名打印出来，方便调试
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;get_filename_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;PROJECT_NAME&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUB_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;STATUS&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Found and added subproject: ${PROJECT_NAME}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# 将这个文件夹作为一个子项目添加进来
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUB_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;endforeach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;## 根据需要按需添加
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# # 查找 Qt 库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 添加可执行文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# # 链接 Qt 库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# target_link_libraries(MyQtProject PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# # 自动处理 MOC、UIC 和 RCC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# set_target_properties(MyQtProject PROPERTIES
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# AUTOMOC ON
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# AUTOUIC ON
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# AUTORCC ON
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可以根据自己的需要，设置 C++ 标准，扫描的文件夹，链接的库。这里就以&lt;code&gt;src&lt;/code&gt;目录放置源文件，&lt;code&gt;include&lt;/code&gt;放置头文件为例。&lt;/p&gt;
&lt;p&gt;进入&lt;code&gt;src/current&lt;/code&gt;创建一个子&lt;code&gt;CMakeLists.txt&lt;/code&gt;，内容写上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmake" data-lang="cmake"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;get_filename_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;PROJECT_NAME&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;## 如果你需要添加别的源文件，别忘了在这里添加文件名，空白符为分隔。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;main.cpp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;MSVC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;PRIVATE&lt;/span&gt; &lt;span class="s"&gt;/W4&lt;/span&gt; &lt;span class="s"&gt;/WX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;else&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;## 非MSVC指定一些警告参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;PRIVATE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;-Wall&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;-Wextra&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;-Wpedantic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# -Werror
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;-Wconversion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;## 这里指定的头文件的路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;target_include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;PRIVATE&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/../../include/&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个&lt;code&gt;main.cpp&lt;/code&gt;，内容可以为空，你的项目应该这样：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="project_init"
width="2302"
height="802"
src="https://minortex.github.io/posts/vscode_cpp_environment/project_init_hu_c8dfe5d5ff95c4e5.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/project_init_hu_c8dfe5d5ff95c4e5.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/project_init_hu_2fd38584822b5986.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/project_init.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;接着我们给&lt;code&gt;CMake&lt;/code&gt;生成一些预设。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="quick_start"
width="1268"
height="657"
src="https://minortex.github.io/posts/vscode_cpp_environment/quick_start_hu_61d3a0dfcf6a4a70.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/quick_start_hu_61d3a0dfcf6a4a70.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/quick_start.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/quick_start.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;快速入门 - 添加新预设 - 从编译器创建&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="create_from_compiler"
width="1239"
height="357"
src="https://minortex.github.io/posts/vscode_cpp_environment/create_from_compiler_hu_d312a6337f00368.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/create_from_compiler_hu_d312a6337f00368.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/create_from_compiler.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/create_from_compiler.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;你如果只装了&lt;code&gt;Clang&lt;/code&gt;，那么用不带&lt;code&gt;cl&lt;/code&gt;的就行，同样也可以用&lt;code&gt;GCC&lt;/code&gt;，看个人喜好。&lt;/p&gt;
&lt;p&gt;输入名字，这里我是用&lt;code&gt;平台-编译器-构建器-版本&lt;/code&gt;来命名的，回车会自动生成一个&lt;code&gt;CMakePresets.json&lt;/code&gt;，像这样：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="preset"
width="2102"
height="867"
src="https://minortex.github.io/posts/vscode_cpp_environment/preset_hu_f84a5090f586bc06.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/preset_hu_f84a5090f586bc06.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/preset_hu_ecc951f003d26168.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/preset.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;因为我比较喜欢&lt;code&gt;Ninja&lt;/code&gt;，所以加入这一行。如果你想用&lt;code&gt;Make&lt;/code&gt;，那么记得手动安装。&lt;/p&gt;
&lt;p&gt;然后在这里添加新生成预设，从配置预设中构建，名字可以是构建预设后面加上&lt;code&gt;-build&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="build_preset"
width="2854"
height="951"
src="https://minortex.github.io/posts/vscode_cpp_environment/build_preset_hu_b3c4efb607ab19b1.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/build_preset_hu_b3c4efb607ab19b1.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/build_preset_hu_a0b97c7d23bca8f3.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/build_preset.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;然后在这里选一下就好了：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="choose_build_preset"
width="657"
height="462"
src="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_preset.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_preset.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/choose_build_preset.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_preset.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;下面的生成目标可以选择你的文件夹名称了：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="choose_build_target"
width="2134"
height="400"
src="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_target_hu_5f3698d55187ed15.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_target_hu_5f3698d55187ed15.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/choose_build_target_hu_f3a6d29c2dc2e179.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/choose_build_target.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;打开你的&lt;code&gt;main.cpp&lt;/code&gt;，看看&lt;code&gt;clangd&lt;/code&gt;怎么样？原来已经成功加载数据库并运行了：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="clangd_log"
width="2982"
height="1680"
src="https://minortex.github.io/posts/vscode_cpp_environment/clangd_log_hu_7bf6f851b2a929ef.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/clangd_log_hu_7bf6f851b2a929ef.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/clangd_log_hu_d312478f90a3e34f.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/clangd_log.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;随便写点东西，然后点击左下角的生成，启动，你的代码应该可以运行了。&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="build_and_run"
width="2977"
height="1673"
src="https://minortex.github.io/posts/vscode_cpp_environment/build_and_run_hu_4ec44f1957a5f00b.webp"
srcset="https://minortex.github.io/posts/vscode_cpp_environment/build_and_run_hu_4ec44f1957a5f00b.webp 800w, https://minortex.github.io/posts/vscode_cpp_environment/build_and_run_hu_54fecd7a1381c0e8.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/vscode_cpp_environment/build_and_run.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;配置 task
&lt;div id="配置-task" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%85%8d%e7%bd%ae-task" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;有人说，哎，我的&lt;key&gt;F5&lt;/key&gt;一键调试呢？&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;.vscode/&lt;/code&gt;下创建一个&lt;code&gt;launch.json&lt;/code&gt;，写上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lldb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;request&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Debug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;program&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${command:cmake.launchTargetPath}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;console&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;integratedTerminal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${workspaceFolder}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;大功告成！&lt;/p&gt;
&lt;h2 class="relative group"&gt;后续
&lt;div id="后续" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%90%8e%e7%bb%ad" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;格式化
&lt;div id="格式化" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a0%bc%e5%bc%8f%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;你可能对于&lt;code&gt;Clangd&lt;/code&gt;的格式化不太满意，那么可以在项目根目录创建一个&lt;code&gt;.clang-format&lt;/code&gt;的文件，里面写上你想要的风格，具体可以去搜一下。&lt;/p&gt;
&lt;p&gt;这是我的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# .clang-format&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 使用 LLVM 风格作为基础，这是很常见的风格&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;BasedOnStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;LLVM&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 强制缩进宽度为 4 个空格&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;IndentWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 强制指针和引用靠左&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;PointerAlignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Left&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 强制使用空格进行缩进，而不是制表符&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;UseTab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Never&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;其他
&lt;div id="其他" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b6%e4%bb%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;如果想添加头文件，直接往&lt;code&gt;include/&amp;lt;你的项目名字&amp;gt;&lt;/code&gt;放置即可。&lt;/p&gt;
&lt;p&gt;如果觉得项目不错，想保留代码，直接把&lt;code&gt;current&lt;/code&gt;文件夹复制一份，改个名，运行的时候到&lt;code&gt;CMake&lt;/code&gt;里面切换配置/生成目标就好。&lt;/p&gt;
&lt;p&gt;如果你想用&lt;code&gt;MSVC&lt;/code&gt;，那么可以下&lt;code&gt;VS Installer&lt;/code&gt;，里面只选构建工具，不过注意这样是没法生成&lt;code&gt;compile_commands.json&lt;/code&gt;给&lt;code&gt;Clangd&lt;/code&gt;读取的。&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：类、构造函数与成员初始化</title><link>https://minortex.github.io/posts/modern-cpp-5-class-ctor-init/</link><pubDate>Mon, 29 Sep 2025 08:25:14 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-5-class-ctor-init/</guid><description>
&lt;h2 class="relative group"&gt;枚举
&lt;div id="枚举" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9e%9a%e4%b8%be" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;枚举实现了内容与整形相关联的数据结构，是隐式的编译时常量。&lt;/p&gt;
&lt;p&gt;枚举类型，枚举符和枚举变量。&lt;/p&gt;
&lt;p&gt;枚举符会放入当前命名空间和其本身的命名空间中，如果是全局的枚举 (unscoped),那么可以直接使用。&lt;/p&gt;
&lt;p&gt;对枚举变量空初始化会使得枚举符是 0，所以推荐把第一个枚举符设置成未知/无效。&lt;/p&gt;
&lt;p&gt;枚举类用&lt;code&gt;enum class Name{};&lt;/code&gt;定义，只能在其本身的作用域使用，无法被隐式转换成整数，在安全性上更优。&lt;/p&gt;
&lt;p&gt;可以在需要的使用&lt;code&gt;using enum Name;&lt;/code&gt;来简化使用 (C++20)。&lt;/p&gt;
&lt;h2 class="relative group"&gt;结构体
&lt;div id="结构体" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%93%e6%9e%84%e4%bd%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;对其中某些参数进行初始化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;wage&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Worker&lt;/span&gt; &lt;span class="n"&gt;John&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},.&lt;/span&gt;&lt;span class="n"&gt;wage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt; &lt;span class="c1"&gt;//部分列表初始化，age 初始化为 0，clangd 有 bug 会警告
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Worker&lt;/span&gt; &lt;span class="n"&gt;John&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="n"&gt;John&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt; &lt;span class="c1"&gt;//这样可以占位，一样使得 age 初始化为 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;结构体模板
&lt;div id="结构体模板" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%93%e6%9e%84%e4%bd%93%e6%a8%a1%e6%9d%bf" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;结构体当然也可以传入模板参数，不过在 C++20 更加完善，如果使用 C++17，可能需要手动指定推断：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Here&amp;#39;s a deduction guide for our Pair (needed in C++17 only)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Pair objects initialized with arguments of type T and T should deduce to Pair&amp;lt;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// explicitly specify class template Pair&amp;lt;int&amp;gt; (C++11 onward)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Pair&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// CTAD used to deduce Pair&amp;lt;int&amp;gt; from the initializers (C++17)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以为类型模板起别名：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Coord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Coord is an alias for Pair&amp;lt;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 class="relative group"&gt;类
&lt;div id="类" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b1%bb" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;类的成员变量，成员函数是隐式内联的，所以放在头文件中被多个源文件引用的时候，不违反 ODR 原则。&lt;/p&gt;
&lt;p&gt;在类中，不强制声明语句位于使用之前，只要他们都在同一个类中即可。&lt;/p&gt;
&lt;p&gt;调用常量类对象内部的函数，要将此成员函数声明为&lt;code&gt;const&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;struct&lt;/code&gt;和&lt;code&gt;class&lt;/code&gt;仅有的差异：&lt;code&gt;struct&lt;/code&gt;默认使用&lt;code&gt;public:&lt;/code&gt;，而&lt;code&gt;class&lt;/code&gt;默认使用&lt;code&gt;private:&lt;/code&gt;。在继承性上也是这样。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;protect:&lt;/code&gt;允许派生的类访问，其可见性介于两者之间。&lt;/p&gt;
&lt;h3 class="relative group"&gt;推荐实践
&lt;div id="推荐实践" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8e%a8%e8%8d%90%e5%ae%9e%e8%b7%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;不要让成员函数返回引用，否则将会使得成员变量被以外修改。&lt;/li&gt;
&lt;li&gt;不推荐使用成员函数，为了使类的实现更加简洁。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 class="relative group"&gt;成员初始化列表
&lt;div id="成员初始化列表" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%88%90%e5%91%98%e5%88%9d%e5%a7%8b%e5%8c%96%e5%88%97%e8%a1%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;现代 C++ 使用成员初始化列表，而不是直接初始化列表。具体是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_a&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_b&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_a&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;m_b&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//other...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以前 (C++03) 之前则是用的&lt;code&gt;()&lt;/code&gt;直接初始化。&lt;/p&gt;
&lt;p&gt;成员初始化的顺序和成员初始化列表无关，只跟泪中定义成员变量的顺序有关。&lt;/p&gt;
&lt;h3 class="relative group"&gt;&lt;code&gt;std::string_view&lt;/code&gt;特殊处理
&lt;div id="stdstring_view特殊处理" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stdstring_view%e7%89%b9%e6%ae%8a%e5%a4%84%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;一个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string_view&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ball&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;m_color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;none&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;m_radius&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Ball&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_radius&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;getColor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m_color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;getRadius&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m_radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Ball&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Ball(&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getColor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;, &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getRadius&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;blue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;red&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;12.0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;为什么这里构造函数需要用&lt;code&gt;string_view&lt;/code&gt;作为参数？不能用&lt;code&gt;const string&amp;amp;&lt;/code&gt;来替换？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;string_view&lt;/code&gt;提供了灵活性，如果选择后者，那么如果传入一个 C 语言的字面量字符串的时候，就必须在堆上构建临时的&lt;code&gt;string&lt;/code&gt;对象，而这对性能开销很大；对于前者，永远不会构造临时对象，而是只有一个指针，在真正初始化的时候完成一次复制。(甚至后面还能用移动语义)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么&lt;code&gt;getter&lt;/code&gt;不能像上面一样反过来？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为在类内部的存储对象就是真正的&lt;code&gt;string&lt;/code&gt;，给出它的引用使得外部访问的类型明确，不会造成歧义；同时也提示这个引用的生命周期是明确的————跟成员变量一致。&lt;/p&gt;
&lt;h3 class="relative group"&gt;构造函数
&lt;div id="构造函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;构造函数用于初始化类，在此我们可以手动指定成员变量如何初始化。&lt;/p&gt;
&lt;h4 class="relative group"&gt;转化构造函数
&lt;div id="转化构造函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bd%ac%e5%8c%96%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;转化构造函数其实就是特殊的构造函数，其只拥有一个参数。如果传入一个该参数类型的变量，那么就会自动把这这个变量转化成类。&lt;/p&gt;
&lt;h4 class="relative group"&gt;默认构造函数
&lt;div id="默认构造函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%bb%98%e8%ae%a4%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_b&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//可以显式的声明构造函数为默认构造函数，此时 m_a 会被默认初始化为 0。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;//用户自己初始化，但是空初始化，此时 m_a 不会被初始化，是垃圾值。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 class="relative group"&gt;委托构造函数
&lt;div id="委托构造函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a7%94%e6%89%98%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;在一个构造函数的&lt;code&gt;:&lt;/code&gt;后面写上参数更多的重载构造函数，可以用另一个构造函数来初始化，但是此构造函数就不能再初始化成员变量。&lt;/p&gt;
&lt;p&gt;尽量不要使用太多的构造函数。&lt;/p&gt;
&lt;h4 class="relative group"&gt;复制构造函数
&lt;div id="复制构造函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a4%8d%e5%88%b6%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;复制构造函数的参数必须是引用，要不然就会发生无限递归的调用复制函数。&lt;/p&gt;
&lt;p&gt;复制构造函数不应用于复制以外的意图，因为编译器可能会发生复制省略 (copy elision)&lt;/p&gt;
&lt;h3 class="relative group"&gt;explicit 关键字
&lt;div id="explicit-关键字" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#explicit-%e5%85%b3%e9%94%ae%e5%ad%97" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;禁止隐式转换：从单个参数隐式转换成类对象；从列表&lt;code&gt;{xxx,xxx}&lt;/code&gt;转换成类对象。&lt;/p&gt;
&lt;p&gt;在具有单个参数的构造函数前加入，避免编译器执行隐式转换。&lt;/p&gt;
&lt;p&gt;由于 C++ 对于用户定义的转换，只允许转换一次，所以下面的代码会报错：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string_view&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printEmployee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// has an Employee parameter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 此时要经历两次转换：C-Style string -&amp;gt; string_view -&amp;gt; class Employee
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printEmployee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Joe&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// compile error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 修正：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 方法 1:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// using std::literals;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// printEmployee(&amp;#34;Joe&amp;#34;sv);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 方法 2:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// printEmployee(Employee{&amp;#34;Joe&amp;#34;});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不对复制/移动构造函数使用&lt;code&gt;explicit&lt;/code&gt;，因为他们不执行隐式转换。&lt;/p&gt;
&lt;p&gt;如果转换的时候两者等效且零开销，可以不使用&lt;code&gt;explicit&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;const char*&lt;/code&gt; -&amp;gt; &lt;code&gt;string_view&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string&lt;/code&gt; -&amp;gt; &lt;code&gt;string_view&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 class="relative group"&gt;constexpr 问题
&lt;div id="constexpr-问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#constexpr-%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;从 C++14 开始，&lt;code&gt;constexpr&lt;/code&gt;修饰函数仅仅是作为编译时求值的提示，如果传入的变量不是一个&lt;code&gt;constexpr&lt;/code&gt;，那么这个函数就具有运行时的上下文，&lt;code&gt;constexpr&lt;/code&gt;修饰就不起作用。&lt;/p&gt;
&lt;p&gt;对于&lt;code&gt;struct&lt;/code&gt;，其作为一个聚合体，默认的构造函数无须加入&lt;code&gt;constexpr&lt;/code&gt;就可以用它初始化一个类对象。但是对于&lt;code&gt;class&lt;/code&gt;，就必须&lt;code&gt;public&lt;/code&gt;，同时手动指定构造函数是&lt;code&gt;constexpr&lt;/code&gt;，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而&lt;code&gt;constexpr&lt;/code&gt;修饰一个类对象的时候，如果涉及到的函数（构造函数、&lt;code&gt;setter&lt;/code&gt;以及使用到的成员函数）都有&lt;code&gt;constexpr&lt;/code&gt;修饰，表示这个类具有编译时的上下文，会让此对象成为一个编译时常量。在编译时如果此对象是用临时对象初始化的，对临时对象是可以修改的，但是一旦这个对象初始化完毕，那么就不再可以修改。&lt;/p&gt;
&lt;p&gt;后续要访问此&lt;code&gt;constexpr&lt;/code&gt;对象，那么所有的函数&lt;code&gt;()&lt;/code&gt;后，都必须有&lt;code&gt;const&lt;/code&gt;，保证不修改此对象。&lt;/p&gt;
&lt;p&gt;参考 (learncpp - 14.17)[https://www.learncpp.com/cpp-tutorial/constexpr-aggregates-and-classes/]&lt;/p&gt;
&lt;h3 class="relative group"&gt;this 指针
&lt;div id="this-指针" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#this-%e6%8c%87%e9%92%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;this&lt;/code&gt;指针是一个&lt;code&gt;const&lt;/code&gt;指针（顶层），指向当前操作的类。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;this&lt;/code&gt;指针出现比引用早，不然它多少是个引用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;成员函数类外定义
&lt;div id="成员函数类外定义" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%88%90%e5%91%98%e5%87%bd%e6%95%b0%e7%b1%bb%e5%a4%96%e5%ae%9a%e4%b9%89" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;成员函数可以在类外定义，要加上域访问解析符&lt;code&gt;::&lt;/code&gt;。如果是类的正下方（&lt;code&gt;.h&lt;/code&gt;中），前面加&lt;code&gt;inline&lt;/code&gt;关键字以防止重复包含；如果是对应的 cpp 中，无须加&lt;code&gt;inline&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;默认参数在声明时给出。&lt;/p&gt;
&lt;h3 class="relative group"&gt;类型模板参数
&lt;div id="类型模板参数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b1%bb%e5%9e%8b%e6%a8%a1%e6%9d%bf%e5%8f%82%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;可以在类指定类型模板参数，不过如果成员函数先声明，然后在类外定义，那么需要单独再指定一次模板参数。&lt;/p&gt;
&lt;p&gt;模板类外的成员函数要紧挨着类定义的下面。&lt;/p&gt;
&lt;p&gt;注意：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;所有的模板函数都是默认内联的，所以就算在类外定义，已经隐式&lt;code&gt;inline&lt;/code&gt;了。&lt;/li&gt;
&lt;li&gt;类外定义的函数的类型模板参数必须与类的一致。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;传入类模板的参数，无须加&lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt;，因为已经在&lt;code&gt;Pair&amp;lt;T&amp;gt;::&lt;/code&gt;作用域中了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;isEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// note the parameter has type Pair, not Pair&amp;lt;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m_first&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m_first&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;m_second&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m_second&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;静态成员变量
&lt;div id="静态成员变量" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%9d%99%e6%80%81%e6%88%90%e5%91%98%e5%8f%98%e9%87%8f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;静态成员变量在所有实例化的对象都可用，具有相同的值，在未实例化的时候也可以使用，直接用类名和域访问解析符访问。&lt;/p&gt;
&lt;p&gt;声明的时候，在类内加&lt;code&gt;static&lt;/code&gt;关键字，类外定义不能加关键字。&lt;/p&gt;
&lt;p&gt;位置：直接在类后面/类对应的&lt;code&gt;cpp&lt;/code&gt;，头文件中可以加&lt;code&gt;inline&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;只有静态成员变量可以自动推断，普通的不允许。&lt;/p&gt;
&lt;p&gt;用途：一个根据数量递增的 ID&lt;/p&gt;
&lt;h3 class="relative group"&gt;静态成员函数
&lt;div id="静态成员函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%9d%99%e6%80%81%e6%88%90%e5%91%98%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;静态成员函数用于访问静态全局变量。&lt;/p&gt;
&lt;p&gt;有替代品：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命名空间：没有访问控制&lt;/li&gt;
&lt;li&gt;静态全局类对象&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;友元
&lt;div id="友元" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%8b%e5%85%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在被访问的类中声明，从而使得外部的类/函数能够访问&lt;code&gt;private&lt;/code&gt;和&lt;code&gt;protected&lt;/code&gt;的对象。&lt;/p&gt;
&lt;h4 class="relative group"&gt;友元函数
&lt;div id="友元函数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%8b%e5%85%83%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;在类中声明，自动成为非成员的函数，类外定义。&lt;/p&gt;
&lt;p&gt;此项特性对于运算符重载非常有用。&lt;/p&gt;
&lt;h4 class="relative group"&gt;友元类
&lt;div id="友元类" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%8b%e5%85%83%e7%b1%bb" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;直接在类内定义。&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：引用、右值与函数模板</title><link>https://minortex.github.io/posts/modern-cpp-4-reference-rvalue-templates/</link><pubDate>Thu, 25 Sep 2025 16:18:15 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-4-reference-rvalue-templates/</guid><description>
&lt;h2 class="relative group"&gt;函数默认参数
&lt;div id="函数默认参数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%87%bd%e6%95%b0%e9%bb%98%e8%ae%a4%e5%8f%82%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;声明的时候带默认参数，定义的时候可以不带。但是不能定义的时候有默认参数而声明的时候没有。&lt;/li&gt;
&lt;li&gt;如果用默认参数，那么必须从右到左都有（左边可以没有，不能中断）。&lt;/li&gt;
&lt;li&gt;传入参数的时候，只能从左往右传入，未传入的自动使用默认的参数。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;模板
&lt;div id="模板" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a8%a1%e6%9d%bf" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//your function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果还有一个同名的非模板函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;调用的时候：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果使用&lt;code&gt;foo()&lt;/code&gt;，那么会直接调用非模板的函数。&lt;/li&gt;
&lt;li&gt;如果用&lt;code&gt;foo&amp;lt;&amp;gt;()&lt;/code&gt;，那么会自动推断，并从模板实例化函数。如果指定类型，则直接从模板按指定的类型实例化函数。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在两个不同的模板参数时，可以定义模板返回用&lt;code&gt;auto&lt;/code&gt;关键字，让编译器自动推导返回类型。这是 C++17 后的 CTAD(class template auto deduction)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：
CTAD 仅用于推断实参 (argument，调用时)
不能推断形参 (parameter，函数声明)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 C++20 后，函数参数传入&lt;code&gt;auto&lt;/code&gt;会自动变成模板。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;非类型模板：定义&lt;code&gt;constexpr&lt;/code&gt;或者&lt;code&gt;consteval&lt;/code&gt;（C++20）,不写 &lt;code&gt;typename&lt;/code&gt; 。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// print 5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于模板展开需要看到定义，所以模板函数应该放在头中。模板不违反单一定义原则。&lt;/p&gt;
&lt;h2 class="relative group"&gt;左值和右值
&lt;div id="左值和右值" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b7%a6%e5%80%bc%e5%92%8c%e5%8f%b3%e5%80%bc" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;左值表达式：在表达式结束之后，还能用标识对象访问的表达式。&lt;/li&gt;
&lt;li&gt;右值表达式：表达式结束之后立即销毁的临时量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特别：&lt;code&gt;++x&lt;/code&gt;是左值，&lt;code&gt;x++&lt;/code&gt;是右值。&lt;/p&gt;
&lt;h2 class="relative group"&gt;引用
&lt;div id="引用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%95%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;左值引用
&lt;div id="左值引用" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b7%a6%e5%80%bc%e5%bc%95%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;左值引用只能绑定到可修改的左值。&lt;/p&gt;
&lt;p&gt;常量左值引用可以绑定到可修改/不可修改的左值，右值。&lt;/p&gt;
&lt;p&gt;一个常量引用可以引用非常量的对应对象，这使得此引用由编译器保证不修改其引用的对象，而且是函数的只读参数的推荐做法。&lt;/p&gt;
&lt;p&gt;如果类型不匹配，引用会尝试将其进行隐式类型转换，得到一个临时对象。需要注意：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只允许数值提升和非窄化转换。&lt;/li&gt;
&lt;li&gt;此时转化后的临时对象会被引用持有，生命周期与引用本身相同。&lt;/li&gt;
&lt;li&gt;修改原始对象不会对因引用产生的临时对象造成影响。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;小心！引用无法延长函数返回值的声明周期，以下例子为 UB：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getTempVar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;引用和指针
&lt;div id="引用和指针" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%95%e7%94%a8%e5%92%8c%e6%8c%87%e9%92%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;左值引用可以绑定到右值，但是必须是常量的引用才可以。同时，对于右值的常量引用会使得直接绑定的临时对象生命周期被延长。&lt;/p&gt;
&lt;p&gt;而右值引用 (&amp;amp;&amp;amp;) 则是用于移动语义。&lt;/p&gt;
&lt;h3 class="relative group"&gt;与 auto 相结合
&lt;div id="与-auto-相结合" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%8e-auto-%e7%9b%b8%e7%bb%93%e5%90%88" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;顶级&lt;code&gt;const&lt;/code&gt;: 指的是修饰此别名/对象的&lt;code&gt;const&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;低级&lt;code&gt;const&lt;/code&gt;: 指的是被引用绑定/被指针指向的&lt;code&gt;const&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;指针可以同时拥有两种&lt;code&gt;const&lt;/code&gt;，但是引用不行，因为引用不是对象。&lt;/p&gt;
&lt;p&gt;当用&lt;code&gt;auto&lt;/code&gt;关键字以及引用来初始化对象时，遵守：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引用会被删除。&lt;/li&gt;
&lt;li&gt;顶层的常量会被删除。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然可以用&lt;code&gt;auto&amp;amp;&lt;/code&gt;来重新添加引用，不过这样会同时保留顶级的&lt;code&gt;const&lt;/code&gt;。我们一般推荐要手动加上&lt;code&gt;const&lt;/code&gt;，这样会让语义更加清晰。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;constexpr&lt;/code&gt;不是表达式的一部分，所以&lt;code&gt;auto&lt;/code&gt;不会自动推断出它，如果需要则要在 auto 前面手动加上，就像这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;getConstRef&lt;/span&gt;&lt;span class="p"&gt;()};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;但是推断指针的时候，&lt;code&gt;auto&lt;/code&gt;会完整的保留指针的类型。&lt;/p&gt;
&lt;p&gt;有一点比较特殊，使用&lt;code&gt;const auto&lt;/code&gt;和&lt;code&gt;auto const&lt;/code&gt;是同义的，都会使得新对象成为一个常量的指针。但是如果你想&lt;code&gt;const auto const&lt;/code&gt;，那就会报错。&lt;/p&gt;
&lt;p&gt;如果想要这么做，最好明确一点，就像这样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;const auto*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto* const&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;const auto* const&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样就很明显的看出，在*左边的意义是指向常量，在右边的意义是常量的指针，事实上既然都&lt;code&gt;auto&lt;/code&gt;了，也不差这几个关键字吧？我其实比较担心&lt;code&gt;auto&lt;/code&gt;不够智能这个问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为指针是一个对象，而引用可以理解成一个别名，所以当我们对引用进行推断的时候，实际上是推断原始的对象究竟是什么类型。&lt;/p&gt;
&lt;h3 class="relative group"&gt;in/out 参数
&lt;div id="inout-参数" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#inout-%e5%8f%82%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;为了避免拷贝，可以预先初始化一个对象传入其引用，这样可以避免拷贝，不过使得参数列表很乱。如果可行，还是通过返回值优化或者移动语义。&lt;/p&gt;</description></item><item><title>RouterOS 的 IPv6 多网段统一进行 NAPT 转换</title><link>https://minortex.github.io/posts/routeros_ipv6/</link><pubDate>Tue, 23 Sep 2025 21:05:32 +0800</pubDate><guid>https://minortex.github.io/posts/routeros_ipv6/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;终于决定给社团的网络修一修了~&lt;/p&gt;
&lt;h2 class="relative group"&gt;实践
&lt;div id="实践" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%9e%e8%b7%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;内网划分
&lt;div id="内网划分" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%86%85%e7%bd%91%e5%88%92%e5%88%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;对于内网的划分是这样的：&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;ULA&lt;/code&gt;子网：&lt;code&gt;fd11:4514:5a51::/48&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后划分四个网段，分别给设施、1-3 楼用户。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fd11:4514:5a51:0::/64&lt;/code&gt;，尽量使用&lt;code&gt;EUI64&lt;/code&gt;分配地址，同时分配公网前缀用于&lt;code&gt;IPv6&lt;/code&gt;访问。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fd11:4514:5a51:[1-3]::/64&lt;/code&gt;，仅仅分配&lt;code&gt;ULA&lt;/code&gt;，不分配公网前缀，使得设备流量分流到三条移动宽带。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;/DNPT 规则
&lt;div id="dnpt-规则" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dnpt-%e8%a7%84%e5%88%99" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;相比之前使用的&lt;code&gt;fc00-fc03&lt;/code&gt;网段来说，可以少写很多规则，直接转换&lt;code&gt;/60&lt;/code&gt;的前缀即可。&lt;/p&gt;
&lt;p&gt;参考如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-1 Output
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mark-packet new-packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-1 &lt;span class="nv"&gt;passthrough&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address-list&lt;span class="o"&gt;=&lt;/span&gt;!ULAs per-connection-classifier&lt;span class="o"&gt;=&lt;/span&gt;src-address:3/0 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-2 Output
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mark-packet new-packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-2 &lt;span class="nv"&gt;passthrough&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address-list&lt;span class="o"&gt;=&lt;/span&gt;!ULAs per-connection-classifier&lt;span class="o"&gt;=&lt;/span&gt;src-address:3/1 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-3 Output
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mark-packet new-packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-3 &lt;span class="nv"&gt;passthrough&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address-list&lt;span class="o"&gt;=&lt;/span&gt;!ULAs per-connection-classifier&lt;span class="o"&gt;=&lt;/span&gt;src-address:3/2 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-1 Output NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postrouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SNPT src-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:6af0::/60 src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-1 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-2 Output NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postrouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SNPT src-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:1360::/60 src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-2 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-3 Output NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postrouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SNPT src-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:bc5:c950::/60 src-address&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 packet-mark&lt;span class="o"&gt;=&lt;/span&gt;PCC-3 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;9&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-1 Input NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dnpt src-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:6af0::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:6af0::/60 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-2 Input NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dnpt src-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:1360::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:ae0:1360::/60 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;11&lt;/span&gt; &lt;span class="p"&gt;;;;&lt;/span&gt; PCC-3 Input NPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prerouting &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dnpt src-prefix&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:bc5:c950::/60 dst-prefix&lt;span class="o"&gt;=&lt;/span&gt;fd11:4514:5a51::/60 dst-address&lt;span class="o"&gt;=&lt;/span&gt;2409:8a20:bc5:c950::/60 &lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no log-prefix&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;总共 9 条规则就可以解决，依次是出站 PCC 规则分流，出站规则，入站 DNPT 规则。&lt;/p&gt;
&lt;h2 class="relative group"&gt;问题
&lt;div id="问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;脚本自动更新问题
&lt;div id="脚本自动更新问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%84%9a%e6%9c%ac%e8%87%aa%e5%8a%a8%e6%9b%b4%e6%96%b0%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;我真的非常不愿意去看 RouterOS 的脚本，太乱了，沿用了之前的内容，只是修改了其中的网段。&lt;/p&gt;
&lt;h3 class="relative group"&gt;入站问题
&lt;div id="入站问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%a5%e7%ab%99%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;按照现有的规则，实际上是无法入站的，需要单独添加一条规则，而且似乎只对&lt;code&gt;EUI64&lt;/code&gt;的地址生效。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; 2 ;;; PCC-1 LAN_Device
chain=prerouting action=accept dst-address=2409:8a20:ae0:6af0::/64 log=no log-prefix=&amp;#34;&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其实，对于隐私地址，防火墙也非常难以匹配规则，因为后缀变，运营商的前缀也跟着变，一般解决方案都是&lt;code&gt;socat&lt;/code&gt;或者反向匹配 (目标地址写&lt;code&gt;::be24:11ff:fe82:8282/::ffff:ffff:ffff:ffff&lt;/code&gt;这样)。&lt;/p&gt;
&lt;h3 class="relative group"&gt;内网访问问题
&lt;div id="内网访问问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%86%85%e7%bd%91%e8%ae%bf%e9%97%ae%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;对于内网 v6 互访 (从 0 网段到 1) 是失败的，我怀疑是 PCC 规则误伤了，但是排查还是挺困难的，因为 RouterOS 的日志调试我根本不会用，不出日志，你说要是有个&lt;code&gt;tcpdump&lt;/code&gt;我就会了&amp;hellip;&lt;/p&gt;
&lt;h2 class="relative group"&gt;补充
&lt;div id="补充" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a1%a5%e5%85%85" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;关于 EUI64 和 RFC4291
&lt;div id="关于-eui64-和-rfc4291" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b3%e4%ba%8e-eui64-%e5%92%8c-rfc4291" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;NAPT&lt;/code&gt;，对于&lt;code&gt;EUI64&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2: ens192: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu &lt;span class="m"&gt;1500&lt;/span&gt; qdisc mq state UP group default qlen &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; link/ether 00:0c:29:65:43:21 brd ff:ff:ff:ff:ff:ff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; altname enp11s0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inet 192.168.114.39/24 brd 192.168.114.255 scope global dynamic ens192
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; valid_lft 13117sec preferred_lft 13117sec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inet6 fd11:4514:5a51:0:20c:29ff:fe65:4321/64 scope global dynamic mngtmpaddr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; valid_lft 6993sec preferred_lft 3393sec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inet6 2409:8a20:ae0:6af0:20c:29ff:fe65:4321/64 scope global dynamic mngtmpaddr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; valid_lft 7124sec preferred_lft 3524sec
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;开启了隐私拓展后：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4: enp0s20f0u3u4: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu &lt;span class="m"&gt;1500&lt;/span&gt; qdisc fq_codel state UP group default qlen &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; link/ether 01:e0:22:65:43:21 brd ff:ff:ff:ff:ff:ff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; altname enx00e04c6809ce
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inet 192.168.101.80/24 brd 192.168.101.255 scope global dynamic noprefixroute enp0s20f0u3u4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; valid_lft 8484sec preferred_lft 8484sec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; inet6 fd11:4514:5a51:1:ff9d:d0eb:c4f3:ad54/64 scope global dynamic noprefixroute
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; valid_lft 7117sec preferred_lft 3517sec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ curl 6.ipw.cn
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2409:8a20:ae0:1361:cfab:d0eb:c4f3:ad543
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 65-80 的这一段，若不是真实反应 MAC 地址，那么好像在&lt;code&gt;SNPT&lt;/code&gt;的时候不会为你保留。&lt;/p&gt;
&lt;p&gt;有人说，变化是因为要符合校验和，但是对于&lt;code&gt;EUI64&lt;/code&gt;地址好像并不需要啊，这是为什么呢？&lt;/p&gt;
&lt;p&gt;查了下，发现&lt;code&gt;EUI64&lt;/code&gt;并没有强制要求必须保留，所以为了效率和随机性，就找了一个能够唯一标识的地址使用了。&lt;/p&gt;
&lt;h2 class="relative group"&gt;总结
&lt;div id="总结" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%80%bb%e7%bb%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;感觉调 RouterOS 完全就是在猜啊，没有 wrt 类那种我理解的感觉，希望以后能找到个大佬浇浇我。&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：类型转换、auto 与标准 I/O</title><link>https://minortex.github.io/posts/modern-cpp-3-cast-auto-io/</link><pubDate>Tue, 23 Sep 2025 08:50:10 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-3-cast-auto-io/</guid><description>
&lt;h2 class="relative group"&gt;循环，条件与分支
&lt;div id="循环条件与分支" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%be%aa%e7%8e%af%e6%9d%a1%e4%bb%b6%e4%b8%8e%e5%88%86%e6%94%af" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这个东西老生常谈了，我本来没有看的欲望，但是有一道题目很有意思，在此记录：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从 1 开始，向上计数，将任何只能被 3 整除的数字替换为“fizz”一词，将任何只能被 5 整除的数字替换为“buzz”一词，将任何可被 3 和 5 整除的数字替换为“fizzbuzz”一词，如果还可被 7 整除，那么替换为“fizzbuzzpop”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先，介绍一个名词：分支预测。&lt;/p&gt;
&lt;p&gt;简单来说，现代 CPU 在进行多分支的判断（&lt;code&gt;if-else&lt;/code&gt;）的时候，其实会去猜程序下一步的路径，如果猜对了，那么效率会提升；如果猜错了，那么只能把流水线上面的内容清空，然后重新执行指令。&lt;/p&gt;
&lt;p&gt;所以无论如何，应该尽量避免写&lt;code&gt;if-else&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;题目给出的答案是这么写的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// h/t to reader Waldo for suggesting this quiz
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fizzbuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;printed&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fizz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;buzz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;pop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;printed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// end for loop
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fizzbuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;能运行吗？能，优雅吗？我不太满意。&lt;/p&gt;
&lt;p&gt;所以说，有没有一个办法，不同于这个思路，同时又能更计算机一点？&lt;/p&gt;
&lt;p&gt;评论区的思路：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fizzbuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fizz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;buzz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fizzbuzz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;pop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fizzpop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;buzzpop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fizzbuzzpop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fizzbuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个方法非常巧妙，把每一个分支映射到了一个二进制的向量空间上面，这将会极大的提高性能，因为对于 CPU 来说，移位操作是原生的。但是，带来的就是更多相似字符串的重复占用，不知道这是不是良好的实践呢？&lt;/p&gt;
&lt;p&gt;如果还想减少的话，还是用&lt;code&gt;if&lt;/code&gt;来去测试位，不过这样又引入了分支。&lt;/p&gt;
&lt;p&gt;其实这么用的地方还是很多的，POSIX 的权限就是个例子。但是，用的时候就是想不到啊！&lt;/p&gt;
&lt;h2 class="relative group"&gt;标准输出，标准错误和标准输入
&lt;div id="标准输出标准错误和标准输入" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a0%87%e5%87%86%e8%be%93%e5%87%ba%e6%a0%87%e5%87%86%e9%94%99%e8%af%af%e5%92%8c%e6%a0%87%e5%87%86%e8%be%93%e5%85%a5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这三者是这样的：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;文件描述符&lt;/th&gt;
&lt;th&gt;在 c++ 中的对应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;标准输入&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;std::cin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标准输出&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;std::cout&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标准错误&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;std::cerr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 class="relative group"&gt;类型转换
&lt;div id="类型转换" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b1%bb%e5%9e%8b%e8%bd%ac%e6%8d%a2" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;数值提升 (numberic promotion):
&lt;div id="数值提升-numberic-promotion" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%95%b0%e5%80%bc%e6%8f%90%e5%8d%87-numberic-promotion" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;同类型，把一个更短的类型转换成一个更长的类型，安全，不会造成精度丢失。&lt;/p&gt;
&lt;p&gt;只有两条路径：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;bool&lt;/code&gt;，有/无负号的短整形，字符型-&amp;gt;&lt;code&gt;int&lt;/code&gt;-&amp;gt;&lt;code&gt;unsigned int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;float&lt;/code&gt;-&amp;gt;&lt;code&gt;double&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意，从&lt;code&gt;int&lt;/code&gt;到&lt;code&gt;long&lt;/code&gt;不是提升，是转换。&lt;/p&gt;
&lt;h3 class="relative group"&gt;数值转换 (numberic conversion):
&lt;div id="数值转换-numberic-conversion" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%95%b0%e5%80%bc%e8%bd%ac%e6%8d%a2-numberic-conversion" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;基本类型的转换，包括：&lt;/p&gt;
&lt;p&gt;窄化 (narrowing conversion)：长的类型转化成一个更短的类型，在{}中不被允许。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但是&lt;code&gt;constexpr&lt;/code&gt;是允许的，因为是编译时求值，所以实际上是截断不是转化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 class="relative group"&gt;算术转换 (arithmetic conversion):
&lt;div id="算术转换-arithmetic-conversion" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ae%97%e6%9c%af%e8%bd%ac%e6%8d%a2-arithmetic-conversion" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;对基本算术操作符两端的不同类型进行转换，有些规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;整数转浮点。&lt;/li&gt;
&lt;li&gt;对于无符号/有符号：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;如果没超过有符号上限，那就转换成有符号。&lt;/li&gt;
&lt;li&gt;超过了，那就转换成无符号。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;static_cast&lt;/code&gt;在编译时检查（类型在编译时是确定的），&lt;code&gt;dynamic_cast&lt;/code&gt;在运行时检查。&lt;/p&gt;
&lt;p&gt;不建议使用 C-Style 的转换，因为他可能使用上面中的任意一个以及&lt;code&gt;const_cast&lt;/code&gt;。&lt;/p&gt;
&lt;h2 class="relative group"&gt;auto
&lt;div id="auto" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#auto" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;对于&lt;code&gt;auto&lt;/code&gt;来说，必须要先确定得到的结果是什么，才能做推断，不能对没有初始化值的变量做推断。&lt;/p&gt;
&lt;p&gt;在用&lt;code&gt;std::cin&lt;/code&gt;的时候，给的类型是&lt;code&gt;istream&lt;/code&gt;，所以自动推断会推出这个类型，肯定不是你想要的结果。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;auto&lt;/code&gt;不会保留&lt;code&gt;const&lt;/code&gt;的修饰，所以如果需要保留，用&lt;code&gt;const auto&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 class="relative group"&gt;auto 的尾随返回值
&lt;div id="auto-的尾随返回值" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#auto-%e7%9a%84%e5%b0%be%e9%9a%8f%e8%bf%94%e5%9b%9e%e5%80%bc" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这其实一点都不&lt;code&gt;auto&lt;/code&gt;，只是为了让函数名对齐，然后用&lt;code&gt;-&amp;gt;&lt;/code&gt;给出返回类型罢了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;printSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;generateSubstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>现代 C++ 笔记：string_view、static 与内部链接</title><link>https://minortex.github.io/posts/modern-cpp-2-stringview-static-linkage/</link><pubDate>Fri, 19 Sep 2025 19:13:57 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-2-stringview-static-linkage/</guid><description>
&lt;h2 class="relative group"&gt;符号变量
&lt;div id="符号变量" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ac%a6%e5%8f%b7%e5%8f%98%e9%87%8f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;magic number 这个词，我一直认为是大家约定好了的一组数，但是这里是不同的含义。&lt;/p&gt;
&lt;p&gt;它指的是直接放在函数里面，没有任何交代的数据，使得程序更加难读。&lt;/p&gt;
&lt;p&gt;所以做法一般是推荐用&lt;code&gt;const&lt;/code&gt;修饰，如果标准高的话，&lt;code&gt;constexpr&lt;/code&gt;，请。&lt;/p&gt;
&lt;h2 class="relative group"&gt;字符串
&lt;div id="字符串" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ad%97%e7%ac%a6%e4%b8%b2" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;从 c 开始那么久，都一直没认真了解过字符串，这会终于明白了&lt;code&gt;std::string&lt;/code&gt;的一些基本操作。&lt;/p&gt;
&lt;p&gt;众所周知，字符串里面不可避免包含空白字符，那么办法就是通过&lt;code&gt;std::getline()&lt;/code&gt;，但是如果在&lt;code&gt;std::cin&lt;/code&gt;之后使用，大概率这么传入&lt;code&gt;(std::cin, target)&lt;/code&gt;是无效的，因为&lt;code&gt;cin&lt;/code&gt;在读到空白字符之后会停止，并且把空白字符留在缓冲区，那么下一次读取的时候，直接就把一个&lt;code&gt;\n&lt;/code&gt;传入了，所以你的字符串啥都没有。&lt;/p&gt;
&lt;p&gt;问题解决是通过流操纵符&lt;code&gt;std::ws&lt;/code&gt;（这个需要每次 cin 的时候都提供），控制如果一开始就读取到空白字符，那么就忽略掉这个字符继续读取后面的东西。&lt;/p&gt;
&lt;p&gt;这时就很好的解决了读取到&lt;code&gt;\n&lt;/code&gt;的问题，除非读取到下一个&lt;code&gt;\n&lt;/code&gt;，那么&lt;code&gt;getline&lt;/code&gt;才停止读取，此时字符串也成功存入了这一行。&lt;/p&gt;
&lt;p&gt;怎么说呢，感觉比 c 先进一点点（？&lt;/p&gt;
&lt;h3 class="relative group"&gt;string
&lt;div id="string" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#string" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;因为&lt;code&gt;string&lt;/code&gt;本身就是一个类，其构造函数本身就是重载的，所以无论在初始化的时候传入&lt;code&gt;string&lt;/code&gt;,&lt;code&gt;C-Style string&lt;/code&gt;还是&lt;code&gt;string_view&lt;/code&gt;，都不会发生隐式转换。&lt;/p&gt;
&lt;p&gt;但是如果函数要求的只是一个&lt;code&gt;string&lt;/code&gt;参数，那么把别的类型传入，就会发生编译错误。原因是不允许发生&lt;code&gt;string_view&lt;/code&gt;到&lt;code&gt;string&lt;/code&gt;的窄化转换，性能问题。&lt;/p&gt;
&lt;h3 class="relative group"&gt;string_view
&lt;div id="string_view" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#string_view" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 用 string_view 指向 string 的话，不产生多余的拷贝，只是在初始化的时候拷贝进入 string
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;//此处产生拷贝
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;name_v&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// 不拷贝
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 实际上，是让 string_view 指向一个 c 风格的字符串，这个过程没有拷贝。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果常量能够在编译时确定，那么就可以应用&lt;code&gt;constexpr&lt;/code&gt;。很显然，前者是不可行的，后者可以。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ string_view 的行为与普通的引用和指针不一致。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当用&lt;code&gt;string&lt;/code&gt;来初始化&lt;code&gt;string_view&lt;/code&gt;，然后&lt;code&gt;string&lt;/code&gt;被修改之后，&lt;code&gt;string_view&lt;/code&gt;并不会更新&lt;code&gt;string&lt;/code&gt;的新长度。&lt;/p&gt;
&lt;p&gt;如果要避免，得更新视图。（不知道智能指针是否有帮助？）&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;不建议用&lt;code&gt;string_view&lt;/code&gt;来返回，除非它是用 C 式的字符串初始化的。&lt;/p&gt;
&lt;p&gt;对于用&lt;code&gt;string_view&lt;/code&gt;来说，推荐用法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;aaa&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;//在堆上分配了 C-Style 字符串，但是用的构造函数不一致，在复制省略中，这是一种过编译的办法。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实现零成本抽象，原因嘛，以后再补充&amp;hellip;&lt;/p&gt;
&lt;h2 class="relative group"&gt;对于流程控制的一些建议
&lt;div id="对于流程控制的一些建议" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%af%b9%e4%ba%8e%e6%b5%81%e7%a8%8b%e6%8e%a7%e5%88%b6%e7%9a%84%e4%b8%80%e4%ba%9b%e5%bb%ba%e8%ae%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;对于只有两个分支的&lt;code&gt;if&lt;/code&gt;，可以考虑用三元运算符&lt;code&gt;? :&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果表达式本身只有为&lt;code&gt;0/1&lt;/code&gt;两种可能，那么就无须写&lt;code&gt;==&lt;/code&gt;，直接把表达式扔上去即可。&lt;/p&gt;
&lt;h2 class="relative group"&gt;static 与内部链接
&lt;div id="static-与内部链接" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#static-%e4%b8%8e%e5%86%85%e9%83%a8%e9%93%be%e6%8e%a5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;在变量前加入&lt;code&gt;static&lt;/code&gt;关键字，不仅仅会使变量在整个程序的生命周期有效，还会使得无法被外部文件访问此变量。&lt;/p&gt;
&lt;p&gt;现代 cpp 已经不推荐使用此关键字了。转而替代的是匿名命名空间，后者可以实现里面的变量直接在当前文件访问，其他文件不可访问。&lt;/p&gt;
&lt;p&gt;没加&lt;code&gt;static&lt;/code&gt;的变量默认是全局变量，在别的文件使用&lt;code&gt;extern&lt;/code&gt;引入，&lt;code&gt;const&lt;/code&gt;默认就是内部变量。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;static 的最佳用法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;static&lt;/code&gt;最适合用于函数内的计数场景，比如调用了函数几次，只需在函数内声明一个静态局部变量即可。&lt;/p&gt;
&lt;p&gt;不推荐使用此方法实现相同的输入不同的输出内容，这其实也不符合 fp 的原则。如果必须要这么用，那么最好的办法是将条件一起传入。&lt;a
href="https://www.learncpp.com/cpp-tutorial/static-local-variables/"
target="_blank"
&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;inline 关键字
&lt;div id="inline-关键字" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#inline-%e5%85%b3%e9%94%ae%e5%ad%97" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;从 cpp17 开始，完善了&lt;code&gt;inline&lt;/code&gt;，使得&lt;code&gt;constexpr&lt;/code&gt;对于编译时常量跨文件有了最佳解决方案。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;inline&lt;/code&gt;本义其实指的是把函数展开到调用的地方，但是修饰&lt;code&gt;constexpr&lt;/code&gt;时，它被赋予了不同的意义。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;inline&lt;/code&gt;修饰&lt;code&gt;constexpr&lt;/code&gt;特殊，当在多个文件中定义的时候，会告诉链接器从任意一份中取出一份定义，并且在所有文件中共享，同时并不违反 ODR 原则，这使得把&lt;code&gt;inline&lt;/code&gt;放在文件头里面，就可以向引入这个头文件的所有源文件进行分发。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;inline&lt;/code&gt;同时还可以修饰命名空间，修饰非匿名的命名空间可以使得本来需要域解析操作符的使用，直接被透传到全局的命名空间中去。&lt;/p&gt;
&lt;p&gt;下面是一个嵌套的命名空间的例子，使得接口能够安全的对当前文件中的其他函数暴露：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;V1&lt;/span&gt; &lt;span class="c1"&gt;// declare a normal namespace named V1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;V1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;V2&lt;/span&gt; &lt;span class="c1"&gt;// declare an inline namespace named V2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="c1"&gt;// unnamed namespace
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// has internal linkage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;V2&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;V1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// calls the V1 version of doSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;V2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// calls the V2 version of doSomething()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// calls the inline version of doSomething() (which is V2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;inline&lt;/code&gt;作用于&lt;code&gt;V2&lt;/code&gt;命名空间，使得内部内容能够无须域解析操作符，但是匿名命名空间保护性最强，使得内部内容只能有当前文件访问，无法暴露给外部文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In modern C++, the term inline has evolved to mean “multiple definitions are allowed”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;总结下，&lt;code&gt;inline&lt;/code&gt;除了内联的意义，其实还有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于变量：在头文件中定义，使得此头被多次引入的时候只使用共享的一次，避免每次都加载到文件，在头文件被多次引用的时候很有用 (c++17)。同时，如果变量同时被&lt;code&gt;static constexpr&lt;/code&gt;修饰，那么它是隐式&lt;code&gt;inline&lt;/code&gt;的。&lt;/li&gt;
&lt;li&gt;对于函数：在头文件中定义的函数需要加（但是不推荐，等到后面看到类的构造函数再会来补充），声明的函数无须加。&lt;/li&gt;
&lt;li&gt;对于命名空间：把某个命名空间中的内容暴露到全局的命名空间，使得其中内容访问无须加域解析操作符。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;using 语句和 using 声明
&lt;div id="using-语句和-using-声明" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-%e8%af%ad%e5%8f%a5%e5%92%8c-using-%e5%a3%b0%e6%98%8e" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;语句指的是用一个命名空间，这个一般范围过大，不推荐，此处狠狠批评那些教用&lt;code&gt;using namespace std;&lt;/code&gt;的人。&lt;/p&gt;
&lt;p&gt;using 声明是可以接受的，因为它比起别名更加清晰，而不是只让写代码的人看懂。&lt;/p&gt;</description></item><item><title>现代 C++ 笔记：初始化、字面量与编译期</title><link>https://minortex.github.io/posts/modern-cpp-1-init-literals-compiletime/</link><pubDate>Thu, 11 Sep 2025 08:10:38 +0800</pubDate><guid>https://minortex.github.io/posts/modern-cpp-1-init-literals-compiletime/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;最近找到个个还不错的&lt;a
href="https://learncpp.com"
target="_blank"
&gt;网站&lt;/a&gt;，适合我这种大白，遂开始学习，记录一些感受。&lt;/p&gt;
&lt;p&gt;此系列可能没有很好的排版，因为是给自己看的。&lt;/p&gt;
&lt;p&gt;不保证所有的内容一定正确，因为是学习过程中记录的，所以可能还会有完全不正确的理解。&lt;/p&gt;
&lt;h2 class="relative group"&gt;澄清一个我混淆的概念
&lt;div id="澄清一个我混淆的概念" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%be%84%e6%b8%85%e4%b8%80%e4%b8%aa%e6%88%91%e6%b7%b7%e6%b7%86%e7%9a%84%e6%a6%82%e5%bf%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;表达式：求值，调用函数，变量/常量和运算符相结合的东西&lt;/p&gt;
&lt;p&gt;语句：有分号的东西，可以由多个表达式复合而成。&lt;/p&gt;
&lt;p&gt;statement expression（原谅我不会翻译）：只含一个表达式的语句。&lt;/p&gt;
&lt;p&gt;标识符：用户自定义的名称，不以数字开头。&lt;/p&gt;
&lt;p&gt;关键字：系统保留字，用于流程控制。&lt;/p&gt;
&lt;h2 class="relative group"&gt;cin/cout探秘
&lt;div id="cincout探秘" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cincout%e6%8e%a2%e7%a7%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;我一直好奇，&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;是怎么实现的，还能同时接着，是怎么使用的。&lt;/p&gt;
&lt;p&gt;事实上，这运用的是运算符重载，语义分析会处理左右两边的表达式，然后决定是否重载它。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;是左结合的，所以优先处理左边的部分。&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;一看到左边是一个&lt;code&gt;ostream&lt;/code&gt;类型，右边是一个字符串，自动重载，通过运算返回一个&lt;code&gt;ostream&lt;/code&gt;，接着再处理次级优先的，依次拼接一个完整的字符串流。&lt;/p&gt;
&lt;p&gt;这里提到一个重要概念：副作用。&lt;/p&gt;
&lt;p&gt;正常来说，一个函数应该要返回值。当我们只使用一个&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;的时候，确实只用到了副作用，如果我们连在一起使用的时候，那么返回值就非常重要了。这是不是有点像 fp，给定输入得到输出，接着成为另一个输入？&lt;/p&gt;
&lt;h2 class="relative group"&gt;现代的初始化
&lt;div id="现代的初始化" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%8e%b0%e4%bb%a3%e7%9a%84%e5%88%9d%e5%a7%8b%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;现在 c++ 推荐使用列表直接初始化，避免隐式类型转换。&lt;/p&gt;
&lt;p&gt;如：&lt;code&gt;foo{}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;还有别的办法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接初始化：&lt;code&gt;foo(1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;间接列表初始化：&lt;code&gt;foo = {1}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠️不推荐在同一行定义多个变量，因为会在 git 中导致 diff 不清晰。&lt;/p&gt;
&lt;h2 class="relative group"&gt;literal/variable
&lt;div id="literalvariable" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#literalvariable" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;学过微机的人都知道，一个程序加载进内存，会分别放到代码段和数据段，接着获取到一些段（stack，pile）。&lt;/p&gt;
&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码段是只读的，同时和堆栈段中间还相隔了一些部分，这是避免数据溢出破坏程序的运行。&lt;/li&gt;
&lt;li&gt;对于字面量来说，使用它，就直接到数据段里面取，没有分配给他额外的空间。&lt;/li&gt;
&lt;li&gt;而对于变量来说，使用它，先得从数据段中拷贝一份到栈/堆（假设初始化的话），接着读取这个地址指向的内容，才能进行访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 class="relative group"&gt;compile-time / runtime
&lt;div id="compile-time--runtime" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#compile-time--runtime" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;什么是编译时和运行时的概念？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;编译时：编译器在进行语法检查的时候。&lt;/p&gt;
&lt;p&gt;运行时：程序真正运行的时候。&lt;/p&gt;
&lt;p&gt;所以说，我们有下面的内容：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;标识符和编译时相对应，对象和运行时相对应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;作用域（scope）和编译时对应，生命周期（lifetime）和运行时相对应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同时还有一个大杀器：&lt;code&gt;constexpr&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这个关键字的意思，指的是能够在编译时就确定常量（甚至函数），而不是运行时，从而把运行时开销转换成编译时间。&lt;/p&gt;
&lt;p&gt;现在，对于简单的函数，不含&lt;code&gt;new/delete&lt;/code&gt;，&lt;code&gt;cin/cout&lt;/code&gt;，随机数生成的函数，我们都推荐使用&lt;code&gt;constexpr&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;需要注意的是，如果一个常量是在块作用域中，而且初始化用的是常量表达式（&lt;code&gt;{}&lt;/code&gt; &lt;em&gt;零初始化&lt;/em&gt; 是常量表达式），那么它会自动成为常量表达式。&lt;/p&gt;
&lt;p&gt;需要注意的是，在 C++11~20 的不同版本中，支持的类型略有差异，比如后期才支持流程控制的 for/while/if。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;感觉 gemini 这么说不错：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;标识符是编译时的抽象，是对象在代码中的名字和类型。
对象是运行时的实体，是标识符所代表的内存和数据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 class="relative group"&gt;cmake 的一些使用方法（对于多文件/引入 libs）
&lt;div id="cmake-的一些使用方法对于多文件引入-libs" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cmake-%e7%9a%84%e4%b8%80%e4%ba%9b%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95%e5%af%b9%e4%ba%8e%e5%a4%9a%e6%96%87%e4%bb%b6%e5%bc%95%e5%85%a5-libs" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;加入这一行把需要的头包含进来，就可以直接在#include 里面写&lt;code&gt;.h&lt;/code&gt;的文件了，无须写相对路径。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmake" data-lang="cmake"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;target_include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="s"&gt;PRIVATE&lt;/span&gt; &lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/include/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 cmake 3.30 之后，引入 Boost 直接用&lt;code&gt;find_package(Boost)&lt;/code&gt;就好。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;Best Practise
&lt;div id="best-practise" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#best-practise" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;尽量在使用一个变量的正前面初始化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽量不要使用无符号整形，因为其具有的环绕特性会在一些行为（比如与有符号整数比较）上面让人难以调试。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>给老机 x200 刷开源 BIOS--Coreboot！</title><link>https://minortex.github.io/posts/upgrade_my_thinkpad_x200/</link><pubDate>Sat, 30 Aug 2025 12:33:31 +0800</pubDate><guid>https://minortex.github.io/posts/upgrade_my_thinkpad_x200/</guid><description>&lt;p&gt;这篇文本来是想写一下升级过程的，因为我在家里面翻出了这台 x200，也算是我早期用得比较久的本子了（B 站就是在这上面注册的）。&lt;/p&gt;
&lt;p&gt;出于情怀，给它换了无线网卡，换了风扇，内存 4G-&amp;gt;8G，换了原来坏了的键盘，总共没超过 200 元。&lt;/p&gt;
&lt;p&gt;不过暂时没带来学校，所以图片的话，以后再补充吧。&lt;/p&gt;
&lt;h2 class="relative group"&gt;刷入 Coreboot 注意事项
&lt;div id="刷入-coreboot-注意事项" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%b7%e5%85%a5-coreboot-%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这里记载了折腾过程中遇到的坑。&lt;/p&gt;
&lt;h3 class="relative group"&gt;请不要购买 CH341A！
&lt;div id="请不要购买-ch341a" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%af%b7%e4%b8%8d%e8%a6%81%e8%b4%ad%e4%b9%b0-ch341a" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;为什么我会在这里得出跟 libreboot 一样的结论呢？&lt;/p&gt;
&lt;p&gt;CH341A 无法设置 spispeed，而是默认 2M，这就造成一个问题，如果你的连接质量不好的话，这已经是一个相当高的速率了。可能会使得你根本没法看到你的芯片读取到。这里还是推荐买正版的 pico 吧，这玩意用的 buck-boost 电路，相比国产的 pico 来说，发热肯定会小不少，这可能对稳定刷写有一定的帮助。&lt;/p&gt;
&lt;h3 class="relative group"&gt;刷写
&lt;div id="刷写" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%b7%e5%86%99" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在&lt;a
href="https://libreboot.org/docs/install/spi.html"
target="_blank"
&gt;libreboot 的教程&lt;/a&gt;中，告诉你&lt;code&gt;1, 9&lt;/code&gt;脚分别是 WP/HOLD，据说主板自带上拉电阻，所以不用连接。&lt;/p&gt;
&lt;p&gt;经过测试，发现夹子连接正确的话，这两个脚会有 3.1v 左右的电压，稍微低了一点但是不影响。&lt;/p&gt;
&lt;p&gt;如果原始的固件你可能根本刷不进去，写入没一会就提示擦除失败，我试了连接这两个脚是无关事项，所以后来者可以不用连接。&lt;/p&gt;
&lt;p&gt;请看过程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ flashprog -p serprog:dev&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0,spispeed&lt;span class="o"&gt;=&lt;/span&gt;16M -c &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; -w x200_bios.rom
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Using clock_gettime &lt;span class="k"&gt;for&lt;/span&gt; delay loops &lt;span class="o"&gt;(&lt;/span&gt;clk_id: 1, resolution: 1ns&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;serprog: Programmer name is &lt;span class="s2"&gt;&amp;#34;pico-serprog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Found Macronix flash chip &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8192&lt;/span&gt; kB, SPI&lt;span class="o"&gt;)&lt;/span&gt; on serprog.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Reading old flash chip contents... &lt;span class="k"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Erasing and writing flash chip... FAILED at 0x00130000! &lt;span class="nv"&gt;Expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xff, &lt;span class="nv"&gt;Found&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x2f, failed byte count from 0x00130000-0x00137fff: 0x44aa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERASE FAILED!
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;FAILED!
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Uh oh. Erase/write failed. Checking &lt;span class="k"&gt;if&lt;/span&gt; anything has changed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Reading current flash chip contents... &lt;span class="k"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Apparently at least some data has changed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Your flash chip is in an unknown state.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那这个问题如何解决呢？答案是&lt;strong&gt;先擦除，再写入&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;像这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ flashprog -p serprog:dev&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0,spispeed&lt;span class="o"&gt;=&lt;/span&gt;16M -c &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; -E
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Using clock_gettime &lt;span class="k"&gt;for&lt;/span&gt; delay loops &lt;span class="o"&gt;(&lt;/span&gt;clk_id: 1, resolution: 1ns&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;serprog: Programmer name is &lt;span class="s2"&gt;&amp;#34;pico-serprog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Found Macronix flash chip &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8192&lt;/span&gt; kB, SPI&lt;span class="o"&gt;)&lt;/span&gt; on serprog.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Erasing flash chip... Erase &lt;span class="k"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后就可以完美写入了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ flashprog -p serprog:dev&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0,spispeed&lt;span class="o"&gt;=&lt;/span&gt;16M -c &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; -w x200_bios.rom
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Using clock_gettime &lt;span class="k"&gt;for&lt;/span&gt; delay loops &lt;span class="o"&gt;(&lt;/span&gt;clk_id: 1, resolution: 1ns&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;serprog: Programmer name is &lt;span class="s2"&gt;&amp;#34;pico-serprog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Found Macronix flash chip &lt;span class="s2"&gt;&amp;#34;MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E/MX25L6473F&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8192&lt;/span&gt; kB, SPI&lt;span class="o"&gt;)&lt;/span&gt; on serprog.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Reading old flash chip contents... &lt;span class="k"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Erasing and writing flash chip... Erase/write &lt;span class="k"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Verifying flash... VERIFIED.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;提醒一句，尽量不要接在 hub 或者台式机的前面板，很可能会供电不足导致写入之后验证失败。&lt;/p&gt;
&lt;p&gt;如果杜邦线的长度太长（最好 10cm），夹子的质量不好，可能会导致你根本无法用 16M 的 spispeed 写入，甚至根本没法读取到芯片，经过我测试，现在我手上的这套设备已经不太行了，只能在 64K 的速度稳定写入，这消耗了大概 50 分钟进行一轮读取 - 擦除/写入 - 读取，所以当你成功刷入 coreboot 后，最好保守一点，直接在目标机子上面刷吧。&lt;/p&gt;</description></item><item><title>Immortalwrt：IPv6、VLAN 与 NDP 代理</title><link>https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/</link><pubDate>Sat, 19 Jul 2025 14:15:52 +0800</pubDate><guid>https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;早年接触过 wrt，但是那时候用的还是 7621 的设备，使用 mtk 开源驱动后信号会变得非常差，闭源驱动会出现死机的情况。如今入手 7981 的路由器，终于可以研究一下配置以及日用了。&lt;/p&gt;
&lt;h2 class="relative group"&gt;IPv6 上网
&lt;div id="ipv6-上网" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ipv6-%e4%b8%8a%e7%bd%91" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;想让路由器下面的设备上网，无非就这么几种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;PD(Prefix Delegation): 上级下发 ipv6 的/56 或者/60 的前缀，可以自己划分子网使用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NDP Proxy: 上级通过 SLAAC 分配 ipv6 地址，只开启了 O 标志，获得一个/64 的地址的时候使用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NPTv6: 你有多条宽带，需要做负载均衡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NAPTv6: 上级只允许通过 DHCPv6 获取地址（一般是学校之类需要强管理或者认证的场景），获得一个/128 的地址。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;欸，这时候有人问，/128 不是也可以使用 ndp 代理吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;理论上确实可行，但是很多 DHCPv6 服务器并不支持代理获取，下面这个&lt;code&gt;tcpdump&lt;/code&gt;结果说明了这一点：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@ImmortalWrt:~# tcpdump -i eth1 udp port 546 or udp port 547
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:03:40.878466 IP6 fe80::aaaa.546 &amp;gt; ff02::1:2.547: dhcp6 solicit
15:03:40.880132 IP6 fe80::bbbb.547 &amp;gt; fe80::aaaa.546: dhcp6 advertise
15:03:42.905527 IP6 fe80::aaaa.546 &amp;gt; ff02::1:2.547: dhcp6 request
15:03:42.908198 IP6 fe80::bbbb.547 &amp;gt; fe80::aaaa.546: dhcp6 reply
15:04:39.149414 IP6 ImmortalWrt.lan.547 &amp;gt; ff05::1:3.547: dhcp6 relay-fwd
15:04:40.188299 IP6 ImmortalWrt.lan.547 &amp;gt; ff05::1:3.547: dhcp6 relay-fwd
15:04:42.189520 IP6 ImmortalWrt.lan.547 &amp;gt; ff05::1:3.547: dhcp6 relay-fwd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;使用 ff05 的广播根本就不回你。&lt;/p&gt;
&lt;h3 class="relative group"&gt;NDP 代理
&lt;div id="ndp-代理" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ndp-%e4%bb%a3%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;在路由器上面这样设置：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;wan6&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DHCP 服务器-IPv6 设置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指定的主接口（打勾）&lt;/li&gt;
&lt;li&gt;RA 服务：中继模式&lt;/li&gt;
&lt;li&gt;DHCPv6 服务：中继模式&lt;/li&gt;
&lt;li&gt;NDP 代理：中继模式&lt;/li&gt;
&lt;li&gt;学习路由（打勾）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;code&gt;br-lan&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DHCP 服务器-IPv6 设置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指定的主接口（不允许选择了）&lt;/li&gt;
&lt;li&gt;RA 服务：中继模式&lt;/li&gt;
&lt;li&gt;DHCPv6 服务：中继模式&lt;/li&gt;
&lt;li&gt;NDP 代理：中继模式&lt;/li&gt;
&lt;li&gt;学习路由（打勾）&lt;/li&gt;
&lt;li&gt;NDP 代理从属设备（打勾）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样操作相当于透传了上级路由的 RA（路由器通告），上面怎么样配置，你的局域网设备就是怎么样的，没法更改，当然如果你能获取到/64 的前缀，那么下面的设备一般来说也没有问题。&lt;/p&gt;
&lt;h3 class="relative group"&gt;前缀下发
&lt;div id="前缀下发" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e7%bc%80%e4%b8%8b%e5%8f%91" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这当然是最推荐的方式了。&lt;/p&gt;
&lt;p&gt;在现代的类 wrt 上面，使用&lt;code&gt;PPPoE&lt;/code&gt;在 wan 口上拨号，同时在高级设置里面开启获取 IPv6 地址为自动，如果你的 ISP 有 IPv6 支持，会自动产生一个虚拟动态接口&lt;code&gt;wan_6&lt;/code&gt;。一般这个接口能获取到一个/64 的地址和 IPv6-PD，这是我们能分配的前提条件。&lt;/p&gt;
&lt;p&gt;我的配置是这样的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RA 服务：服务器模式&lt;/li&gt;
&lt;li&gt;DHCPv6 服务：已禁用&lt;/li&gt;
&lt;li&gt;NDP 代理：已禁用&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;使用简单的路由通告配置
&lt;div id="使用简单的路由通告配置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%bd%bf%e7%94%a8%e7%ae%80%e5%8d%95%e7%9a%84%e8%b7%af%e7%94%b1%e9%80%9a%e5%91%8a%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;在家用的小型网络中，完全可以做到不架设 DHCPv6 服务器，只通过 RA 就可以实现基本的配置了，请看这个包：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@t-router:~# tcpdump -nvi br-lan ip6&lt;span class="o"&gt;[&lt;/span&gt;40&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;134&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tcpdump: listening on br-lan, link-type EN10MB &lt;span class="o"&gt;(&lt;/span&gt;Ethernet&lt;span class="o"&gt;)&lt;/span&gt;, snapshot length &lt;span class="m"&gt;262144&lt;/span&gt; bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;10:35:28.476086 IP6 &lt;span class="o"&gt;(&lt;/span&gt;flowlabel 0x4f20d, hlim 255, next-header ICMPv6 &lt;span class="o"&gt;(&lt;/span&gt;58&lt;span class="o"&gt;)&lt;/span&gt; payload length: 144&lt;span class="o"&gt;)&lt;/span&gt; fe80::aaaa &amp;gt; ff02::1: &lt;span class="o"&gt;[&lt;/span&gt;icmp6 sum ok&lt;span class="o"&gt;]&lt;/span&gt; ICMP6, router advertisement, length &lt;span class="m"&gt;144&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; hop limit 64, Flags &lt;span class="o"&gt;[&lt;/span&gt;none&lt;span class="o"&gt;]&lt;/span&gt;, pref medium, router lifetime 1800s, reachable &lt;span class="nb"&gt;time&lt;/span&gt; 0ms, retrans timer 0ms
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;source&lt;/span&gt; link-address option &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;: aa:aa:aa:aa:aa:aa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mtu option &lt;span class="o"&gt;(&lt;/span&gt;5&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="m"&gt;1492&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; prefix info option &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;32&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;: 2409:xxxx:xxxx:13a::/64, Flags &lt;span class="o"&gt;[&lt;/span&gt;onlink, auto&lt;span class="o"&gt;]&lt;/span&gt;, valid &lt;span class="nb"&gt;time&lt;/span&gt; 5400s, pref. &lt;span class="nb"&gt;time&lt;/span&gt; 2700s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; route info option &lt;span class="o"&gt;(&lt;/span&gt;24&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;: 2409:xxxx:xxxx:130::/60, &lt;span class="nv"&gt;pref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;medium, &lt;span class="nv"&gt;lifetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1800s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rdnss option &lt;span class="o"&gt;(&lt;/span&gt;25&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;: lifetime 1800s, addr: 2409:xxxx:xxxx:13a::1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dnssl option &lt;span class="o"&gt;(&lt;/span&gt;31&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;24&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;: lifetime 1800s, domain&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: lan.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; advertisement interval option &lt;span class="o"&gt;(&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt;, length &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;: 600000ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;几个重要的信息全部体现在这个里面了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;地址：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;没有配置 Flag，说明是 Stateless，设备自己生成配置，是否开启 RFC4941（隐私地址）由设备自己决定。&lt;/p&gt;
&lt;p&gt;不过无状态自动配置要求地址的前缀必须是/64 以上的，否则会破坏这一行为。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;网关：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;规定路由通告必须使用 fe80 开头的本地地址来发送路由通告，只要路由存活时间不为 0，那么下面的设备就可以使用这个地址当做默认路由。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;rdnss option&lt;/code&gt;来配置 DNS。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Windows 在大概 1709 这个版本之后支持&lt;code&gt;rdnss&lt;/code&gt;了，其他系统应该都支持吧？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;dnssl option&lt;/code&gt;来配置 DNS 搜索域。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个感觉意义不大，因为使用 DHCPv6 才会解析主机名.lan，这个使用 DHCPv4 已经足够了。&lt;/p&gt;
&lt;p&gt;不过注意不要设置成.local，因为 mDNS 会使用这个域名导致冲突，而 mDNS 会自动发现的。&lt;/p&gt;
&lt;p&gt;如果你有/60 的前缀，可以在高级设置里面填写 IPv6 分配提示，4 位正好可以填一位 16 进制数，&lt;code&gt;0-f&lt;/code&gt;，以此类推/56 可以填两位，从而划分多个 IPv6 子网。&lt;/p&gt;
&lt;p&gt;像我就是填了&lt;code&gt;a&lt;/code&gt;。&lt;/p&gt;
&lt;h4 class="relative group"&gt;使用 DHCPv6
&lt;div id="使用-dhcpv6" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%bd%bf%e7%94%a8-dhcpv6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;如果设备比较老，可能还是得开启 DHCPv6，办法就是把 DHCPv6 服务设置成服务器模式。&lt;/p&gt;
&lt;p&gt;这时候就可以在 RA 设置里面的 RA 标记开启几个选项，让设备使用 DHCPv6 服务，这里配置项目已经讲解得很明白，我就不多说了，直接贴上来：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RA 标记
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;受管配置 &lt;span class="o"&gt;(&lt;/span&gt;M&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;受管地址配置 &lt;span class="o"&gt;(&lt;/span&gt;M&lt;span class="o"&gt;)&lt;/span&gt; 标记表明可以通过 DHCPv6 获取 IPv6 地址。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;其他配置 &lt;span class="o"&gt;(&lt;/span&gt;O&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;其他配置 &lt;span class="o"&gt;(&lt;/span&gt;O&lt;span class="o"&gt;)&lt;/span&gt; 标记表明其他信息，如 DNS 服务器，可以通过 DHCPv6 获得。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;移动家乡代理 &lt;span class="o"&gt;(&lt;/span&gt;H&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;移动 IPv6 家乡代理 &lt;span class="o"&gt;(&lt;/span&gt;H&lt;span class="o"&gt;)&lt;/span&gt; 标记表明该设备在此链路上还充当移动 IPv6 家乡代理。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;DHCPv6 还可以配置别的一大堆东西，比如 NTP 之类的，但是 v4 已经干了这一堆了，我觉得除非 v4 被完全替代，否则现阶段完全没有理由用。&lt;/p&gt;
&lt;h3 class="relative group"&gt;NPTv6/NAPTv6
&lt;div id="nptv6naptv6" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nptv6naptv6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;如果有多条宽带并且都有 PD，你可以内网分配一个 ULA 地址，然后在路由的时候进行前缀转换，这也是我们学校社团网络的做法。这种办法对于性能要求的不高，本身也算符合 IPv6 的实践。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;但是如果你只能通过 DHCPv6 配到一个/128 的地址，那就没办法了，直接用动态地址伪装（masquerade）吧，一般的路由器都不会有硬件加速，所以网速不太行，但是如果是 IPv6 刚需也没有别的办法了。&lt;/p&gt;
&lt;p&gt;我有点懒得写了，想用的可以参考&lt;a
href="https://blog.creedowl.com/posts/ipv6_and_nat6_in_nuaa/"
target="_blank"
&gt;南航校园网 OpenWRT 配置 IPv6 NAT6&lt;/a&gt;，我自己在学校里面配的过程已经忘记了，不过跟这个应该大差不差。&lt;/p&gt;
&lt;h3 class="relative group"&gt;邻居协议
&lt;div id="邻居协议" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%82%bb%e5%b1%85%e5%8d%8f%e8%ae%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;IPv6 相比 v4 的 b 就是不同，就是邻居协议取代 ARP，RA 实现了 DHCP 的基本功能。&lt;/p&gt;
&lt;p&gt;路由器和客户端使用 NS（Neighbour Solicitation）和 NA（Neighbour Advertisement）来检测地址冲突，探测别的设备。&lt;/p&gt;
&lt;p&gt;一个 SLAAC 的过程具体是这样：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接到网络，先生成一个地址，用&lt;code&gt;::&lt;/code&gt;发送到这个地址，看自己生成的&lt;code&gt;fe80::/16&lt;/code&gt;有没有冲突，没有就使用。&lt;/li&gt;
&lt;li&gt;向&lt;code&gt;ff02::&lt;/code&gt;发送 RS，寻找路由器。&lt;/li&gt;
&lt;li&gt;路由器默认会每隔几分钟发送 RA，接收到 RS 后，会尽快回复 RA 消息。&lt;/li&gt;
&lt;li&gt;客户端自己生成一个全球唯一的可路由地址，然后再看有没有冲突，没冲突就开始使用这个地址上网。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;VLAN 划分
&lt;div id="vlan-划分" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vlan-%e5%88%92%e5%88%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;出于管理需要，我决定在路由器上面划分 VLAN 了，不过我其实并没有另一台 VLAN 的交换机，所以说并不是满血的 VLAN。同时由于是新手，对于 VLAN 的理解肯定有不到位之处，请多包涵。&lt;/p&gt;
&lt;h3 class="relative group"&gt;简单的概念
&lt;div id="简单的概念" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ae%80%e5%8d%95%e7%9a%84%e6%a6%82%e5%bf%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;trunk：在这个通道上面跑的是打上不同 ID 的 VLAN。&lt;/li&gt;
&lt;li&gt;access：某个端口，一般来说一个端口只分配给一个 VLAN ID。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在某个端口上面，选择了 VLAN ID，有不同的标志可以分配：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不属于：这个 VLAN 不分配到这个端口。&lt;/li&gt;
&lt;li&gt;tagged：通过这个端口的数据包，都会被打上这个 VLAN 的 ID。&lt;/li&gt;
&lt;li&gt;untagged：通过这个端口的数据包，如果有 VLAN ID，将会被删除掉。&lt;/li&gt;
&lt;li&gt;是主 VLAN：如果从这个接口进来的数据包没有任何标记，就认为他是这个 VLAN ID 的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;划分
&lt;div id="划分" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%92%e5%88%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;我的想法是，&lt;code&gt;.1&lt;/code&gt;的网段分配给熟悉的设备上网，&lt;code&gt;.2&lt;/code&gt;的网段分配给物联网设备，&lt;code&gt;.3&lt;/code&gt;的网段分配给访客网络。&lt;/p&gt;
&lt;p&gt;所以我在设备上的&lt;code&gt;br-lan&lt;/code&gt;的网桥 VLAN 过滤是这么配置的：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="VLAN Settings"
width="1579"
height="954"
src="https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/image_hu_ea4d2fcd69c212ee.webp"
srcset="https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/image_hu_ea4d2fcd69c212ee.webp 800w, https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/image_hu_978147032300a369.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/immortalwrt-1-ipv6-vlan/image.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;这个本地指的是路由器的 CPU 可以识别这个 VLAN 的内容，从而对它进行设置 DHCP 服务器之类的操作。&lt;/p&gt;
&lt;p&gt;路由器有三个口，我全部分配给了 VLAN1，同时设置为主 VLAN 接口（显示为*），这是因为我大概不会在这上面接物联网设备（因为都是 2.4G 的设备）和插入访客的网线。&lt;/p&gt;
&lt;p&gt;这时候在接口里面就可以看到这些虚拟的 VLAN 设备了，为它们设置 DHCP 服务器，防火墙，保存。&lt;/p&gt;
&lt;p&gt;在无线里面，可以创建很多个同频段的 SSID（不会有人才知道吧），但是信道是一样的，接口配置里面可以选择关联的网络，这里选上你设置防火墙的接口，这时候通过这个 SSID 收到的数据就会自动地被交换机打上 VLAN ID 了。&lt;/p&gt;
&lt;p&gt;在防火墙里面可以做更加细致的管理，比如使用&lt;code&gt;conntrack&lt;/code&gt;，只允许 LAN 区段的设备主动发起到 IOT 设备的连接，隔离访客网络和信任网络的区段，非常灵活。&lt;/p&gt;
&lt;p&gt;这时原有配置使用&lt;code&gt;br-lan&lt;/code&gt;的应用，记得修改为&lt;code&gt;br-lan.1&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里有一个我刚开始不明白的问题：
外部网络根本不识别 VLAN ID，那么转发到 WAN 的流量为什么会自动把 VLAN 头给去掉呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实不是主动去掉的，因为 VLAN 是二层的东西，路由（三层）和 VLAN 是两个相对独立的过程，跨局域网的时候需要换二层封装，所以 VLAN ID 自然没有了。&lt;/p&gt;</description></item><item><title>中国移动 RAX3000Me USB3.0 版折腾小记</title><link>https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/</link><pubDate>Sat, 12 Jul 2025 13:47:15 +0800</pubDate><guid>https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这几天在闲鱼蹲路由器，终于让我蹲到了一台 65 的 RAX3000Me，遂开始折腾。&lt;/p&gt;
&lt;p&gt;买之前看了下恩山发现可以免拆，无非就是导出导入配置文件破解 ssh 呗，但是到手之后，发现我大意了。&lt;/p&gt;
&lt;p&gt;这台没法按照&lt;cite&gt;标准流程&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/cite&gt;破解密码，通过爬帖得知，无法导出配置的自己的加密都存在问题，没办法免拆。&lt;/p&gt;
&lt;p&gt;那？！CH340，启动！&lt;/p&gt;
&lt;h2 class="relative group"&gt;刷写教程
&lt;div id="刷写教程" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%b7%e5%86%99%e6%95%99%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;教程大部分都是参考这篇文章，在此感谢。&lt;/p&gt;
&lt;p&gt;&lt;a
href="https://www.right.com.cn/forum/thread-8408539-1-1.html"
target="_blank"
&gt;CMCC RAX3000Me 可通过 mtk_uartboot 启动刷机，拆机刷 OpenWRT/ImmortalWRT 无障碍！&lt;/a&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;拆机
&lt;div id="拆机" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8b%86%e6%9c%ba" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;先把贴纸撕下来，拧下两颗螺丝。&lt;/p&gt;
&lt;p&gt;左手握着长边，右手握着短边，像这样：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="操作手法"
width="1039"
height="841"
src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-3_hu_72df38cb10784c8c.webp"
srcset="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-3_hu_72df38cb10784c8c.webp 800w, https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-3.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-3.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;用力往下掰，会出现一条缝，用薄点的拆机片或者卡片撬开，小心别把卡扣弄断了。&lt;/p&gt;
&lt;p&gt;拧下 5 颗螺丝，卸下散热片，上面涂有硅脂，要稍微移动一下散热片才好下来，装回去的时候得重新涂一下。&lt;/p&gt;
&lt;p&gt;（其实不卸下也行，但是我探针就太难插了）&lt;/p&gt;
&lt;p&gt;推一下下面和右边的卡扣，把主板撬出来，小心不要弄断天线连接处了。&lt;/p&gt;
&lt;p&gt;你会看到这样：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="拆机图"
width="1736"
height="923"
src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image_hu_850f85c7558569ba.webp"
srcset="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image_hu_850f85c7558569ba.webp 800w, https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image_hu_a0bc269f80f97c3e.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;电源线左边，分别是&lt;code&gt;GND&lt;/code&gt; &lt;code&gt;TX&lt;/code&gt; &lt;code&gt;3.3v&lt;/code&gt; &lt;code&gt;RX&lt;/code&gt;。&lt;/p&gt;
&lt;h3 class="relative group"&gt;文件准备
&lt;div id="文件准备" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%96%87%e4%bb%b6%e5%87%86%e5%a4%87" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;准备好这些东西：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ ls -lah
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;总计 2.3M
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;drwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; texsd texsd &lt;span class="m"&gt;148&lt;/span&gt; 7月19日 14:11 .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;drwx------ &lt;span class="m"&gt;1&lt;/span&gt; texsd texsd 1.3K 7月18日 21:58 ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-rw-r--r-- &lt;span class="m"&gt;1&lt;/span&gt; texsd texsd 248K 7月14日 18:10 mt7981-cmcc_rax3000me-nand-ddr3-fip-fit.bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-rwxrwxr-x &lt;span class="m"&gt;1&lt;/span&gt; texsd texsd 206K 2024年 2月 2日 mt7981-ddr3-bl2.bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-rwxr-xr-x &lt;span class="m"&gt;1&lt;/span&gt; texsd texsd 1.9M 2024年 3月 2日 mtk_uartboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a
href="https://www.right.com.cn/forum/thread-8400306-1-1.html"
target="_blank"
&gt;天灵的 U-Boot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a
href="https://www.lanzouw.com/ioTYu1pvi23g"
target="_blank"
&gt;bl2&lt;/a&gt; &lt;cite&gt;（预构建，也可自行编译）&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;&lt;a
href="https://github.com/981213/mtk_uartboot/releases/tag/v0.1.1"
target="_blank"
&gt;mtk_uartboot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;打开你的&lt;code&gt;shell&lt;/code&gt;，&lt;code&gt;cd&lt;/code&gt;到此文件夹，连接你的 CH340，你应该能在设备管理器看到对应的串口。我是&lt;code&gt;/dev/ttyUSB0&lt;/code&gt;，如果你使用 Windows，一般是&lt;code&gt;COMX&lt;/code&gt;，请自行替换。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./mtk_uartboot -s /dev/ttyUSB0 -p ./mt7981-ddr3-bl2.bin -f mt7981-cmcc_rax3000me-nand-ddr3-fip-fit.bin --brom-load-baudrate &lt;span class="m"&gt;115200&lt;/span&gt; --bl2-load-baudrate &lt;span class="m"&gt;115200&lt;/span&gt; -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;这里要记得加&lt;code&gt;-a&lt;/code&gt;，否则可能识别成 aarch32 导致超时。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这时候应该会提示：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mtk_uartboot - 0.1.1
Using serial port: /dev/ttyUSB0
Handshake...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;准备完成！&lt;/p&gt;
&lt;h3 class="relative group"&gt;刷入 UBOOT
&lt;div id="刷入-uboot" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%b7%e5%85%a5-uboot" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;把你的 CH340 插上线，用杜邦线什么的都行，但是最好有带弹簧的测试探针，这是我的工具：&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="CH340"
width="1681"
height="1015"
src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-2_hu_d8edd934ec3918e7.webp"
srcset="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-2_hu_d8edd934ec3918e7.webp 800w, https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-2_hu_2b0be9a9ae98c7f9.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://minortex.github.io/posts/flashing_cmcc_rax3000me_usb/image-2.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;找出 CH340 的&lt;code&gt;GND&lt;/code&gt; &lt;code&gt;RX&lt;/code&gt; &lt;code&gt;TX&lt;/code&gt;，从左往右依次按上主板的触点（不用接 3.3v），这时候右手还可以操作电脑。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你不确定是否插好，可以退出 mtk_uartboot，打开任意串口工具并连接，直接插电启动，应该能看到调试信息。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;确认插好了，就直接上电，大概 30 秒左右，mtk_uartboot 输出以下内容并交还 shell：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mtk_uartboot - 0.1.1
Using serial port: /dev/ttyUSB0
Handshake...
hw code: 0x7981
hw sub code: 0x8a00
hw ver: 0xca00
sw ver: 0x1
Baud rate set to 115200
sending payload to 0x201000...
Checksum: 0x55cf
Setting baudrate back to 115200
Jumping to 0x201000 in aarch64...
Waiting for BL2. Message below:
==================================
NOTICE: BL2: v2.10.0 (release):v2.4-rc0-5845-gbacca82a8-dirty
NOTICE: BL2: Built : 20:18:08, Feb 2 2024
NOTICE: WDT: Cold boot
NOTICE: WDT: disabled
NOTICE: EMI: Using DDR3 settings
NOTICE: EMI: Detected DRAM size: 512MB
NOTICE: EMI: complex R/W mem test passed
NOTICE: CPU: MT7981 (1300MHz)
NOTICE: Starting UART download handshake ...
==================================
BL2 UART DL version: 0x10
Baudrate set to: 115200
FIP sent.
==================================
NOTICE: Received FIP 0x3df58 @ 0x40400000 ...
==================================
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后路由器就会亮起蓝灯（正常进官方系统没联网是红灯），网线插 LAN 口连到电脑，输入&lt;code&gt;192.168.1.1/uboot.html&lt;/code&gt;，上传之前下载的 uboot。&lt;/p&gt;
&lt;h4 class="relative group"&gt;一些问题
&lt;div id="一些问题" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e4%ba%9b%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mtk_uartboot&lt;/code&gt;异常退出，提示 coredump&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这大概率是连接不好/接上了 3.3v。建议不要接 3.3v，否则每次连上都会导致 CH340 断开连接。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;无论怎么尝试，总是提示&lt;code&gt;Timeout waiting for specified message.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有下面两种情况：&lt;/p&gt;
&lt;p&gt;第一种：没有加&lt;code&gt;-d&lt;/code&gt;参数。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Jumping to 0x201000 in aarch32...
Waiting for BL2. Message below:
==================================
==================================
Timeout waiting for specified message.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这就是默认使用了&lt;code&gt;aarch32&lt;/code&gt;，请严格按照上面的命令执行！&lt;/p&gt;
&lt;p&gt;第二种：没识别出内存。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: BL2: v2.10.0 &lt;span class="o"&gt;(&lt;/span&gt;release&lt;span class="o"&gt;)&lt;/span&gt;:v2.4-rc0-5845-gbacca82a8-dirty
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: BL2: Built : 20:20:25, Feb &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;2024&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: WDT: Cold boot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: WDT: disabled
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: EMI: Using DDR4 settings
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NOTICE: EMI: Detected DRAM size: &lt;span class="nv"&gt;0MB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你的 bl2 用错了，请确认你的 ddr 类型，或者尝试另外一种 ddr 类型。&lt;/p&gt;
&lt;p&gt;第三种：是&lt;code&gt;aarch64&lt;/code&gt;，但是没有输出。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Jumping to 0x201000 in aarch64...
Waiting for BL2. Message below:
==================================
==================================
Timeout waiting for specified message.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可能是以下情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自己编译的 bl2 没成功。&lt;/li&gt;
&lt;li&gt;下载中出现损坏。&lt;/li&gt;
&lt;li&gt;使用了 immortalwrt 官方的 preloader，此文件我试了很多次都没成功，原因未知。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 class="relative group"&gt;刷入固件
&lt;div id="刷入固件" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%b7%e5%85%a5%e5%9b%ba%e4%bb%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;新版固件
&lt;div id="新版固件" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%96%b0%e7%89%88%e5%9b%ba%e4%bb%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;新版的&lt;code&gt;immortalwrt&lt;/code&gt;（23.05 和我打算用的 24.10）已经在使用一种新的镜像文件&lt;code&gt;*.itb&lt;/code&gt;，这个镜像同时包含&lt;code&gt;dtb&lt;/code&gt; &lt;code&gt;ramdisk&lt;/code&gt; &lt;code&gt;kernel&lt;/code&gt;。想要详细了解的可以参考&lt;a
href="http://www.wowotech.net/u-boot/fit_image_overview.html"
target="_blank"
&gt;u-boot FIT image 介绍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;直接刷入&lt;code&gt;sysupgrade&lt;/code&gt;的&lt;code&gt;itb&lt;/code&gt;可能会出现不成功的情况，表现为一直闪蓝灯，这里我们需要先刷入&lt;code&gt;initramfs&lt;/code&gt;的&lt;code&gt;itb&lt;/code&gt;，重启进入系统之后，再刷入对应的&lt;code&gt;sysupgrade&lt;/code&gt;镜像。&lt;/p&gt;
&lt;p&gt;请注意&lt;code&gt;uboot&lt;/code&gt;版本可能与固件版本相关，经过测试天灵 25 年 3 月编译的&lt;code&gt;uboot&lt;/code&gt;是可以使用我上面的方法刷入 237 固件。&lt;/p&gt;
&lt;p&gt;&lt;a
href="https://github.com/padavanonly/immortalwrt-mt798x-6.6"
target="_blank"
&gt;padavanonly/immortalwrt-mt798x-6.6&lt;/a&gt;&lt;/p&gt;
&lt;h4 class="relative group"&gt;旧版固件
&lt;div id="旧版固件" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%97%a7%e7%89%88%e5%9b%ba%e4%bb%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;如果你想使用原汁原味的&lt;a
href="https://cmi.hanwckf.top/p/immortalwrt-mt798x/"
target="_blank"
&gt;immortalwrt-mt798x&lt;/a&gt;，你可能得使用原版的 hanwckf 的&lt;code&gt;uboot&lt;/code&gt;，这个版本没有&lt;code&gt;dhcp&lt;/code&gt;需要手动设置静态地址。建议使用 24.11 的版本，因为尝试了 22 年的版本发现不认复旦微的这颗 NAND，导致&lt;code&gt;uboot&lt;/code&gt;无法启动，只能使用 CH340 重新刷一遍。&lt;/p&gt;
&lt;p&gt;而且，使用最新源码编译出来会导致 wifi 设置中缺少加密方式的问题，此问题亦有在 issues 里面提及，但感觉修复的可能性不大。&lt;/p&gt;
&lt;h2 class="relative group"&gt;参考
&lt;div id="参考" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%82%e8%80%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a
href="https://github.com/Daniel-Hwang/RAX3000Me"
target="_blank"
&gt;Daniel-Hwang/RAX3000Me: RAX3000Me 路由器开发与固件刷入详细教程&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a
href="https://www.cnblogs.com/p123/p/18046679"
target="_blank"
&gt;MediaTek Filogic 系列路由器串口救砖教程 - 暗云 - 博客园&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>黑群晖与 PVE：SMART 修复与 ACL 权限</title><link>https://minortex.github.io/posts/synology-pve-smart-acl/</link><pubDate>Mon, 07 Jul 2025 21:00:20 +0800</pubDate><guid>https://minortex.github.io/posts/synology-pve-smart-acl/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这是一篇记录自己折腾的过程的文章，可能会随着我的折腾过程补充，可能开会补一点动机或者小技巧之类的，所以你看到的不一定是完整版~&lt;/p&gt;
&lt;h2 class="relative group"&gt;直通 SATA 控制器的硬盘 SMART 修复
&lt;div id="直通-sata-控制器的硬盘-smart-修复" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%9b%b4%e9%80%9a-sata-%e6%8e%a7%e5%88%b6%e5%99%a8%e7%9a%84%e7%a1%ac%e7%9b%98-smart-%e4%bf%ae%e5%a4%8d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;使用 PVE 直通我的 SATA 控制器进群晖，发现命令行的 SMART 的功能不正常，表现就是没法显示其中的详细信息，而且温度显示为 0°C。&lt;/p&gt;
&lt;p&gt;虽然群晖里面显示有温度，但是最新的 DSM7.2.2 已经没法在桌面端读取 SMART 信息了，不知道是出于什么考虑。&lt;/p&gt;
&lt;p&gt;在 shell 里面是这样的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;texsd@tnas ~ [2]&amp;gt; sudo smartctl -a /dev/sata1
smartctl 6.5 (build date Sep 26 2022) [x86_64-linux-5.10.55+] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
=== START OF INFORMATION SECTION ===
Vendor: WDC
Product: WD40EJRX-89AKWY0
Revision: 0B80
User Capacity: 4,000,787,030,016 bytes [4.00 TB]
Logical block size: 512 bytes
Physical block size: 4096 bytes
LU is fully provisioned
Rotation Rate: 5400 rpm
Form Factor: 3.5 inches
Logical Unit id: 0x50014ee2bed41999
Serial number: WD-XXXXXXXXXXXX
Device type: disk
Local Time is: Thu Jul 10 21:23:30 2025 CST
SMART support is: Unavailable - device lacks SMART capability.
=== START OF READ SMART DATA SECTION ===
Current Drive Temperature: 0 C
Drive Trip Temperature: 0 C
Error Counter logging not supported
[GLTSD (Global Logging Target Save Disable) set. Enable Save with &amp;#39;-S on&amp;#39;]
Device does not support Self Test logging
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我问了下 Gemini，跟我说消费级主板不支持，叫我买 LSI 卡，我当时想麻烦了，亏我专门找一块 4SATA 的板子。&lt;/p&gt;
&lt;h3 class="relative group"&gt;问题解决
&lt;div id="问题解决" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%97%ae%e9%a2%98%e8%a7%a3%e5%86%b3" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;我先是一通操作，把 SATA 控制器直通进 Arch 虚拟机，发现是能成功读取的。那说明是 DSM 出了点问题。在 RR 里面一通乱改添加&lt;code&gt;smartctl&lt;/code&gt;，本来以为成了，结果接上三块硬盘，一看，只有其中一块是正常的。&lt;/p&gt;
&lt;p&gt;看了网上一些教程，发现要手动指定设备类型，才能正确读出硬盘的 smart 信息。&lt;/p&gt;
&lt;p&gt;群晖手动改了 sata 盘的名字，不是 sd[a-z]了，而是 sata[1-9]。所以我得这样获取：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo smartctl -d sat -a /dev/sata1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;smartctl 6.5 (build date Sep 26 2022) [x86_64-linux-5.10.55+] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
=== START OF INFORMATION SECTION ===
Device Model: WDC WD40EJRX-89AKWY0
Serial Number: WD-XXXXXXXXXXXX
LU WWN Device Id: 5 0014ee 2bed41999
Firmware Version: 80.00B80
User Capacity: 4,000,787,030,016 bytes [4.00 TB]
Sector Sizes: 512 bytes logical, 4096 bytes physical
Rotation Rate: 5400 rpm
Form Factor: 3.5 inches
Device is: Not in smartctl database [for details use: -P showall]
ATA Version is: ACS-3 T13/2161-D revision 5
SATA Version is: SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is: Thu Jul 10 21:33:49 2025 CST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
=== START OF READ SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED
General SMART Values:
Offline data collection status: (0x00) Offline data collection activity
was never started.
Auto Offline Data Collection: Disabled.
Self-test execution status: ( 0) The previous self-test routine completed
without error or no self-test has ever
been run.
Total time to complete Offline
data collection: (41760) seconds.
Offline data collection
capabilities: (0x11) SMART execute Offline immediate.
No Auto Offline data collection support.
Suspend Offline collection upon new
command.
No Offline surface scan supported.
Self-test supported.
No Conveyance Self-test supported.
No Selective Self-test supported.
SMART capabilities: (0x0003) Saves SMART data before entering
power-saving mode.
Supports SMART auto save timer.
Error logging capability: (0x01) Error logging supported.
General Purpose Logging supported.
Short self-test routine
recommended polling time: ( 2) minutes.
Extended self-test routine
recommended polling time: ( 443) minutes.
SCT capabilities: (0x303d) SCT Status supported.
SCT Error Recovery Control supported.
SCT Feature Control supported.
SCT Data Table supported.
SMART Attributes Data Structure revision number: 16
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x002f 200 200 051 Pre-fail Always - 0
3 Spin_Up_Time 0x0027 223 219 021 Pre-fail Always - 3850
4 Start_Stop_Count 0x0032 100 100 000 Old_age Always - 303
5 Reallocated_Sector_Ct 0x0033 200 200 140 Pre-fail Always - 0
7 Seek_Error_Rate 0x002e 200 200 000 Old_age Always - 0
9 Power_On_Hours 0x0032 056 056 000 Old_age Always - 32405
10 Spin_Retry_Count 0x0032 100 100 000 Old_age Always - 0
11 Calibration_Retry_Count 0x0032 100 100 000 Old_age Always - 0
12 Power_Cycle_Count 0x0032 100 100 000 Old_age Always - 203
192 Power-Off_Retract_Count 0x0032 200 200 000 Old_age Always - 79
193 Load_Cycle_Count 0x0032 200 200 000 Old_age Always - 233
194 Temperature_Celsius 0x0022 110 105 000 Old_age Always - 40
196 Reallocated_Event_Count 0x0032 200 200 000 Old_age Always - 0
197 Current_Pending_Sector 0x0032 200 200 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0030 100 253 000 Old_age Offline - 0
199 UDMA_CRC_Error_Count 0x0032 200 200 000 Old_age Always - 0
200 Multi_Zone_Error_Rate 0x0008 100 253 000 Old_age Offline - 0
SMART Error Log Version: 1
No Errors Logged
SMART Self-test log structure revision number 1
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Short offline Aborted by host 90% 32358 -
# 2 Short offline Completed without error 00% 32333 -
# 3 Short offline Completed without error 00% 25336 -
# 4 Short offline Completed without error 00% 25093 -
# 5 Short offline Completed without error 00% 15531 -
# 6 Short offline Completed without error 00% 15527 -
# 7 Short offline Completed without error 00% 15527 -
# 8 Short offline Completed without error 00% 7694 -
# 9 Short offline Completed without error 00% 6217 -
#10 Short offline Completed without error 00% 3870 -
#11 Short offline Completed without error 00% 3869 -
#12 Short offline Completed without error 00% 3868 -
#13 Short offline Completed without error 00% 3609 -
#14 Short offline Completed without error 00% 3598 -
#15 Short offline Completed without error 00% 3545 -
#16 Short offline Completed without error 00% 3524 -
#17 Short offline Completed without error 00% 3335 -
#18 Short offline Completed without error 00% 3033 -
#19 Short offline Completed without error 00% 0 -
#20 Short offline Completed without error 00% 0 -
#21 Short offline Completed without error 00% 0 -
Selective Self-tests/Logging not supported
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;确实获取到了，但是为什么呢？&lt;/p&gt;
&lt;h3 class="relative group"&gt;问题溯源
&lt;div id="问题溯源" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%97%ae%e9%a2%98%e6%ba%af%e6%ba%90" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;得懂能行，更得明白为什么可以这样。&lt;/p&gt;
&lt;p&gt;我去翻了&lt;code&gt;smartctl&lt;/code&gt;的 manual，里面关于&lt;code&gt;-d&lt;/code&gt;是这么写的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;...
smartctl guesses the device type if possible. If necessary, the &amp;#39;-d&amp;#39; option can be used
to override this guess.
...
-d TYPE, --device=TYPE
Specifies the type of the device. The valid arguments to this option are:
auto - attempt to guess the device type from the device name or from controller
type info provided by the operating system or from a matching USB ID entry in the
drive database. This is the default.
test - prints the guessed TYPE, then opens the device and prints the (possibly
changed) TYPE name and then exits without performing any further commands.
ata - the device type is ATA. This prevents smartctl from issuing SCSI commands
to an ATA device.
scsi - the device type is SCSI. This prevents smartctl from issuing ATA commands
to a SCSI device.
nvme[,NSID] - the device type is NVM Express (NVMe). The optional parameter NSID
specifies the namespace id (in hex) passed to the driver. Use 0xffffffff for the
broadcast namespace id. The default for NSID is the namespace id addressed by
the device name.
sat[,auto][,N] - the device type is SCSI to ATA Translation (SAT). This is for
ATA disks that have a SCSI to ATA Translation Layer (SATL) between the disk and
the operating system. SAT defines two ATA PASS THROUGH SCSI commands, one 12
bytes long and the other 16 bytes long. The default is the 16 byte variant which
can be overridden with either &amp;#39;-d sat,12&amp;#39; or &amp;#39;-d sat,16&amp;#39;.
If &amp;#39;-d sat,auto&amp;#39; is specified, device type SAT (for ATA/SATA disks) is only used
if the SCSI INQUIRY data reports a SATL (VENDOR: &amp;#34;ATA &amp;#34;). Otherwise device
type SCSI (for SCSI/SAS disks) is used.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;好家伙！原来是给我自动在猜啊😲&lt;/p&gt;
&lt;p&gt;康康你猜到了什么~&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;texsd@tnas ~&amp;gt; sudo smartctl -d &lt;span class="nb"&gt;test&lt;/span&gt; /dev/sata1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;smartctl 6.5 &lt;span class="o"&gt;(&lt;/span&gt;build date Sep &lt;span class="m"&gt;26&lt;/span&gt; 2022&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;x86_64-linux-5.10.55+&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt; build&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Copyright &lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt; 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata1: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;SCSI&lt;span class="o"&gt;]&lt;/span&gt; detected
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata1: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;SCSI&lt;span class="o"&gt;]&lt;/span&gt; opened
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;texsd@tnas ~ &lt;span class="o"&gt;[&lt;/span&gt;255&lt;span class="o"&gt;]&lt;/span&gt;&amp;gt; sudo smartctl -d &lt;span class="nb"&gt;test&lt;/span&gt; /dev/sata2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;smartctl 6.5 &lt;span class="o"&gt;(&lt;/span&gt;build date Sep &lt;span class="m"&gt;26&lt;/span&gt; 2022&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;x86_64-linux-5.10.55+&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt; build&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Copyright &lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt; 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata2: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;SCSI&lt;span class="o"&gt;]&lt;/span&gt; detected
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata2: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;SCSI&lt;span class="o"&gt;]&lt;/span&gt; opened
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;texsd@tnas ~ &lt;span class="o"&gt;[&lt;/span&gt;255&lt;span class="o"&gt;]&lt;/span&gt;&amp;gt; sudo smartctl -d &lt;span class="nb"&gt;test&lt;/span&gt; /dev/sata3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;smartctl 6.5 &lt;span class="o"&gt;(&lt;/span&gt;build date Sep &lt;span class="m"&gt;26&lt;/span&gt; 2022&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;x86_64-linux-5.10.55+&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt; build&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Copyright &lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt; 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata3: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;SCSI&lt;span class="o"&gt;]&lt;/span&gt; detected
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata3 &lt;span class="o"&gt;[&lt;/span&gt;SAT&lt;span class="o"&gt;]&lt;/span&gt;: Device open changed &lt;span class="nb"&gt;type&lt;/span&gt; from &lt;span class="s1"&gt;&amp;#39;scsi&amp;#39;&lt;/span&gt; to &lt;span class="s1"&gt;&amp;#39;sat&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dev/sata3 &lt;span class="o"&gt;[&lt;/span&gt;SAT&lt;span class="o"&gt;]&lt;/span&gt;: Device of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sat&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;ATA&lt;span class="o"&gt;]&lt;/span&gt; opened
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以问题已经很清晰了，PVE 在把 SATA 控制器直通的时候，识别出了一点问题，没有正确的报告需要使用 sat(SCSI to ATA Translation)，同时 DSM 配套的&lt;code&gt;smartctl&lt;/code&gt;太旧了，猜错了，没有猜到这一个 SCSI 控制器是需要使用 sat 来翻译，直接使用 SCSI 来获取 SMART 信息，就没法读取到正确的信息。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但是为什么是 SCSI 控制器？我不是把 PCIe 设备直通吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这还是挺奇妙的，因为 PVE 很懒，只想加载一个 VirtIO SCSI 驱动，不想再折腾一个 SATA 驱动，所以虚拟机看到的，是一个 SCSI 设备，想猜出是 sat，得靠点想象力。&lt;/p&gt;
&lt;p&gt;但是新版的&lt;code&gt;smartctl&lt;/code&gt;就成功了，&lt;del&gt;这就不得不提 Arch 的滚动更新的优越性了&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;一些知识补充：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SCSI 并不是为了磁盘而生的，而是一个系统的接口，用于连接大量外围设备的。它的成本早期非常高昂，需要一个独立的 SCSI 控制器，所以只有高端工作站才用得起。八十年代，IBM 兼容机崛起，为了降低成本，人们开发了 ATA (IDE)，这个控制器的成本低廉，而且控制部分集成在硬盘上，大幅降低了成本。（这就是为什么 SAS 硬盘没法接在 SATA 控制器上，但是反过来就可以）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;突然想起了*nix 和 dos 的斗争，当年的 dos 也太简陋了，连用户管理都没有，早年我说这设计那么垃圾的系统，自动补全又没有，用的逆天的反斜杠，怎么当时会抢占那么多市场？&lt;/p&gt;
&lt;p&gt;但是人家确实打下了市场，就是因为硬件要求低。&lt;/p&gt;
&lt;p&gt;所以说当时降本是当时时代的趋势，就算现在 SAS 阵列卡已经白菜价了，*nix 甚至占用比 Windows 还低，但是前者估计是不太可能回到消费级电子领域了，后者可能还有点希望，得指望 deepin 和信创了。&lt;/p&gt;
&lt;h2 class="relative group"&gt;群晖的文件权限管理
&lt;div id="群晖的文件权限管理" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%be%a4%e6%99%96%e7%9a%84%e6%96%87%e4%bb%b6%e6%9d%83%e9%99%90%e7%ae%a1%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;我们已经早都知道，传统的 Linux 权限很简单而且很不方便，只能设置所有者、组和其他的权限，随后拓展出来的 ACL 规则则很灵活。但是群晖上面没有&lt;code&gt;setfacl&lt;/code&gt;和&lt;code&gt;getfacl&lt;/code&gt;之类的命令，取而代之的是&lt;code&gt;synoacltool&lt;/code&gt;，应该是存了数据库以便 web 也能够正确使用这些权限。&lt;/p&gt;
&lt;p&gt;我从旧 NAS 迁移过来的数据是使用 rsync 的，旧 nas 的权限管理比较混乱，导致所有存储的文件都是 UID0（root），迁移过来就造成了一些问题。&lt;/p&gt;
&lt;p&gt;我不知道群晖是怎么实现网页可以读写的，但是我在 shell 里面就没法进入这些文件夹，此时就需要修复。&lt;/p&gt;
&lt;p&gt;群晖提供的方便的 webui 来修改，在共享文件夹的子文件夹右键 - 属性 - 权限。此时会显示此共享文件夹默认配置的权限，点击下面的“应用到这个文件夹、子文件夹及文件”，保存。此时群晖就把这个文件夹改成 ACL 模式，使得在 shell 里面也能访问。&lt;/p&gt;
&lt;p&gt;但是我有些疑问：&lt;/p&gt;
&lt;h3 class="relative group"&gt;1. 为什么改 ACL 那么快？
&lt;div id="1-为什么改-acl-那么快" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e6%94%b9-acl-%e9%82%a3%e4%b9%88%e5%bf%ab" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;当您在 File Station 的图形界面中勾选“应用到子文件夹&amp;hellip;”时，您不是在运行一个简单的脚本，而是在向 DSM 的核心服务发出一个高级指令。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;这个指令更像是：“嘿，DSM 内核/存储服务，请你用最高效的方式，把这个 ACL 策略应用到整个文件夹树”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;DSM 的底层服务接收到这个“批发订单”后，会直接在内核层面或者以最优化的方式遍历文件系统的元数据 (metadata)。它可以批量处理、减少磁盘 I/O、避免不必要的上下文切换，效率远非 chmod -R 可比。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;（摘抄自 Gemini 的回答）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 class="relative group"&gt;2. ACL 是怎么工作的？
&lt;div id="2-acl-是怎么工作的" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-acl-%e6%98%af%e6%80%8e%e4%b9%88%e5%b7%a5%e4%bd%9c%e7%9a%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;通过 ACL 管理的权限，我很意外的发现&lt;code&gt;ls&lt;/code&gt;的显示已经不正常了。先举个例子：&lt;/p&gt;
&lt;p&gt;ACL 规则是这样的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;texsd@tnas /volume1&amp;gt; sudo synoacltool -get 个人数据
ACL version: 1
Archive: has_ACL,is_support_ACL
Owner: [root(user)]
[0] group:administrators:allow:rwxpdDaARWc--:fd-- (level:0)
[1] group:custom_user:allow:rwxpdDaARWc--:fd-- (level:0)
[2] user:ActiveBackup:allow:rwxpdDaARWc--:fd-- (level:0)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以看到，我在&lt;code&gt;administrators&lt;/code&gt;组，有访问这个目录的权限。&lt;/p&gt;
&lt;p&gt;我用&lt;code&gt;ls&lt;/code&gt;查看权限：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;texsd@tnas /volume1&amp;gt; ls -lah |grep 个人数据
drwxrwxrwx+ 1 root root 86 Jul 8 11:11 个人数据
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我看到所有人都是有这个权限的。&lt;/p&gt;
&lt;p&gt;guest 用&lt;code&gt;ls&lt;/code&gt;查看权限：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;guest@tnas:/volume1$ ls -lah | grep 个人数据
d---------+ 1 root root 86 Jul 8 11:11 个人数据
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;他没有任何权限，自然无法进入。&lt;/p&gt;
&lt;p&gt;所以说，ACL 实际上改变了每个人看到的&lt;code&gt;ls&lt;/code&gt;信息，在 ACL Mode 下，&lt;code&gt;ls&lt;/code&gt;的输出已经不准确了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;3. 虽然说权限已经不准确了，那么所有者和组怎么样？
&lt;div id="3-虽然说权限已经不准确了那么所有者和组怎么样" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e8%99%bd%e7%84%b6%e8%af%b4%e6%9d%83%e9%99%90%e5%b7%b2%e7%bb%8f%e4%b8%8d%e5%87%86%e7%a1%ae%e4%ba%86%e9%82%a3%e4%b9%88%e6%89%80%e6%9c%89%e8%80%85%e5%92%8c%e7%bb%84%e6%80%8e%e4%b9%88%e6%a0%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;所有者和组仍然是以创建的用户为准的。谁创建的就是谁和启主要用户组（UID 显示的组）。&lt;/p&gt;
&lt;h3 class="relative group"&gt;4. ACL 让我整个终端显示都是一片绿，为什么要在 POSIX 的 rwx 中设置为 777？
&lt;div id="4-acl-让我整个终端显示都是一片绿为什么要在-posix-的-rwx-中设置为-777" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-acl-%e8%ae%a9%e6%88%91%e6%95%b4%e4%b8%aa%e7%bb%88%e7%ab%af%e6%98%be%e7%a4%ba%e9%83%bd%e6%98%af%e4%b8%80%e7%89%87%e7%bb%bf%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e5%9c%a8-posix-%e7%9a%84-rwx-%e4%b8%ad%e8%ae%be%e7%bd%ae%e4%b8%ba-777" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这里有一个小前置知识，如果其他用户可写，那么就会显示为绿色背景，如果文件可执行，就是显示为绿色字体。&lt;/p&gt;
&lt;p&gt;因为 ACL 已经接管了权限管理，所以说 POSIX 显示的已经不够用了，比如说我上面的例子，有两个组都能访问这个文件夹，这个究竟如何界定？&lt;/p&gt;
&lt;p&gt;如果使用 770，那么指的是 users 才有这个权限，实际上并不是。&lt;/p&gt;
&lt;p&gt;所以，ACL 让每个人看到的东西不一样，这里的 777 表示有极大的权限，反之没权限的为 000，不是传统意义上面的 421 了。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;我明白为什么 fish 不兼容 POSIX 了，因为确实不符合现代的一些实践了&lt;/del&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;参考
&lt;div id="参考" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%82%e8%80%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;smartctl&lt;/code&gt; Manual&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a
href="https://zh.wikipedia.org/wiki/%E5%B0%8F%E5%9E%8B%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E6%8E%A5%E5%8F%A3"
target="_blank"
&gt;小型计算机系统接口 - 维基百科&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a
href="https://zh.wikipedia.org/wiki/SATA"
target="_blank"
&gt;SATA - 维基百科&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a
href="https://zh.wikipedia.org/wiki/%E9%AB%98%E6%8A%80%E8%A1%93%E9%85%8D%E7%BD%AE"
target="_blank"
&gt;高技术配置 - 维基百科&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>操作系统笔记：设备与驱动程序</title><link>https://minortex.github.io/posts/os-1-devices-drivers/io/</link><pubDate>Wed, 02 Jul 2025 11:28:27 +0800</pubDate><guid>https://minortex.github.io/posts/os-1-devices-drivers/io/</guid><description>
&lt;h2 class="relative group"&gt;插上u盘，是如何工作的？
&lt;div id="插上u盘是如何工作的" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8f%92%e4%b8%8au%e7%9b%98%e6%98%af%e5%a6%82%e4%bd%95%e5%b7%a5%e4%bd%9c%e7%9a%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;usb控制器检测到电平变化 -&amp;gt; 硬件中断 -&amp;gt; 总线驱动捕获中断 -&amp;gt; sysfs创建设备目录和属性文件 -&amp;gt; 内核通过&lt;code&gt;netlink&lt;/code&gt;发出uevent消息，广播&lt;/p&gt;
&lt;h2 class="relative group"&gt;为什么udev的效率高？
&lt;div id="为什么udev的效率高" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%ba%e4%bb%80%e4%b9%88udev%e7%9a%84%e6%95%88%e7%8e%87%e9%ab%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;udev被内核发送的uevent唤醒，而不是一直去轮询内核。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;udev是并行工作的。在多个设备插入的时候，为每个事件启动一个处理实例。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;udev使用的Netlink为传递短小的控制信息优化，比其他IPC更加轻量和迅速。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;udev启动的时候会读取所有&lt;code&gt;.rules&lt;/code&gt;文件，并解析成快速查询的内存数据结构。（这个就像mihomo一样，提前把规则处理成一个搜索树，使得匹配效率大幅提高）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;I/O设备控制
&lt;div id="io设备控制" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#io%e8%ae%be%e5%a4%87%e6%8e%a7%e5%88%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;X86同时支持MMIO/PMIO，ARM只支持MMIO。&lt;/p&gt;
&lt;p&gt;通过一条线，一条指令就可以实现控制。&lt;/p&gt;
&lt;p&gt;控制方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MMIO&lt;/strong&gt;: 把外部控制寄存器映射到内存地址空间。使得一块特定的内存地址不再对应物理RAM。像这块内存地址写入，就是发送到硬件的控制寄存器，而从这里读取，就是获取寄存器当前的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PMIO&lt;/strong&gt;: （微机里面的IN/OUT指令）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;使用内存的地址空间，不会造成存的浪费吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;早期的32位系统，你安装了4G内存，会发现只有3.几可用，剩下为硬件保留的内存就是这个原因。一般都是被强制保留给PCIe的MMIO。&lt;/p&gt;
&lt;h2 class="relative group"&gt;GPIO
&lt;div id="gpio" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#gpio" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;一个通用的IO，有别于专用IO（USB/HDMI）。可以编程，可以作为输入或者输出。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;输入模式：读取外部世界。通过这个引脚送过来的电平是高或者低来感知外部世界。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输出模式：设置这个引脚为高电平或者低电平，从而控制外部设备。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>维护 Esxi 的小记</title><link>https://minortex.github.io/posts/esxi-maintenance/</link><pubDate>Fri, 20 Jun 2025 23:53:22 +0800</pubDate><guid>https://minortex.github.io/posts/esxi-maintenance/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;学校社团的虚拟机空间快要不够了，派我进行整理以下虚拟机。看了一通，发现好多虚拟机是厚制备的，感叹当时买新硬盘的时候真是奢侈！&lt;/p&gt;
&lt;p&gt;现在空间快要用尽了，用了百分之八十左右，大家还在讨论要不要给 esxi 加一块硬盘。我提出其实没必要，因为很多空间是可以压缩的，比如一台&lt;code&gt;FreshCup&lt;/code&gt;机子，本身就只有一个服务，但是当时磁盘选了厚置备，只用了 13G，但是占用了快 50G，这真浪费啊！于是便有了这篇文。&lt;/p&gt;
&lt;h2 class="relative group"&gt;初探 ESXi 内部结构
&lt;div id="初探-esxi-内部结构" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%9d%e6%8e%a2-esxi-%e5%86%85%e9%83%a8%e7%bb%93%e6%9e%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;开始我脑子没转过来，还想着找一台大硬盘的电脑，用 WorkStation 来进行压缩。不知道是不是无线的问题，下载速度不过百兆出头，这对于几十个 G 来说真是挑战！&lt;/p&gt;
&lt;p&gt;但是你看 ESXi 的那个 webui 叫作一个完成度低啊，根本找不到转换的选项。&lt;/p&gt;
&lt;p&gt;我之前也听说过 ESXi 用的是定制的系统，但是人家竟然能用 22 端口访问，登录进去甚至还有 bash，GNU 的基本工具还是有的，但是稍微常见一点的工具比如&lt;code&gt;curl&lt;/code&gt;就没有了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;这个系统是什么呢？
&lt;div id="这个系统是什么呢" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bf%99%e4%b8%aa%e7%b3%bb%e7%bb%9f%e6%98%af%e4%bb%80%e4%b9%88%e5%91%a2" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@localhost:/vmfs/volumes/65522cf6-65fd4b38-f248-6c92bfca102e&lt;span class="o"&gt;]&lt;/span&gt; uname -a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VMkernel localhost 7.0.3 &lt;span class="c1"&gt;#1 SMP Release build-21686933 Apr 28 2023 08:42:51 x86_64 x86_64 x86_64 ESXi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;呃呃，&lt;code&gt;VMkernel&lt;/code&gt;，大概是基于&lt;code&gt;unix&lt;/code&gt;定制的吧，但是我觉得也有可能是&lt;code&gt;linux&lt;/code&gt;。&lt;/p&gt;
&lt;h3 class="relative group"&gt;看看硬盘占用情况？
&lt;div id="看看硬盘占用情况" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%9c%8b%e7%9c%8b%e7%a1%ac%e7%9b%98%e5%8d%a0%e7%94%a8%e6%83%85%e5%86%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@localhost:/vmfs/volumes/65522cf6-65fd4b38-f248-6c92bfca102e&lt;span class="o"&gt;]&lt;/span&gt; df -h
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Filesystem Size Used Available Use% Mounted on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VMFS-6 745.0G 481.7G 263.3G 65% /vmfs/volumes/99-datastore0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VFFS 103.5G 3.5G 100.0G 3% /vmfs/volumes/OSDATA-654f485c-fa8f3400-bbb9-6c92bfca102e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vfat 4.0G 204.8M 3.8G 5% /vmfs/volumes/BOOTBANK1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vfat 4.0G 64.0K 4.0G 0% /vmfs/volumes/BOOTBANK2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（完成了才贴上来，假装没看到剩余的 263G 空间吧）&lt;/p&gt;
&lt;h3 class="relative group"&gt;有什么特殊工具？
&lt;div id="有什么特殊工具" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9c%89%e4%bb%80%e4%b9%88%e7%89%b9%e6%ae%8a%e5%b7%a5%e5%85%b7" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;当然，这个系统里面有一大堆 vm 开头的工具，这就是为了命令行管理用的 ESXi 专有工具了。&lt;/p&gt;
&lt;p&gt;我主要用的就是&lt;code&gt;vmkfstools&lt;/code&gt;这个工具。&lt;/p&gt;
&lt;p&gt;这个工具用起来思路也非常清晰：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**用&lt;code&gt;-i&lt;/code&gt;来指定输入的虚拟磁盘。**这里注意是那个后缀最短的&lt;code&gt;vmdk&lt;/code&gt;，如果是多文件的话，这个文件包含了一个虚拟磁盘使用的所有文件清单。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用&lt;code&gt;-K&lt;/code&gt;压缩现有的精简置备磁盘。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用&lt;code&gt;-d thin&lt;/code&gt;指定创建一个精简置备磁盘&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后跟上你要输出的磁盘名字，跟不跟都行，看参数。&lt;/p&gt;
&lt;h2 class="relative group"&gt;坑点
&lt;div id="坑点" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9d%91%e7%82%b9" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;本来这块硬盘空间就不剩多少了，所以我开始直接根据厚置备的磁盘生成一个精简置备的磁盘。因为按照正常的逻辑，生成的精简置备磁盘大小只有十几 G，那么我现有的空间还是够的。&lt;/p&gt;
&lt;p&gt;结果失败了，试了好几次，都不行。（&lt;em&gt;&lt;strong&gt;坑点 1&lt;/strong&gt;&lt;/em&gt;）&lt;/p&gt;
&lt;p&gt;我决定把磁盘搞到电脑上面，用 WorkStation 来转换。转换也成功，生成的磁盘是我需要的精简类型，占用正常。然后我就转移回 ESXi 上面，结果导入失败。&lt;/p&gt;
&lt;p&gt;ESXi 不支持多文件的磁盘格式，这台是 7.0。可能旧的确实不可以吧？（&lt;em&gt;&lt;strong&gt;坑点 2&lt;/strong&gt;&lt;/em&gt;）&lt;/p&gt;
&lt;p&gt;我又创建了一个单文件的精简置备移回去，这时候我正好删除了其他不要的虚拟机，总空间&amp;gt;50G 了，传上去了我也没管这台是否占用了这么大的空间。&lt;/p&gt;
&lt;p&gt;然后我觉得这样太费事了，怎么不能在主机上面直接压缩？接着我就使用那个命令行工具压缩，但是压了好几次，该填零的也用&lt;code&gt;dd/sdelete&lt;/code&gt;填充了，但是使用&lt;code&gt;ls&lt;/code&gt;发现显示的还是 50G！&lt;/p&gt;
&lt;p&gt;我以为这是 bug，但是真正的问题在&lt;code&gt;ls&lt;/code&gt;和&lt;code&gt;vmfs&lt;/code&gt;上。（&lt;em&gt;&lt;strong&gt;坑点 3&lt;/strong&gt;&lt;/em&gt;）&lt;/p&gt;
&lt;p&gt;众所周知，Android 上使用 KernelSU 的时候，有的人会看到自己的手机显示成使用 2T 的存储，但是事实上很多人根本没有 2T 的手机，这是因为稀疏文件（Sparse file）造成的。使用 ls 列出的就是 2T，我估计手机厂商获取的时候也是用类似的方法做统计，所以闹出了这一出。&lt;/p&gt;
&lt;p&gt;其实还有别的例子，比如说 btrfs 就不能用&lt;code&gt;du&lt;/code&gt;正确显示，就是因为文件系统的各种特性导致的。&lt;/p&gt;
&lt;p&gt;所以说，其实我使用&lt;code&gt;-K&lt;/code&gt;压缩，结果是成功的，而且使用&lt;code&gt;du&lt;/code&gt;也可以直接看到，ESXi 的状态面板也可以直接看到存储被释放了。但是我怎么在这个坑上面找问题，找了那么久，唉唉唉&lt;/p&gt;</description></item><item><title>防火墙：iptables、nftables 与 firewalld</title><link>https://minortex.github.io/posts/firewall-1-iptables-nftables/</link><pubDate>Fri, 25 Apr 2025 21:46:16 +0800</pubDate><guid>https://minortex.github.io/posts/firewall-1-iptables-nftables/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;其实防火墙这玩意，我三年前就接触过。那时候在用 iptables 来配置路由器的&lt;code&gt;ipv6&lt;/code&gt;转发，照着教程抄完了也不知所云。&lt;/p&gt;
&lt;p&gt;如今过去了那么久，知识储备多了那么一点点，正好有需求，就重新了解了一下。&lt;/p&gt;
&lt;h2 class="relative group"&gt;iptables
&lt;div id="iptables" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#iptables" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;iptables，这玩意算是个古董了，大概在 21 世纪初就有了。不过毕竟是老东西，现在大部分旧设备都是用的它，很多软件在修改防火墙规则的时候也只改它。&lt;/p&gt;
&lt;p&gt;提起&lt;code&gt;iptables&lt;/code&gt;，我们也许会想到五链四表，不过一般来说，知道&lt;code&gt;nat&lt;/code&gt;和&lt;code&gt;filter&lt;/code&gt;表就已经足够完成 80% 的工作了（&lt;code&gt;ipv4&lt;/code&gt;），我在&lt;a
href="https://wiki.archlinuxcn.org/wiki/Iptables"
target="_blank"
&gt;archwiki 的 iptables&lt;/a&gt;章节翻到这张图，也许能够有助于理解：&lt;/p&gt;
&lt;div class="no-limit-code"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXXXXXXXXXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXX Network XXX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXXXXXXXXXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-------------+ +------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |table: filter| &amp;lt;---+ | table: nat |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |chain: INPUT | | | chain: PREROUTING|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-----+-------+ | +--------+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v | v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [local process] | **************** +--------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | +---------+ Routing decision +------&amp;gt; |table: filter |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v **************** |chain: FORWARD|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**************** +------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Routing decision |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**************** |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v **************** |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-------------+ +------&amp;gt; Routing decision &amp;lt;---------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|table: nat | | ****************
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|chain: OUTPUT| | +
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----+-------+ | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | | v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v | +-------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+--------------+ | | table: nat |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|table: filter | +----+ | chain: POSTROUTING|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|chain: OUTPUT | +--------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+--------------+ |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXXXXXXXXXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXX Network XXX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; XXXXXXXXXXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这里的 Network，指的就是不同的网卡接口。&lt;/p&gt;
&lt;p&gt;接着我来说说自己的理解：&lt;/p&gt;
&lt;h3 class="relative group"&gt;啥表啥链？
&lt;div id="啥表啥链" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%95%a5%e8%a1%a8%e5%95%a5%e9%93%be" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;filter&lt;/strong&gt;表 最简单，也是默认的表。它只有&lt;code&gt;OUTPUT&lt;/code&gt;，&lt;code&gt;INPUT&lt;/code&gt;，&lt;code&gt;FORWARD&lt;/code&gt;链。这几个顾名思义，分别是来源本机，目标本机，经过本机的数据包。
一般来说，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OUTPUT&lt;/strong&gt; 的默认规则是&lt;code&gt;ACCEPT&lt;/code&gt;，也就是允许所有来源本机的数据包发出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;INPUT&lt;/strong&gt; 的默认规则是&lt;code&gt;DROP&lt;/code&gt;，也就是不允许外来的数据包访问本机。我们一般监听的端口需要配置例外的规则，否则就无法被访问。&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;FORWARD&lt;/em&gt; *也是&lt;code&gt;DROP&lt;/code&gt;。默认不允许不同网络接口之间的数据包转发。想要转发不仅仅需要手动放行，还得开启内核的 IP 转发。
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 临时启用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sysctl -w net.ipv4.ip_forward&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 永久启用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;net.ipv4.ip_forward=1&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee -a /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sysctl -p
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;举个例子：
&lt;pre tabindex="0"&gt;&lt;code&gt;Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1 52 ACCEPT all -- wg0 * 0.0.0.0/0 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;nat&lt;/strong&gt;表 这个表有四条链，不过我一般只用其中的两条：&lt;code&gt;PREROUTING&lt;/code&gt;和&lt;code&gt;POSTROUTING&lt;/code&gt;，分别处理端口映射以及 ip 伪装。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PREROUTING&lt;/strong&gt; 这一个链其实我接触得不多，因为基本都是在 ui 上面设置的。但是摸过 SAST 的 RouterOS 之后，那个 ui 配置起来就像是手搓&lt;code&gt;iptables&lt;/code&gt;一样，我就马上理解了。
一个例子就像这样：
&lt;pre tabindex="0"&gt;&lt;code&gt;Chain PREROUTING (policy ACCEPT 2885K packets, 212M bytes)
pkts bytes target prot opt in out source destination
16 952 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22345 to:192.168.123.2:22
## 这里因为涉及到自定义链，我就直接合并了。
&lt;/code&gt;&lt;/pre&gt;你会注意到，&lt;code&gt;policy&lt;/code&gt;为什么是&lt;code&gt;ACCEPT&lt;/code&gt;？我觉得是因为如果不给你规则，路由也不会帮你转发，所以大家都说&lt;code&gt;ipv4&lt;/code&gt;的&lt;code&gt;nat&lt;/code&gt;安全，原来是这个意思。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;POSTROUTING&lt;/strong&gt; 你是否想过，&lt;code&gt;nat&lt;/code&gt;后的主机，是如何用路由器的 ip 进行通信的？
这条链改写了对应目标的源 ip 地址和端口，然后再发出去。规则 SNAT 需要指定 ip，但是在家庭环境中，ip 通常是变化的，这时候神器&lt;code&gt;MASQUERADE&lt;/code&gt;出现了，它可以动态的获取出口网卡的 ip 地址，把 ip 改写再发出去。至于出口怎么决定，那就是路由表的事情了。
例子：
&lt;pre tabindex="0"&gt;&lt;code&gt;Chain POSTROUTING (policy ACCEPT 804K packets, 60M bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * ppp0 0.0.0.0/0 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 class="relative group"&gt;命令
&lt;div id="命令" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%91%bd%e4%bb%a4" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt;的命令非常的精简，看多了倒还不错，不过现在的风格倒变成自然语言了，像&lt;code&gt;ip&lt;/code&gt;和&lt;code&gt;nft&lt;/code&gt;之流。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -t nat -nvL &lt;span class="o"&gt;[&lt;/span&gt;CHAIN-NAME&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;#看 nat 表的规则，链名是可选的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -nvL --line-numbers &lt;span class="c1"&gt;#看 filter 表的规则，filter 默认可以省略；--line-numbers 用于显示编号。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一些我常用的就一起放在这了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip6tables -A INPUT -p udp --dport &lt;span class="m"&gt;26741&lt;/span&gt; -j ACCEPT &lt;span class="c1"&gt;# ip6tables 是用于控制 ipv6 的防火墙，我目前只接触了 filter 表，NPT 还不会...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -A INPUT -i wg0 -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -A FORWARD -i wg0 -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -t nat -A POSTROUTING -o ppp0 -s 10.0.8.0/24 -j MASQUERADE --mode fullcone
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-A&lt;/code&gt;是&lt;code&gt;Append&lt;/code&gt;，&lt;code&gt;-I&lt;/code&gt;是&lt;code&gt;Insert&lt;/code&gt;，&lt;code&gt;-D&lt;/code&gt;是&lt;code&gt;Delete&lt;/code&gt;，据此可以精确的删除规则。&lt;/p&gt;
&lt;p&gt;用行号也可以：&lt;code&gt;iptables -D INPUT 2&lt;/code&gt;&lt;/p&gt;
&lt;h3 class="relative group"&gt;补充
&lt;div id="补充" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a1%a5%e5%85%85" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;还有子链和其他表等其他内容，等到后面有时间再写吧。&lt;/p&gt;
&lt;h2 class="relative group"&gt;nftables + firewalld
&lt;div id="nftables--firewalld" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nftables--firewalld" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;nftables&lt;/code&gt;是一个新的&lt;code&gt;netfilter&lt;/code&gt;工具，&lt;code&gt;firewalld&lt;/code&gt;是 RedHat 开发的一个防火墙前端。&lt;code&gt;firewalld&lt;/code&gt;的默认后端是&lt;code&gt;nft&lt;/code&gt;，这两者一般来说会配合起来使用。&lt;/p&gt;
&lt;p&gt;直接操作底层的&lt;code&gt;nft&lt;/code&gt;命令对我来说还是有点困难，这里就简单讲讲他们的结合使用吧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么选择 firewalld 而不是选择 ufw 呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;firewalld&lt;/code&gt;对于动态网络（比如笔记本在不同的热点之间切换）有很好的适配，具体体现在与&lt;code&gt;NetworkManager&lt;/code&gt;之间的配合，使得不同的热点能够应用在不同的区域中，后面会讲讲配置过程。&lt;/p&gt;
&lt;h3 class="relative group"&gt;永久和非永久配置
&lt;div id="永久和非永久配置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%b0%b8%e4%b9%85%e5%92%8c%e9%9d%9e%e6%b0%b8%e4%b9%85%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;firewalld&lt;/code&gt;在设置防火墙的时候，默认是临时配置立即生效。如果想要配置永久规则，加上&lt;code&gt;--permanent&lt;/code&gt;参数即可，记得使用&lt;code&gt;firewall-cmd&lt;/code&gt;重新加载规则生效。&lt;/p&gt;
&lt;h3 class="relative group"&gt;启动
&lt;div id="启动" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%90%af%e5%8a%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;一行命令就好：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now nftables firewalld
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样，系统就为我们配置了一个默认的防火墙，默认所有的网卡会在&lt;code&gt;public&lt;/code&gt;区域，现在只有 22 入站才被允许。&lt;/p&gt;
&lt;h3 class="relative group"&gt;区域
&lt;div id="区域" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8c%ba%e5%9f%9f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;你应该发现，新的防护墙工具多了一个新概念叫作区域，不同的网卡可以分配到不同的区域。&lt;/p&gt;
&lt;p&gt;先来看看有什么区域？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewalld --list-all-zones
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部分输出：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;...
home
target: default
ingress-priority: 0
egress-priority: 0
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client mdns samba-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
...
public (default, active)
target: default
ingress-priority: 0
egress-priority: 0
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
trusted
target: ACCEPT
ingress-priority: 0
egress-priority: 0
icmp-block-inversion: no
interfaces:
sources:
services:
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果设置了&lt;code&gt;tun&lt;/code&gt;模式，你会发现它无法联网了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;将网卡分配到区域
&lt;div id="将网卡分配到区域" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b0%86%e7%bd%91%e5%8d%a1%e5%88%86%e9%85%8d%e5%88%b0%e5%8c%ba%e5%9f%9f" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;分配 tun
&lt;div id="分配-tun" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%86%e9%85%8d-tun" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;我们使用&lt;code&gt;firewalld&lt;/code&gt;来配置规则。&lt;/p&gt;
&lt;p&gt;通过上面的输出可以知道，&lt;code&gt;trusted&lt;/code&gt;区域是允许所有流量的，我们把&lt;code&gt;Mihomo&lt;/code&gt;加到&lt;code&gt;trusted&lt;/code&gt;区域：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;trusted --add-interface&lt;span class="o"&gt;=&lt;/span&gt;Mihomo --permanent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 验证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;firewall-cmd --get-zone-of-interface&lt;span class="o"&gt;=&lt;/span&gt;Mihomo &lt;span class="c1"&gt;## 返回 trusted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里有个小坑，我加入之后发现还是不行，一看日志发现数据包全被丢弃了。&lt;del&gt;折腾半天发现重启解决了，我：？？？&lt;/del&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;真正的隐藏 boss 在后面！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a
href="#rpfilter"&gt;反向路径过滤&lt;/a&gt;&lt;/p&gt;
&lt;h4 class="relative group"&gt;分配 wlan0
&lt;div id="分配-wlan0" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%88%86%e9%85%8d-wlan0" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;如果是便携的设备，我们会连接到不同的热点，有公用的也有家里的，家里可以开放多一点权限，而公用的则不需要。&lt;/p&gt;
&lt;p&gt;这一部分由 NetworkManager 配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli connection modify &lt;span class="s2"&gt;&amp;#34;SAST&amp;#34;&lt;/span&gt; connection.zone &lt;span class="s2"&gt;&amp;#34;home&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 验证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection show SAST &lt;span class="p"&gt;|&lt;/span&gt; grep connection.zone
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 返回&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;connection.zone: home
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样只有连接到指定的热点名称的时候，才会切换 wlan0 到 home 区域，其他都是 public 区域。&lt;/p&gt;
&lt;h3 class="relative group"&gt;开放特定服务和端口
&lt;div id="开放特定服务和端口" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%80%e6%94%be%e7%89%b9%e5%ae%9a%e6%9c%8d%e5%8a%a1%e5%92%8c%e7%ab%af%e5%8f%a3" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;firewalld 事先定义了一些服务需要的端口，可以在&lt;code&gt;/usr/lib/firewalld/services/&lt;/code&gt;找到，这些配置文件以&lt;code&gt;xml&lt;/code&gt;格式存储。&lt;/p&gt;
&lt;p&gt;添加服务和端口的时候，别忘了指定当前区域。如果不指定，那么默认是在 public 区域添加规则的。&lt;/p&gt;
&lt;p&gt;如果你需要的服务恰好在里面，就可以很方便的添加：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;home --add-service&lt;span class="o"&gt;=&lt;/span&gt;kdeconnect --permanent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这个端口是你自己定义的，可以这样子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --add-port&lt;span class="o"&gt;=&lt;/span&gt;8000/tcp --zone&lt;span class="o"&gt;=&lt;/span&gt;home --permanent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后查看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;home --list-all
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;## 输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;home &lt;span class="o"&gt;(&lt;/span&gt;active&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; target: default
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ingress-priority: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; egress-priority: &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; icmp-block-inversion: no
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interfaces: wlan0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sources:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; services: dhcpv6-client kdeconnect mdns samba-client ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ports: 8000/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; protocols:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; forward: yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; masquerade: no
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; forward-ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source-ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; icmp-blocks:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rich rules:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;反向路径过滤
&lt;div id="rpfilter" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#rpfilter" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;反向路径过滤，简称 rpfilter。这一个功能在 ipv4 时代，是由内核实现的。在&lt;a
href="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt"
target="_blank"
&gt;内核文档&lt;/a&gt;中是这么写的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/proc/sys/net/ipv4/* Variables:
rp_filter - INTEGER
0 - No source validation.
1 - Strict mode as defined in RFC3704 Strict Reverse Path
Each incoming packet is tested against the FIB and if the interface
is not the best reverse path the packet check will fail.
By default failed packets are discarded.
2 - Loose mode as defined in RFC3704 Loose Reverse Path
Each incoming packet&amp;#39;s source address is also tested against the FIB
and if the source address is not reachable via any interface
the packet check will fail.
Current recommended practice in RFC3704 is to enable strict mode
to prevent IP spoofing from DDos attacks. If using asymmetric routing
or other complicated routing, then loose mode is recommended.
The max value from conf/{all,interface}/rp_filter is used
when doing source validation on the {interface}.
Default value is 0. Note that some distributions enable it
in startup scripts.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;顺带一提，&lt;code&gt;ip_forward&lt;/code&gt;也是在这个地方配置的。&lt;/p&gt;
&lt;p&gt;这里告诉我们，&lt;code&gt;0&lt;/code&gt;禁用反向路径过滤，&lt;code&gt;1&lt;/code&gt;开启严格的反向路径过滤（反向路径必须是最佳路由），&lt;code&gt;2&lt;/code&gt;开启宽松的反向路径过滤（只要反向路径路由可达即可）。&lt;/p&gt;
&lt;p&gt;默认是不开启反向过滤的，但是似乎防火墙会给每个网卡加上一个&lt;code&gt;2&lt;/code&gt;？由于目前手上没有无防火墙的机器，暂时无法验证&amp;hellip;&lt;/p&gt;
&lt;p&gt;但是如果你装的是&lt;code&gt;firewalld&lt;/code&gt;，默认的反向过滤是全部严格的。这时得到&lt;code&gt;/etc/firewalld/firewalld.conf&lt;/code&gt;改成这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;IPv6_rpfilter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;loose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;才能禁用反向路径过滤。由于&lt;code&gt;firewalld&lt;/code&gt;在 nft 中配置的表是只读的，只能由它改写，所以去删掉它的规则或者尝试绕过都是没用的。&lt;del&gt;别骂了别骂了&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Mihomo&lt;/code&gt;会导致出现很多的&lt;code&gt;drop&lt;/code&gt;数据包，其中一部分就是&lt;code&gt;rpfilter&lt;/code&gt;引起的，日志类似这样：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;5月 18 23:18:08 texsd-spin kernel: rpfilter_DROP: IN=Mihomo OUT= MAC= SRC=2a01:04f9:3081:4e4b:0000:0000:0000:0002 DST=fdfe:dcba:9876:0000:0000:0000:0000:0001 LEN=80 TC=0 HOPLIMIT=64 FLOWLBL=1037094 PROTO=TCP SPT=443 DPT=22000 WINDOW=64260 RES=0x00 ACK SYN URGP=0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;导致所有的&lt;code&gt;ipv6&lt;/code&gt;能&lt;code&gt;ping&lt;/code&gt;通但是无法访问，改成宽松后解决。&lt;/p&gt;
&lt;h3 class="relative group"&gt;管理
&lt;div id="管理" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ae%a1%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;禁用日志
&lt;div id="禁用日志" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%a6%81%e7%94%a8%e6%97%a5%e5%bf%97" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;这是一个标准选项，所以不需要加&lt;code&gt;--permanent&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我比较建议把日志调成&lt;code&gt;unicast&lt;/code&gt;。否则如果你局域网中广播的设备很多的话，&lt;code&gt;journalctl&lt;/code&gt;会被淹没。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --set-log-denied&lt;span class="o"&gt;=&lt;/span&gt;unicast
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 class="relative group"&gt;其他
&lt;div id="其他" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%b6%e4%bb%96" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;我觉得&lt;code&gt;firewalld&lt;/code&gt;的 man page 写得很清晰，去看它是一个很好的选择。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;man &lt;span class="m"&gt;1&lt;/span&gt; firewall-cmd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 class="relative group"&gt;总结
&lt;div id="总结" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%80%bb%e7%bb%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;目前来说个人主机拥抱 firewalld + nftables 更加合适，因为它们提供了更灵活的规则，也拥有更高效的性能。但是服务器上不建议安装 firewalld，因为很多软件是直接通过添加 nftables 规则的，还有很大一部分老软件会添加 iptables 规则。当然 nftables 也做了兼容，&lt;code&gt;iptables-nft&lt;/code&gt;这个包会把 iptables 类的指令自动翻译成 nftables 规则进行加载。此外，&lt;code&gt;iptables-translate&lt;/code&gt;可以把 iptables 指令翻译成 nftables，便于学习和迁移。&lt;/p&gt;
&lt;p&gt;nftables 是未来的防火墙！&lt;del&gt;但是目前直接使用的真的太少了&lt;/del&gt;&lt;/p&gt;</description></item><item><title>字形、字体以及我的配置</title><link>https://minortex.github.io/posts/fonts-settings/</link><pubDate>Fri, 18 Apr 2025 23:38:19 +0800</pubDate><guid>https://minortex.github.io/posts/fonts-settings/</guid><description>&lt;p&gt;终于受不了终端里那丑得要死的宋体了，开干！XML？拿来把你！&lt;/p&gt;
&lt;h2 class="relative group"&gt;衬线，无衬线，等宽
&lt;div id="衬线无衬线等宽" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a1%ac%e7%ba%bf%e6%97%a0%e8%a1%ac%e7%ba%bf%e7%ad%89%e5%ae%bd" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;这大概是三种最常见的字形了。&lt;/p&gt;
&lt;h3 class="relative group"&gt;衬线（Serif)
&lt;div id="衬线serif" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%a1%ac%e7%ba%bfserif" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;简单来说，就是字体有小学练字的时候的老师要求你有的那种笔锋。&lt;/p&gt;
&lt;p&gt;对于英文版来说，很常见的是&lt;code&gt;Times New Roman&lt;/code&gt; ，需要注意的是这是一个版权字体。&lt;/p&gt;
&lt;p&gt;而对于中文版，衬线字体一般跟宋体都是同一个意思了。不过名字还是会有区分：宋体，仿宋（这个公文用的很多）,楷体，这些字体的版权都是归属于中易中标，得小心。还有一部分宋体是方正的，名字就是方正 xx。&lt;/p&gt;
&lt;h3 class="relative group"&gt;无衬线（Sans-Serif）
&lt;div id="无衬线sans-serif" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%97%a0%e8%a1%ac%e7%ba%bfsans-serif" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;人们发现，衬线字体虽然打印出来显示效果不错，但是在屏幕上，每个字是由像素点组成的，在早期分辨率差的屏幕上，衬线字体的各种装饰部分显示效果也很差。于是就出现了无衬线的字体，简化了装饰部分，使得在屏幕上更易读。&lt;/p&gt;
&lt;p&gt;中文，微软雅黑，算是一个历史包袱非常重的字体了，在 windows 上几乎是独占，效果也比其他字体好，后面还出现了一个等线，在雅黑的基础上简化了。微软名字命名的，优化好点也正常吧（？但是这是微软的小动作！&lt;/p&gt;
&lt;p&gt;英文，Arial，这个字体名字不怎么出名，但是你在显示器上看到的，基本都是它。&lt;/p&gt;
&lt;h3 class="relative group"&gt;等宽（monospace）
&lt;div id="等宽monospace" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ad%89%e5%ae%bdmonospace" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;这一种字体是专为代码显示设计的，因为缩进的时候能够更好的对齐，在终端中，也能让 Tab 更好的对齐；而且易于分辨&lt;code&gt;I l&lt;/code&gt;以及&lt;code&gt;O 0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我感觉中文和英文的这些名字，常人应该很少能听到吧？只怪 Windows 和生态的软件把用户驯化得太好了，如果不是用 Arch，我可能也不会去了解这些内容。&lt;/p&gt;
&lt;p&gt;你问我现在用的什么字体呢？&lt;code&gt;FiraMono Nerd Font&lt;/code&gt;以及&lt;code&gt;Noto Sans Mono CJK SC&lt;/code&gt;，这两种字体都是开源的，接下来讲一下他们。&lt;/p&gt;
&lt;h2 class="relative group"&gt;开源字体
&lt;div id="开源字体" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bc%80%e6%ba%90%e5%ad%97%e4%bd%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;对于英文字体来说，有一套字体叫做&lt;code&gt;liberation&lt;/code&gt;，全面实现了对 Windows 上面的常见字体的兼容，这套字体是红帽开发的。&lt;/p&gt;
&lt;p&gt;而对于中文，则是思源/Noto&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么要 Adobe 和 Google 要开发思源字体/Noto CJK 呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Google 在 Android 4.4 之前使用的字体问题很多，不支持的字符有 fallback，导致显示效果不一致。而 Adobe 的软件使用字体会造成版权争议，同时有自己的字体部门。两家公司一拍即合，由 Google 出资、提供建议，Adobe 设计字体，最终出现了这套字体。这套字体可以说是开源的大胜利了，后面还有以此衍生的更纱黑体。&lt;/p&gt;
&lt;p&gt;还有别的开源字体，霞鹜文楷，得意黑什么的，就不一一介绍了。&lt;/p&gt;
&lt;p&gt;值得一提的是，这不是第一套中文的开源字体。在 AUR 上你还能看到文泉驿这款字体，至今有二十年了。虽然它的名字很诗意，但是在 Noto 之后，已经鲜有人用了，也没人维护了。&lt;/p&gt;
&lt;p&gt;思源黑体极大的提高了 Linux 图形界面上面的字体显示体验，不过我早期使用的时候没注意，从 AUR 下载了 Windows 的兼容字体，使得终端一直在随意 fallback 一些奇怪的字体（捂脸），最近才决定了解一下，从而有了这篇东西。&lt;/p&gt;
&lt;h2 class="relative group"&gt;系统字体调优
&lt;div id="系统字体调优" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%b3%bb%e7%bb%9f%e5%ad%97%e4%bd%93%e8%b0%83%e4%bc%98" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;Arch
&lt;div id="arch" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#arch" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;管理字体，现代的 Linux 系统基本都是 fontconfig 了。我们主要是用&lt;code&gt;fc-cache&lt;/code&gt;,&lt;code&gt;fc-match&lt;/code&gt;两个命令。&lt;/p&gt;
&lt;p&gt;先看看你的三种字体会匹配什么？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fc-match serif &lt;span class="c1"&gt;# 添加-a 参数可以显示依次匹配的字体&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fc-match sans-serif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fc-match monospace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我的是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;texsd@texsd-spin ~&lt;span class="o"&gt;]&lt;/span&gt;$ fc-match sans-serif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NotoSansCJK-Regular.ttc: &lt;span class="s2"&gt;&amp;#34;Noto Sans CJK SC&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Regular&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;texsd@texsd-spin ~&lt;span class="o"&gt;]&lt;/span&gt;$ fc-match serif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NotoSerifCJK-Regular.ttc: &lt;span class="s2"&gt;&amp;#34;Noto Serif CJK SC&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Regular&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;texsd@texsd-spin ~&lt;span class="o"&gt;]&lt;/span&gt;$ fc-match monospace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;FiraMonoNerdFontMono-Regular.otf: &lt;span class="s2"&gt;&amp;#34;FiraMono Nerd Font Mono&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Regular&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果群魔乱舞的话，还是先去装一个&lt;code&gt;noto-cjk&lt;/code&gt;包吧。&lt;/p&gt;
&lt;p&gt;安装字体包在&lt;code&gt;/usr/share/fonts&lt;/code&gt;目录，安装时会自动执行&lt;code&gt;fc-cache -fv&lt;/code&gt;来刷新字体缓存，你也可以手动来刷新它们。&lt;/p&gt;
&lt;p&gt;我还额外安装了&lt;code&gt;otf-firamono-nerd&lt;/code&gt;来实现 shell 的 emoji 图标，记住不！要！安！装！nerd-fonts 包集！那总共有&lt;code&gt;7GiB+&lt;/code&gt;！&lt;/p&gt;
&lt;p&gt;我是强烈推荐去修改默认字体配置文件的，虽然 xml 的可读性确实不怎么样，但是根据别人的改嘛，查找替换也不算太难。这里就贴一下我的方案吧：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.config/fontconfig/conf.d/99-notocjk.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#39;1.0&amp;#39;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE fontconfig SYSTEM &amp;#39;urn:fontconfig:fonts.dtd&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 配置黑体--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;compare=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;contains&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;lang&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;zh&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;qual=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;any&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;sans-serif&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;binding=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;strong&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prepend&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Sans CJK SC&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 配置宋体--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;compare=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;contains&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;lang&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;zh&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;qual=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;any&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;serif&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;binding=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;strong&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prepend&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Serif CJK SC&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;qual=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;any&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;monospace&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;binding=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;strong&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prepend&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 这里等宽配置了两个字体，原因是FiraMono没有中文的等宽字体，下面写上Noto的等宽字体就可以自动fallback了--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;FiraMono Nerd Font Mono&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Sans Mono CJK SC&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dir&amp;gt;&lt;/span&gt;~/.local/share/fonts&lt;span class="nt"&gt;&amp;lt;/dir&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;懒得在 kde 里面设置的话，可以直接把这个放到 conf.d:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#39;1.0&amp;#39;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE fontconfig SYSTEM &amp;#39;urn:fontconfig:fonts.dtd&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; Artificial oblique for fonts without an italic or oblique version
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- check to see if the font is roman --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;slant&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;roman&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- check to see if the pattern requested non-roman --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;compare=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;not_eq&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;slant&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;roman&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- multiply the matrix to slant the font --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;matrix&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;times&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;matrix&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;matrix&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;double&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/double&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;double&amp;gt;&lt;/span&gt;0.2&lt;span class="nt"&gt;&amp;lt;/double&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;double&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/double&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;double&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/double&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/matrix&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/times&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- pretend the font is oblique now --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;slant&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;oblique&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- and disable embedded bitmaps for artificial oblique --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;embeddedbitmap&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;bool&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/bool&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; Synthetic emboldening for fonts that do not have bold face available
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- check to see if the weight in the font is less than medium which possibly need emboldening --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;compare=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;less_eq&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;medium&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- check to see if the pattern requests bold --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;compare=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;more_eq&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;bold&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; set the embolden flag
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; needed for applications using cairo, e.g. gucharmap, gedit, ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;embolden&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;bool&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/bool&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; set weight to bold
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; needed for applications using Xft directly, e.g. Firefox, ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;bold&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;rgba&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;none&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hinting&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;bool&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/bool&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hintstyle&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;const&amp;gt;&lt;/span&gt;hintnone&lt;span class="nt"&gt;&amp;lt;/const&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dir&amp;gt;&lt;/span&gt;~/.fonts&lt;span class="nt"&gt;&amp;lt;/dir&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;match&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;edit&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;antialias&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;bool&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/bool&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;总共三段内容，分别对应衬线，无衬线，等宽，按照自己的喜好修改即可。不知道字体名字可以用&lt;code&gt;fc-match -a&lt;/code&gt;查看引号内的字体名称，或者看桌面设置里面的字体选项。&lt;/p&gt;
&lt;h3 class="relative group"&gt;Win
&lt;div id="win" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#win" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;Windows 中文版默认是使用微软雅黑的，修改需要改注册表。所以一般来说我们都会在软件里面单独修改，像 Firefox，Windows Terminal，VSCode 都支持。而其他的应用我选择用微软雅黑，因为 Windows 运行的旧应用很多都是硬编码微软雅黑的，调整他们会导致一些奇怪的问题，下面说说。&lt;/p&gt;
&lt;p&gt;这是个版权字体（属于方正）正因为如此应该不少人吃了官司吧（？&lt;/p&gt;
&lt;h2 class="relative group"&gt;点阵字体和矢量字体
&lt;div id="点阵字体和矢量字体" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%82%b9%e9%98%b5%e5%ad%97%e4%bd%93%e5%92%8c%e7%9f%a2%e9%87%8f%e5%ad%97%e4%bd%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;点阵字体是以前低分辨率的时代使用的，现在已经基本淘汰了。但是你可能偶尔还能看到有人发那些锯齿感字体非常严重的图片，那大概就是用 xp 截图出来的。&lt;/p&gt;
&lt;p&gt;现代系统基本都是矢量字体，这些字体是通过贝塞尔曲线画出来的。Windows 为了兼容旧的程序和低分屏，要求字体都要支持 Hinting。而自带的微软雅黑对此有优化，别的字体就不太行。但是微软雅黑尽力了，没法避免 100% 下的字体发虚。所以还是换 4K 屏吧，200% 的比例放大下，字体渲染还是可以的。&lt;/p&gt;
&lt;p&gt;想了解这篇细节，可以看看&lt;a
href="https://www.bilibili.com/opus/856322865719410692"
target="_blank"
&gt;Windows 的字体渲染的一些鸟事&lt;/a&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;参考
&lt;div id="参考" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8f%82%e8%80%83" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a
href="https://github.com/oodzchen/dotfiles/blob/main/dot_config/fontconfig/fonts.conf"
target="_blank"
&gt;https://github.com/oodzchen/dotfiles/blob/main/dot_config/fontconfig/fonts.conf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>我的 ArchLinux 折腾记录</title><link>https://minortex.github.io/posts/customizing-my-own-archlinux/</link><pubDate>Mon, 09 Dec 2024 15:42:53 +0800</pubDate><guid>https://minortex.github.io/posts/customizing-my-own-archlinux/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;暑假的时候在我的掠夺者·擎 neo 上面第一次纯手动安装了 Arch，过程很有意思，但是后面烂到家的 NV 驱动让我受不了。&lt;/p&gt;
&lt;p&gt;我在 win 下喜欢开独显直连模式，但是在 arch 下就有很大问题：每次睡眠，内建显示器的亮度都会自动开到最大。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;我试过很多办法，都很难实现记忆之前的亮度。只有在 BIOS 里面开启混合模式才会出现 intel_backlight，这真是太折磨了。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;最后，我的评价是，别在日用的电脑上面用 n 卡和 linux！&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;其实是有办法的，给睡眠加个钩子保存亮度，然后唤醒的时候读取文件并还原亮度。也是勉强能用的。（果然水平提高了就有新办法&lt;/p&gt;
&lt;h2 class="relative group"&gt;物色新机
&lt;div id="物色新机" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%89%a9%e8%89%b2%e6%96%b0%e6%9c%ba" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;作为一个穷学生，已经花了大几千实现臭打游戏的目标，无论怎么说都不太好再拿出几千买电脑。开始我把目标锁定在 3k 左右，然后降到 2k，最后 1k。&lt;/p&gt;
&lt;p&gt;正好这段时间有国补，就在看鸡哥的 14x，但是各种说品控差让我望而却步。后面又看火影的 6800 二手，感觉周边又差一点。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;怎么，你又开始后悔没有买 6800 以上的 u 了？！其实只是舍不得出那么多钱罢了！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;几番挑选，我决定捡洋垃圾，目光投向了 chromebook。这样，就有了这篇文章的主角：Acer Spin713 二代 (cp713-2w)。在海鲜市场的 js 以 1050 的价格拿下。&lt;/p&gt;
&lt;p&gt;配置是 i5-10210u，马甲什么的我不是很在意。我在意的是这块屏幕好像是夏老师之前提到的&lt;a
href="https://zhuanlan.zhihu.com/p/570757067"
target="_blank"
&gt;网格纱窗屏&lt;/a&gt;，凑近看确实观感不太好，其实，我根本不需要触摸屏的，但是换不得呀！&lt;/p&gt;
&lt;p&gt;默念 1050&amp;hellip;默念 1050&amp;hellip;默念 1050&amp;hellip;&lt;/p&gt;
&lt;h2 class="relative group"&gt;安装过程
&lt;div id="安装过程" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%89%e8%a3%85%e8%bf%87%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;用 Arch 怎么能不手动安装呢？&lt;/p&gt;
&lt;p&gt;但是 ArchInstall 真的太香了！连好网络分好区，设置好一些东西，直接重启就看到 sddm 了。&lt;/p&gt;
&lt;p&gt;然而，像装黑苹果一样，大家都是装完了然后开系统信息截屏。装完 Arch 你也可以&lt;code&gt;fastfetch&lt;/code&gt;，然而，这一切仅仅是个开始&amp;hellip;&lt;/p&gt;
&lt;h3 class="relative group"&gt;输入法设置
&lt;div id="输入法设置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%be%93%e5%85%a5%e6%b3%95%e8%ae%be%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;首先得有输入法吧？&lt;/p&gt;
&lt;p&gt;我用的是 fcitx5 + rime + 雾凇拼音的组合，最终效果还不错。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先安装 fcitx5、rime 和雾凇拼音：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;paru -S fcitx5 fcitx5-rime rime-ice-git &lt;span class="c1"&gt;## 可以用archlinuxcn源的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;根据&lt;a
href="https://github.com/iDvel/rime-ice"
target="_blank"
&gt;雾凇拼音的配置方案&lt;/a&gt;，把这段配置加入 rime 输入法：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# $HOME/.local/share/fcitx5/rime/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 仅使用「雾凇拼音」的默认配置，配置此行即可&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;__include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rime_ice_suggestion:/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 以下根据自己所需自行定义，仅做参考。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 针对对应处方的定制条目，请使用 &amp;lt;recipe&amp;gt;.custom.yaml 中配置，例如 rime_ice.custom.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;__patch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key_binder/bindings/+&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 开启逗号句号翻页&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- {&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when: paging, accept: comma, send&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Page_Up }&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- {&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when: has_menu, accept: period, send&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Page_Down }&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;
&lt;p&gt;进入设置里面，输入与输出 - 键盘 - 虚拟键盘把 fcitx5 打开，这时候才会在语言和时间下面出现输入法。如果因为 kde 的 bug 出不来的话，重启或者重装吧。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输入法 - 添加输入法，选择中州韵。这里建议点击中州韵的配置，然后把“切换输入法的行为”修改成“提交原始字符串”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本完成了。不过如果没有配置字体，会导致 emoji 显示空白。可以参考我的&lt;a
href="https://blog.texsd.eu.org/p/%E5%AD%97%E5%BD%A2%E5%AD%97%E4%BD%93%E4%BB%A5%E5%8F%8A%E6%88%91%E7%9A%84%E9%85%8D%E7%BD%AE/#%E7%B3%BB%E7%BB%9F%E5%AD%97%E4%BD%93%E8%B0%83%E4%BC%98"
target="_blank"
&gt;字体方案&lt;/a&gt;来解决问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果想要让 fcitx5 符合 breeze 的外观，可以安装这个包：&lt;code&gt;fcitx5-breeze&lt;/code&gt;，然后在输入法 - 配置附加组件 - 经典用户界面 - 主题里面修改成 KDE Plasma 来实现。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 class="relative group"&gt;Locale 设置
&lt;div id="locale-设置" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#locale-%e8%ae%be%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;默认在安装的时候大家都会选择&lt;code&gt;en_US.UTF-8&lt;/code&gt;，这是为了避免 tty 不支持中文显示。不过在安装完毕后，一般都会重新编译&lt;code&gt;zh_CN.UTF-8&lt;/code&gt;的语言支持。&lt;/p&gt;
&lt;p&gt;这里我选用了一些特殊的设置方式，而不是直接设置&lt;code&gt;LC_ALL&lt;/code&gt;。原因是让各种工具提示的信息和显示的日志为英文，同时让单位之类的显示符合国内的习惯。&lt;/p&gt;
&lt;p&gt;配置像这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /etc/locale.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;zh_CN.UTF-8 &lt;span class="c1"&gt;# 默认界面语言为中文&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LC_MESSAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8 &lt;span class="c1"&gt;# 强制日志和错误信息用英文&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LC_PAPER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_SG.UTF-8 &lt;span class="c1"&gt;# 纸张尺寸 A4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LC_MEASUREMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_SG.UTF-8 &lt;span class="c1"&gt;# 公制单位（米、升、℃）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LC_TIME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_SG.UTF-8 &lt;span class="c1"&gt;# 24 小时制时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;参考&lt;a
href="https://wiki.archlinuxcn.org/wiki/%E5%AE%89%E8%A3%85%E6%8C%87%E5%8D%97#%E5%8C%BA%E5%9F%9F%E5%92%8C%E6%9C%AC%E5%9C%B0%E5%8C%96%E8%AE%BE%E7%BD%AE"
target="_blank"
&gt;archwikicn-安装教程&lt;/a&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;对 chromebook 的折腾过程
&lt;div id="对-chromebook-的折腾过程" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%af%b9-chromebook-%e7%9a%84%e6%8a%98%e8%85%be%e8%bf%87%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;声音驱动
&lt;div id="声音驱动" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%a3%b0%e9%9f%b3%e9%a9%b1%e5%8a%a8" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;众所周知，Chromebook 很多型号的声音驱动一直是个大问题，windows 上面甚至得付费才有驱动，而我装好一进来也是没有声音的。&lt;/p&gt;
&lt;p&gt;很幸运的，在 Chrultrabook 上面提示 Linux 是完全支持的。然后我就找到了这个项目：&lt;/p&gt;
&lt;div class="github-card-wrapper"&gt;
&lt;a id="github-c1d2cc0f99d274d966106b16b267712a" target="_blank" href="https://github.com/WeirdTreeThing/chromebook-linux-audio" class="cursor-pointer"&gt;
&lt;div
class="w-full md:w-auto p-0 m-0 border border-neutral-200 dark:border-neutral-700 border rounded-md shadow-2xl"&gt;&lt;div class="w-full nozoom"&gt;
&lt;img
src="https://opengraph.githubassets.com/0/WeirdTreeThing/chromebook-linux-audio"
alt="GitHub Repository Thumbnail"
class="nozoom mt-0 mb-0 w-full h-full object-cover"&gt;
&lt;/div&gt;&lt;div class="w-full md:w-auto pt-3 p-5"&gt;
&lt;div class="flex items-center"&gt;
&lt;span class="text-2xl text-neutral-800 dark:text-neutral me-2"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-brand-github"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" /&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div
id="github-c1d2cc0f99d274d966106b16b267712a-full_name"
class="m-0 font-bold text-xl text-neutral-800 decoration-primary-500 hover:underline hover:underline-offset-2 dark:text-neutral"&gt;
WeirdTreeThing/chromebook-linux-audio
&lt;/div&gt;
&lt;/div&gt;
&lt;p id="github-c1d2cc0f99d274d966106b16b267712a-description" class="m-0 mt-2 text-md text-neutral-800 dark:text-neutral"&gt;
Script that enables audio support on many Chrome devices running various Linux distros
&lt;/p&gt;
&lt;div class="m-0 mt-2 flex items-center"&gt;
&lt;span class="mr-1 inline-block h-3 w-3 rounded-full language-dot" data-language="Python"&gt;&lt;/span&gt;
&lt;div class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
Python
&lt;/div&gt;
&lt;span class="text-md mr-1 text-neutral-800 dark:text-neutral"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"&gt;&lt;path fill="currentColor" d="M287.9 0C297.1 0 305.5 5.25 309.5 13.52L378.1 154.8L531.4 177.5C540.4 178.8 547.8 185.1 550.7 193.7C553.5 202.4 551.2 211.9 544.8 218.2L433.6 328.4L459.9 483.9C461.4 492.9 457.7 502.1 450.2 507.4C442.8 512.7 432.1 513.4 424.9 509.1L287.9 435.9L150.1 509.1C142.9 513.4 133.1 512.7 125.6 507.4C118.2 502.1 114.5 492.9 115.1 483.9L142.2 328.4L31.11 218.2C24.65 211.9 22.36 202.4 25.2 193.7C28.03 185.1 35.5 178.8 44.49 177.5L197.7 154.8L266.3 13.52C270.4 5.249 278.7 0 287.9 0L287.9 0zM287.9 78.95L235.4 187.2C231.9 194.3 225.1 199.3 217.3 200.5L98.98 217.9L184.9 303C190.4 308.5 192.9 316.4 191.6 324.1L171.4 443.7L276.6 387.5C283.7 383.7 292.2 383.7 299.2 387.5L404.4 443.7L384.2 324.1C382.9 316.4 385.5 308.5 391 303L476.9 217.9L358.6 200.5C350.7 199.3 343.9 194.3 340.5 187.2L287.9 78.95z"/&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div id="github-c1d2cc0f99d274d966106b16b267712a-stargazers" class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
494
&lt;/div&gt;
&lt;span class="text-md mr-1 text-neutral-800 dark:text-neutral"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"&gt;&lt;path fill="currentColor" d="M80 104c13.3 0 24-10.7 24-24s-10.7-24-24-24S56 66.7 56 80s10.7 24 24 24zm80-24c0 32.8-19.7 61-48 73.3V192c0 17.7 14.3 32 32 32H304c17.7 0 32-14.3 32-32V153.3C307.7 141 288 112.8 288 80c0-44.2 35.8-80 80-80s80 35.8 80 80c0 32.8-19.7 61-48 73.3V192c0 53-43 96-96 96H256v70.7c28.3 12.3 48 40.5 48 73.3c0 44.2-35.8 80-80 80s-80-35.8-80-80c0-32.8 19.7-61 48-73.3V288H144c-53 0-96-43-96-96V153.3C19.7 141 0 112.8 0 80C0 35.8 35.8 0 80 0s80 35.8 80 80zm208 24c13.3 0 24-10.7 24-24s-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24zM248 432c0-13.3-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24s24-10.7 24-24z"/&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div id="github-c1d2cc0f99d274d966106b16b267712a-forks" class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
79
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script
async
type="text/javascript"
src="https://minortex.github.io/js/fetch-repo.min.dc5533c50cefd50405344b235937142271f26229fe39cbee27fd4960e8bb897a0beebfad77a1091ca91cd0d1fb14e70fc37cc114dd9674fb2c32e0ab512ec8a4.js"
integrity="sha512-3FUzxQzv1QQFNEsjWTcUInHyYin&amp;#43;OcvuJ/1JYOi7iXoL7r&amp;#43;td6EJHKkc0NH7FOcPw3zBFN2WdPssMuCrUS7IpA=="
data-repo-url="https://api.github.com/repos/WeirdTreeThing/chromebook-linux-audio"
data-repo-id="github-c1d2cc0f99d274d966106b16b267712a"&gt;&lt;/script&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;直接一键部署，太舒服了！不过似乎不支持 Ubuntu。&lt;/p&gt;
&lt;p&gt;后来，在 alsa 的一次更新后，我的喇叭不出声了，翻了下 issue，&lt;a
href="https://github.com/WeirdTreeThing/chromebook-linux-audio/issues/185"
target="_blank"
&gt;这里&lt;/a&gt;提到重新安装加上这个参数就可以了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./setup-audio --branch syntax-7
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;按键映射
&lt;div id="按键映射" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8c%89%e9%94%ae%e6%98%a0%e5%b0%84" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;Chromebook 的键盘比较特殊，没有&lt;kbd&gt;Del&lt;/kbd&gt;，没有&lt;kbd&gt;Ins&lt;/kbd&gt;。原来&lt;kbd&gt;Capslock&lt;/kbd&gt;的位置现在变成了&lt;kbd&gt;Meta&lt;/kbd&gt;(&lt;kbd&gt;Win&lt;/kbd&gt;) 键。而左&lt;kbd&gt;Alt&lt;/kbd&gt;和&lt;kbd&gt;Ctrl&lt;/kbd&gt;就变得非常大。&lt;/p&gt;
&lt;p&gt;写声音脚本的这位兄弟顺手写了个按键映射：&lt;/p&gt;
&lt;div class="github-card-wrapper"&gt;
&lt;a id="github-4f7ee459704e5f8a6b41a219a1514b5f" target="_blank" href="https://github.com/WeirdTreeThing/cros-keyboard-map" class="cursor-pointer"&gt;
&lt;div
class="w-full md:w-auto p-0 m-0 border border-neutral-200 dark:border-neutral-700 border rounded-md shadow-2xl"&gt;&lt;div class="w-full nozoom"&gt;
&lt;img
src="https://opengraph.githubassets.com/0/WeirdTreeThing/cros-keyboard-map"
alt="GitHub Repository Thumbnail"
class="nozoom mt-0 mb-0 w-full h-full object-cover"&gt;
&lt;/div&gt;&lt;div class="w-full md:w-auto pt-3 p-5"&gt;
&lt;div class="flex items-center"&gt;
&lt;span class="text-2xl text-neutral-800 dark:text-neutral me-2"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-brand-github"&gt;&lt;path stroke="none" d="M0 0h24v24H0z" fill="none"/&gt;&lt;path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" /&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div
id="github-4f7ee459704e5f8a6b41a219a1514b5f-full_name"
class="m-0 font-bold text-xl text-neutral-800 decoration-primary-500 hover:underline hover:underline-offset-2 dark:text-neutral"&gt;
WeirdTreeThing/cros-keyboard-map
&lt;/div&gt;
&lt;/div&gt;
&lt;p id="github-4f7ee459704e5f8a6b41a219a1514b5f-description" class="m-0 mt-2 text-md text-neutral-800 dark:text-neutral"&gt;
Utility to generate keyd configurations for use on Chromebooks
&lt;/p&gt;
&lt;div class="m-0 mt-2 flex items-center"&gt;
&lt;span class="mr-1 inline-block h-3 w-3 rounded-full language-dot" data-language="Python"&gt;&lt;/span&gt;
&lt;div class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
Python
&lt;/div&gt;
&lt;span class="text-md mr-1 text-neutral-800 dark:text-neutral"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"&gt;&lt;path fill="currentColor" d="M287.9 0C297.1 0 305.5 5.25 309.5 13.52L378.1 154.8L531.4 177.5C540.4 178.8 547.8 185.1 550.7 193.7C553.5 202.4 551.2 211.9 544.8 218.2L433.6 328.4L459.9 483.9C461.4 492.9 457.7 502.1 450.2 507.4C442.8 512.7 432.1 513.4 424.9 509.1L287.9 435.9L150.1 509.1C142.9 513.4 133.1 512.7 125.6 507.4C118.2 502.1 114.5 492.9 115.1 483.9L142.2 328.4L31.11 218.2C24.65 211.9 22.36 202.4 25.2 193.7C28.03 185.1 35.5 178.8 44.49 177.5L197.7 154.8L266.3 13.52C270.4 5.249 278.7 0 287.9 0L287.9 0zM287.9 78.95L235.4 187.2C231.9 194.3 225.1 199.3 217.3 200.5L98.98 217.9L184.9 303C190.4 308.5 192.9 316.4 191.6 324.1L171.4 443.7L276.6 387.5C283.7 383.7 292.2 383.7 299.2 387.5L404.4 443.7L384.2 324.1C382.9 316.4 385.5 308.5 391 303L476.9 217.9L358.6 200.5C350.7 199.3 343.9 194.3 340.5 187.2L287.9 78.95z"/&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div id="github-4f7ee459704e5f8a6b41a219a1514b5f-stargazers" class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
120
&lt;/div&gt;
&lt;span class="text-md mr-1 text-neutral-800 dark:text-neutral"&gt;
&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"&gt;&lt;path fill="currentColor" d="M80 104c13.3 0 24-10.7 24-24s-10.7-24-24-24S56 66.7 56 80s10.7 24 24 24zm80-24c0 32.8-19.7 61-48 73.3V192c0 17.7 14.3 32 32 32H304c17.7 0 32-14.3 32-32V153.3C307.7 141 288 112.8 288 80c0-44.2 35.8-80 80-80s80 35.8 80 80c0 32.8-19.7 61-48 73.3V192c0 53-43 96-96 96H256v70.7c28.3 12.3 48 40.5 48 73.3c0 44.2-35.8 80-80 80s-80-35.8-80-80c0-32.8 19.7-61 48-73.3V288H144c-53 0-96-43-96-96V153.3C19.7 141 0 112.8 0 80C0 35.8 35.8 0 80 0s80 35.8 80 80zm208 24c13.3 0 24-10.7 24-24s-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24zM248 432c0-13.3-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24s24-10.7 24-24z"/&gt;&lt;/svg&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div id="github-4f7ee459704e5f8a6b41a219a1514b5f-forks" class="m-0 mr-5 text-md text-neutral-800 dark:text-neutral"&gt;
29
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script
async
type="text/javascript"
src="https://minortex.github.io/js/fetch-repo.min.dc5533c50cefd50405344b235937142271f26229fe39cbee27fd4960e8bb897a0beebfad77a1091ca91cd0d1fb14e70fc37cc114dd9674fb2c32e0ab512ec8a4.js"
integrity="sha512-3FUzxQzv1QQFNEsjWTcUInHyYin&amp;#43;OcvuJ/1JYOi7iXoL7r&amp;#43;td6EJHKkc0NH7FOcPw3zBFN2WdPssMuCrUS7IpA=="
data-repo-url="https://api.github.com/repos/WeirdTreeThing/cros-keyboard-map"
data-repo-id="github-4f7ee459704e5f8a6b41a219a1514b5f"&gt;&lt;/script&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;这个工具映射了上面的功能按键，让&lt;kbd&gt;Meta&lt;/kbd&gt;+功能键可以实现原来的功能，然后单独按下功能键就是&lt;kbd&gt;F1&lt;/kbd&gt;-&lt;kbd&gt;F10&lt;/kbd&gt;。调节键盘背光，则是按下&lt;kbd&gt;leftAlt&lt;/kbd&gt;+亮度按键。&lt;/p&gt;
&lt;p&gt;我手动对这台笔记本的按键进行定制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;让锁屏按键变成&lt;kbd&gt;Del&lt;/kbd&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;kbd&gt;rightAlt&lt;/kbd&gt;加上锁屏按键变成&lt;kbd&gt;F11&lt;/kbd&gt;，加上&lt;kbd&gt;Backspace&lt;/kbd&gt;变成&lt;kbd&gt;F12&lt;/kbd&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除了一些冗余的配置，避免了之前为了通用一刀切导致的物理音量键变成&lt;kbd&gt;F8&lt;/kbd&gt;/&lt;kbd&gt;F9&lt;/kbd&gt;的 bug&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[ids]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;k:0001:0001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f13&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;rightalt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;layer(rightalt)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[meta]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;forward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;refresh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;f11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;scale&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;brightnessdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f7&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;brightnessup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f9&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;volumedown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f10&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;volumeup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;backspace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;f12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[alt]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;kbdillumdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f7&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;kbdillumup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[rightalt]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;kbdillumdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f7&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;kbdillumup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;backspace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;f12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f13&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;f11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[control+alt]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;f13&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;C-A-delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;另外值得一提的就是，键盘上面所有的英文全部都是小写的。&lt;/p&gt;
&lt;h3 class="relative group"&gt;充电
&lt;div id="充电" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%85%e7%94%b5" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;充电限制
&lt;div id="充电限制" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%85%e7%94%b5%e9%99%90%e5%88%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;首先需要安装 ectool，这一个工具可以在 aur 的&lt;code&gt;fw-ectool-git&lt;/code&gt;下载，fw 代表的是 framework book，这个笔记本使用的是开源的 ec，正好 chromebook 也是这个，所以可以通用。&lt;/p&gt;
&lt;p&gt;ectool 可以控制系统的充电情况。不过可惜我这一台是 10 代的，不支持 sustainer，也就是说 ec 不能实现在某个区间自动断电。所以只能写一个脚本来控制系统的充电情况。尽管这样还是有一些限制，比如说关机充电可能就会充到满。&lt;/p&gt;
&lt;p&gt;接下来我会对我使用的&lt;a
href="https://gist.github.com/minortex/0fe6c1098ec8a4f879fa9315d216a957"
target="_blank"
&gt;一些脚本&lt;/a&gt; 进行解释。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;charge_control.sh&lt;/strong&gt;：这是主要进行控制的脚本，里面有两个功能，一个是&lt;code&gt;--check-battery&lt;/code&gt;，这个参数会检测电池电量，如果超过了 78，就会自动设置成 idle 模式，同时关闭计时器，避免进行无谓的计时；另一个是&lt;code&gt;--connect-charger&lt;/code&gt;，这个用于 udev 的控制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;check-battery.service(timer)&lt;/strong&gt;：尝试一下 systemd-timer，为后续的启动和停用计时器做准备。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;control_systemd_battery_timer.sh&lt;/strong&gt;：这是一个用于设置上述服务的脚本。主要是用于给 udev 触发使用。(在 rules 里面写这么多太不优雅了)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;99-battery.rules&lt;/strong&gt;：这就是 udev 触发的规则了。在插上电源的时候检测一次电池电量，大于 78 就 idle，小于就开始充电。同时开启计时器，每三分钟检测一次电池。如果拔掉电源，那么就停止计时器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;值得小心的是，需要把 service 设置成 enabled，这样当电量达到 78，同时插着电源并开启 idle 模式的时候重启电脑，才会保持这个电量，因为重启会重置限制充电的状态；同时也把 timer 设置成 enabled，这样在电池没到 78，同时冲着电的时候重启电脑，才能在到达 78 的时候检测到从而开启 idle 模式。&lt;/p&gt;
&lt;p&gt;写了那么多，看起来还是有点臃肿的感觉！不过功能总算是实现了。&lt;/p&gt;
&lt;h4 class="relative group"&gt;缓解 kde 的电量显示的 bug
&lt;div id="缓解-kde-的电量显示的-bug" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bc%93%e8%a7%a3-kde-%e7%9a%84%e7%94%b5%e9%87%8f%e6%98%be%e7%a4%ba%e7%9a%84-bug" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;
&lt;p&gt;我到手没几天，就发现 kde 报告的电池状态有 bug。当拔下充电器后，显示已连接充电器，但仍在放电。短暂插入充电器然后拔出，会有概率避免这个问题；睡眠再唤醒也能避免这个问题。&lt;/p&gt;
&lt;p&gt;经过一系列的排查，发现问题出现在&lt;code&gt;upower&lt;/code&gt;上，upower 报告了错误的电池状态。&lt;code&gt;power_supply&lt;/code&gt;总共有三个对象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AC&lt;/li&gt;
&lt;li&gt;CROS_USBPD_CHARGER0&lt;/li&gt;
&lt;li&gt;CROS_USBPD_CHARGER1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;虽然在 sysfs 里面报告的&lt;code&gt;online&lt;/code&gt;状态是正确的，来到&lt;code&gt;upower&lt;/code&gt;就不对了。&lt;code&gt;AC online&lt;/code&gt;的状态和充电器的状态是相反的。重启&lt;code&gt;upower&lt;/code&gt;服务可以让状态正确，但是已经被&lt;code&gt;powerdevil&lt;/code&gt;接收了，所以无济于事。我寻找了很多办法，最终在&lt;code&gt;upower&lt;/code&gt;的 gitlab 上面的&lt;a
href="https://gitlab.freedesktop.org/upower/upower/-/issues/232"
target="_blank"
&gt;issue&lt;/a&gt;看到了解决方案：&lt;/p&gt;
&lt;p&gt;覆写&lt;code&gt;upower&lt;/code&gt;的 systemd 服务，把它访问 AC 的 sysfs 禁用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;### Editing /etc/systemd/system/upower.service.d/override.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;### Anything between here and the comment below will become the contents of the drop-in file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;InaccessiblePaths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/sys/class/power_supply/AC /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/ACPI0003:00/power_supply/AC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;### Edits below this comment will be discarded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然方法很暴力，但是它有效啊！&lt;/p&gt;
&lt;h3 class="relative group"&gt;风扇控制
&lt;div id="风扇控制" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%a3%8e%e6%89%87%e6%8e%a7%e5%88%b6" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;理论上，其他的 chromebook 可以直接通过 aur 安装&lt;code&gt;fw-fanctrl&lt;/code&gt;来安装一个用 python 写的脚本来控制。&lt;/p&gt;
&lt;p&gt;不过我这台 spin713 比较特殊，使用&lt;code&gt;ectool&lt;/code&gt;来查看温度是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sudo ectool temps all
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--sensor name -------- temperature -------- ratio &lt;span class="o"&gt;(&lt;/span&gt;fan_off and fan_max&lt;span class="o"&gt;)&lt;/span&gt; --
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp1 &lt;span class="m"&gt;318&lt;/span&gt; K &lt;span class="o"&gt;(=&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt; C&lt;span class="o"&gt;)&lt;/span&gt; 66% &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;298&lt;/span&gt; K and &lt;span class="m"&gt;328&lt;/span&gt; K&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp2 &lt;span class="m"&gt;324&lt;/span&gt; K &lt;span class="o"&gt;(=&lt;/span&gt; &lt;span class="m"&gt;51&lt;/span&gt; C&lt;span class="o"&gt;)&lt;/span&gt; 86% &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;298&lt;/span&gt; K and &lt;span class="m"&gt;328&lt;/span&gt; K&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp3 &lt;span class="m"&gt;314&lt;/span&gt; K &lt;span class="o"&gt;(=&lt;/span&gt; &lt;span class="m"&gt;41&lt;/span&gt; C&lt;span class="o"&gt;)&lt;/span&gt; N/A &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fan_off&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; K, &lt;span class="nv"&gt;fan_max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; K&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而&lt;code&gt;lm-sensors&lt;/code&gt;的输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ sensors &lt;span class="p"&gt;|&lt;/span&gt;sed -n &lt;span class="s2"&gt;&amp;#34;/coretemp-/,/Temp3/p&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;coretemp-isa-0000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adapter: ISA adapter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Package id 0: +76.0°C &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C, &lt;span class="nv"&gt;crit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Core 0: +59.0°C &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C, &lt;span class="nv"&gt;crit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Core 1: +76.0°C &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C, &lt;span class="nv"&gt;crit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Core 2: +57.0°C &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C, &lt;span class="nv"&gt;crit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Core 3: +61.0°C &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C, &lt;span class="nv"&gt;crit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +100.0°C&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cros_ec-isa-0000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adapter: ISA adapter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fan1: &lt;span class="m"&gt;1641&lt;/span&gt; RPM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp1: +42.9°C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp2: +43.9°C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Temp3: +39.9°C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以发现，ectool 并没有读取到 cpu 的温度，而是其他部分的温度，这个温度会比 cpu 负载高的时候的温度低很多，当 cpu 温度降下来后，又会比 cpu 温度高。而&lt;code&gt;fw-fanctrl&lt;/code&gt;默认是取&lt;code&gt;ectool&lt;/code&gt;里面最高的温度，这就会导致风扇控制不灵敏，&lt;del&gt;目前办法还在想&amp;hellip;&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;已经找到解决办法了：安装旧版的&lt;code&gt;fw-fanctrl&lt;/code&gt;。在 24 年 5 月之后，这个工具读取温度的数据源从&lt;code&gt;lm-sensors&lt;/code&gt;切换到了&lt;code&gt;ectool&lt;/code&gt;，原因是他们认为这样更加准确。但是我的本子 ectool 无法读取到 cpu 的温度，用着旧版就可以了。&lt;/p&gt;
&lt;h2 class="relative group"&gt;小结..
&lt;div id="小结" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%b0%8f%e7%bb%93" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;Arch 确实不算是一个能让人省心的系统，但是折腾的过程还是回味无穷的，paru 和 aur 配合在一起的包管理也非常好用。另外折腾的东西，等到以后再补充吧。&lt;/p&gt;</description></item><item><title>站点日志</title><link>https://minortex.github.io/posts/my-site-logs/</link><pubDate>Wed, 28 Feb 2024 00:00:00 +0000</pubDate><guid>https://minortex.github.io/posts/my-site-logs/</guid><description>&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;2024.02.28&lt;/p&gt;
&lt;p&gt;终于，我成功迁移到了 Hugo。听说性能不错，但是真没有很多东西写来着&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2025.3.31&lt;/p&gt;
&lt;p&gt;从 blowfish 切换到了 stack 这个主题。用的人多，也可以抄别人的前端作业。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;真的是因为不会改主题才半摆烂状态的！&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;参考了以下文章修改了主题，在此感谢：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a
href="https://www.xalaok.top/post/stack-modify/"
target="_blank"
&gt;Hugo Stack 魔改美化 | Naive Koala&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a
href="https://shitao5.org/posts/hugo-stack/"
target="_blank"
&gt;Hugo Stack 主题添加「最后修改于」 - Shitao Wu | 吴诗涛&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2025.6.16&lt;/p&gt;
&lt;p&gt;突然心血来潮想弄博客！&lt;del&gt;（才不是换了新轴体想打字了呢）&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;工欲善其事，必先利其器，又重新装修了一遍，这会又换回了&lt;code&gt;blowfish&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;感谢强大的&lt;a
href="https://aistudio.google.com"
target="_blank"
&gt;gemini&lt;/a&gt;让我有机会写前端配好了友链，不久会写一篇文章讲讲我的过程~&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Android Shell 中的命令：settings</title><link>https://minortex.github.io/posts/setting-commands-in-android-shell/</link><pubDate>Wed, 05 Apr 2023 00:00:00 +0000</pubDate><guid>https://minortex.github.io/posts/setting-commands-in-android-shell/</guid><description>
&lt;h2 class="relative group"&gt;前言
&lt;div id="前言" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%89%8d%e8%a8%80" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;家里的创维盒子需要手机遥控功能，虽说百变遥控可以通过 root 实现，但是毕竟是十年前的老软件了，随着手机系统更新已经落伍了，但是一众新软件都是通过无障碍实现的鼠标和键盘输入，对于没有原生设置的电视，要如何解决呢？
小米 6 的 20.1.16 版本 miui 有个大 bug:处于底部的通知栏磁贴怎么都拖不上去，就只能放弃不用了吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是需要注意的是，修改所有的&lt;code&gt;secure&lt;/code&gt;命名空间下面的设置项都需要 root 权限。&lt;/p&gt;
&lt;h2 class="relative group"&gt;简单上手
&lt;div id="简单上手" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%ae%80%e5%8d%95%e4%b8%8a%e6%89%8b" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;原理介绍
&lt;div id="原理介绍" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8e%9f%e7%90%86%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;现代 Android（7+）的设置项本质是修改&lt;code&gt;/data/system/users/0/&lt;/code&gt;下的 &lt;code&gt;settings_global.xml``settings_secure.xml``settings_system.xml&lt;/code&gt;
三个 xml，这三个文件保存了你在设置 app 里修改的项目。而旧版的 android 则是在“设置存储（com.android.providers.settings”里的/databases 里存储设置项目，查了下资料，大概说是因为效率原因？&lt;/p&gt;
&lt;h3 class="relative group"&gt;使用方法
&lt;div id="使用方法" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p&gt;我们使用的时候，先列出可以修改的项目及参数：
&lt;code&gt;settings list &amp;lt;命名空间&amp;gt;&lt;/code&gt;&lt;br&gt;
对某一设置项进行查看：
&lt;code&gt;settings get &amp;lt;命名空间&amp;gt; &amp;lt;项目&amp;gt;&lt;/code&gt;&lt;br&gt;
对某一设置项进行修改：
&lt;code&gt;settings put &amp;lt;命名空间&amp;gt; &amp;lt;项目&amp;gt; &amp;lt;参数&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h2 class="relative group"&gt;实操
&lt;div id="实操" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%ae%9e%e6%93%8d" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;使用 settings list 输出的内容大概可以粗略分为两种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只能开启或者禁用的。1 为开启，0 为禁用。&lt;/li&gt;
&lt;li&gt;有应用程序特定名字的项目。这些一般反编译 AndroidManifest.xml 会看到应用程序的声明，但由于不会用/和。分开实用性并不大。&lt;br&gt;
要进行修改，得先知道它们的名字。既然我们要在这台设备上修改，那用另一台设备手动开启后就可以 list 出来，从而知道特定的名称。&lt;br&gt;
需要注意的一点是，我们一般只是在原有的参数上面添加，所以在使用 settings put 时，别忘了把原有的参数也加上去。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;首先当然是启用无障碍的总开关：&lt;br&gt;
&lt;code&gt;settings put secure accessibility_enabled 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;以下是我使用的一些常用应用对应的名称：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;旋转的无障碍“旋转”的无障碍权限&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put secure enabled_accessibility_services personal.fameit.nl.eg/nl.fameit.rotate.RotateAccessibilityService
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KDE Connect 的鼠标控制&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put secure enabled_accessibility_services org.kde.kdeconnect_tp/org.kde.kdeconnect.Plugins.MouseReceiverPlugin.MouseReceiverService
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KDE Connect 的通知使用权&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put enabled_accessibility_services org.kde.kdeconnect_tp/org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put enabled_accessibility_services org.kde.kdeconnect_tp/org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KDE Connect 中启用的键盘&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put secure enabled_input_methods org.kde.kdeconnect_tp/org.kde.kdeconnect.Plugins.RemoteKeyboardPlugin.RemoteKeyboardService
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;通知栏磁贴&lt;/strong&gt;&lt;br&gt;
按需添加，有一个叫做 system_qs_tiles 的项目，修改那个似乎没用。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;settings put secure sysui_qs_tiles &lt;span class="s2"&gt;&amp;#34;airplane,cell,wifi,rotation,custom(com.v2ray.ang/.service.QSTileService),batterysaver,hotspot,mute,nfc,custom(net.dinglisch.android.taskerm/.QSTileService0),gps,edit &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 class="relative group"&gt;结语
&lt;div id="结语" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%93%e8%af%ad" aria-label="锚点"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;折腾了那么多，不得不感叹命令行的强大之处：正确就是 stout，错误就是 sterr，不会像 gui 控制那样出现卡顿和按了不反应等奇奇怪怪的 bug，当今时代 cli 还是有存在的意义的。&lt;/p&gt;</description></item></channel></rss>