diff --git a/404.html b/404.html new file mode 100644 index 0000000..7bcc4ad --- /dev/null +++ b/404.html @@ -0,0 +1,333 @@ + + + + +404 Page not found + + + + + + + + + + + + + + + +
+ +
+
+

Not Found

+

This page does not exist

+
+ + + +
+
+ + + + + diff --git a/CC.ico b/CC.ico new file mode 100644 index 0000000..e796d67 Binary files /dev/null and b/CC.ico differ diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..53be5f7 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +3000ye.com \ No newline at end of file diff --git a/ComicMono Nerd Font.ttf b/ComicMono Nerd Font.ttf new file mode 100644 index 0000000..098264f Binary files /dev/null and b/ComicMono Nerd Font.ttf differ diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..c39725e --- /dev/null +++ b/about/index.html @@ -0,0 +1,590 @@ + + + + +About + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + +
+

+ About +

+ + +
+ + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + +
+ + + + + + + + + +
+ + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..a128e4d --- /dev/null +++ b/archives/index.html @@ -0,0 +1,912 @@ + + + + +Archives + + + + + + + + + + + + + + + +
+ +
+
+

Categories

+ + +
+ + + + + + + + + + + + + + + + +
+
+ + + + + diff --git a/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/categories/c++-primer/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/categories/c++-primer/c++primer.jpg b/categories/c++-primer/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/categories/c++-primer/c++primer.jpg differ diff --git a/categories/c++-primer/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/categories/c++-primer/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/categories/c++-primer/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/categories/c++-primer/index.html b/categories/c++-primer/index.html new file mode 100644 index 0000000..5a0a91c --- /dev/null +++ b/categories/c++-primer/index.html @@ -0,0 +1,667 @@ + + + + +Category: C++ Primer - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Categories + +

+ +
+
+

7 pages

+

C++ Primer

+ +

C++ Primer

+ +
+
+ + +
+ +
+
+ +
+ + + + + + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/categories/c++-primer/index.xml b/categories/c++-primer/index.xml new file mode 100644 index 0000000..66e9cb4 --- /dev/null +++ b/categories/c++-primer/index.xml @@ -0,0 +1,1766 @@ + + + + C++ Primer on 3000ye's Blog + https://3000ye.com/categories/c++-primer/ + Recent content in C++ Primer on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 10 Dec 2023 15:58:28 +0800 + C++ Primer Ch09 + https://3000ye.com/p/c-primer-ch09/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/p/c-primer-ch09/ + <img src="https://3000ye.com/p/c-primer-ch09/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch09" /><h1 id="顺序容器"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8">#</a> + 顺序容器 +</h1><p>一个容器就是一些特定类型对象的集合,<strong>顺序容器(sequence container)</strong> 为程序员提供了控制元素存储和访问顺序的能力。</p> +<h2 id="顺序容器概述"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8%e6%a6%82%e8%bf%b0">#</a> + 顺序容器概述 +</h2><p>下面列出了标准库中的顺序容器,不同容器有不同的性能折中:</p> +<ul> +<li>想容器添加或从容器删除元素的代价。</li> +<li>非顺序访问容器中元素的代价。</li> +</ul> +<table> +<thead> +<tr> +<th>容器类型</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector</code></td> +<td>可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。</td> +</tr> +<tr> +<td><code>deque</code></td> +<td>双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。</td> +</tr> +<tr> +<td><code>list</code></td> +<td>双向链表。只支持双向顺序访问。在<code>list</code>中任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>forward_list</code></td> +<td>单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>array</code></td> +<td>固定大小数组。支持快速随机访问。不能添加或者删除元素。</td> +</tr> +<tr> +<td><code>string</code></td> +<td>与<code>vector</code>相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。</td> +</tr> +</tbody> +</table> +<ul> +<li>除了固定大小的 <code>array</code> 外,其他容器都提供高效、灵活的内存管理。</li> +<li>通常使用 <code>vector</code> 是最好的选择,除非你有很好的理由选择其他容器。</li> +<li>如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序要求随记访问元素,应使用 <code>vector</code> 或 <code>deque</code>。</li> +<li>如果程序要求在容器的中间插入或删除元素,应使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 <code>deque</code>。</li> +</ul> +<h2 id="容器库概念"> + <a href="#%e5%ae%b9%e5%99%a8%e5%ba%93%e6%a6%82%e5%bf%b5">#</a> + 容器库概念 +</h2><p>容器类型操作上形成了一种层次:</p> +<ul> +<li>某些操作是所有容器类型都提供的。</li> +<li>另一些操作仅针对顺序容器、关联容器或无序容器。</li> +</ul> +<h3 id="类型"> + <a href="#%e7%b1%bb%e5%9e%8b">#</a> + 类型 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iterator</code></td> +<td>此容器类型的迭代器类型</td> +</tr> +<tr> +<td><code>const_iterator</code></td> +<td>可以读取元素但不能修改元素的迭代器类型</td> +</tr> +<tr> +<td><code>size_type</code></td> +<td>无符号整数类型,足够保存此种容器类型最大可能的大小</td> +</tr> +<tr> +<td><code>difference_type</code></td> +<td>带符号整数类型,足够保存两个迭代器之间的距离</td> +</tr> +<tr> +<td><code>value_type</code></td> +<td>元素类型</td> +</tr> +<tr> +<td><code>reference</code></td> +<td>元素的左值类型;和<code>value_type &amp;</code>含义相同</td> +</tr> +<tr> +<td><code>const_reference</code></td> +<td>元素的<code>const</code>左值类型,即<code>const value_type &amp;</code></td> +</tr> +</tbody> +</table> +<h3 id="构造函数"> + <a href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0">#</a> + 构造函数 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>C c;</code></td> +<td>默认构造函数,构造空容器</td> +</tr> +<tr> +<td><code>C c1(c2);</code>或<code>C c1 = c2;</code></td> +<td>构造<code>c2</code>的拷贝<code>c1</code></td> +</tr> +<tr> +<td><code>C c(b, e)</code></td> +<td>构造<code>c</code>,将迭代器<code>b</code>和<code>e</code>指定范围内的所有元素拷贝到<code>c</code></td> +</tr> +<tr> +<td><code>C c(a, b, c...)</code></td> +<td>列表初始化<code>c</code></td> +</tr> +<tr> +<td><code>C c(n)</code></td> +<td>只支持顺序容器,且不包括<code>array</code>,包含<code>n</code>个元素,这些元素进行了值初始化</td> +</tr> +<tr> +<td><code>C c(n, t)</code></td> +<td>包含<code>n</code>个初始值为<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>只有顺序容器的构造函数才接受大小参数,关联容器并不支持。</li> +<li><code>array</code>具有固定大小。</li> +<li>和其他容器不同,默认构造的<code>array</code>是非空的。</li> +<li>直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。</li> +<li>使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。</li> +</ul> +<h3 id="赋值和swap"> + <a href="#%e8%b5%8b%e5%80%bc%e5%92%8cswap">#</a> + 赋值和<code>swap</code> +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c1 = c2;</code></td> +<td>将<code>c1</code>中的元素替换成<code>c2</code>中的元素</td> +</tr> +<tr> +<td><code>c1 = {a, b, c...}</code></td> +<td>将<code>c1</code>中的元素替换成列表中的元素(不适用于<code>array</code>)</td> +</tr> +<tr> +<td><code>c1.swap(c2)</code></td> +<td>交换<code>c1</code>和<code>c2</code>的元素</td> +</tr> +<tr> +<td><code>swap(c1, c2)</code></td> +<td>等价于<code>c1.swap(c2)</code></td> +</tr> +<tr> +<td><code>c.assign(b, e)</code></td> +<td>将<code>c</code>中的元素替换成迭代器<code>b</code>和<code>e</code>表示范围中的元素,<code>b</code>和<code>e</code>不能指向<code>c</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(il)</code></td> +<td>将<code>c</code>中的元素替换成初始化列表<code>il</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(n, r)</code></td> +<td>将<code>c</code>中的元素替换为<code>n</code>个值是<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>使用非成员版本的<code>swap</code>是一个好习惯。</li> +<li><code>assign</code>操作不适用于关联容器和<code>array</code></li> +</ul> +<h3 id="大小"> + <a href="#%e5%a4%a7%e5%b0%8f">#</a> + 大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.size()</code></td> +<td><code>c</code>中元素的数目(不支持<code>forward_list</code>)</td> +</tr> +<tr> +<td><code>c.max_size()</code></td> +<td><code>c</code>中可保存的最大元素数目</td> +</tr> +<tr> +<td><code>c.empty()</code></td> +<td>若<code>c</code>中存储了元素,返回<code>false</code>,否则返回<code>true</code></td> +</tr> +</tbody> +</table> +<h3 id="添加元素"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%85%83%e7%b4%a0">#</a> + 添加元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.push_back(t)</code></td> +<td>在<code>c</code>尾部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_back(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.push_front(t)</code></td> +<td>在<code>c</code>头部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_front(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前创建一个值是<code>t</code>的元素,返回指向新元素的迭代器</td> +</tr> +<tr> +<td><code>c.emplace(p, args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, n, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前插入<code>n</code>个值为<code>t</code>的元素,返回指向第一个新元素的迭代器;如果<code>n</code>是0,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, b, e)</code></td> +<td>将迭代器<code>b</code>和<code>e</code>范围内的元素,插入到<code>p</code>指向的元素之前;如果范围为空,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, il)</code></td> +<td><code>il</code>是一个花括号包围中的元素值列表,将其插入到<code>p</code>指向的元素之前;如果<code>il</code>是空,则返回<code>p</code></td> +</tr> +</tbody> +</table> +<ul> +<li>因为这些操作会改变大小,因此不适用于<code>array</code>。</li> +<li><code>forward_list</code>有自己专有版本的<code>insert</code>和<code>emplace</code>。</li> +<li><code>forward_list</code>不支持<code>push_back</code>和<code>emplace_back</code>。</li> +<li>当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。</li> +<li><code>emplace</code>开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。</li> +<li>传递给<code>emplace</code>的参数必须和元素类型的构造函数相匹配。</li> +</ul> +<h3 id="访问元素"> + <a href="#%e8%ae%bf%e9%97%ae%e5%85%83%e7%b4%a0">#</a> + 访问元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.back()</code></td> +<td>返回<code>c</code>中尾元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c.front()</code></td> +<td>返回<code>c</code>中头元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c[n]</code></td> +<td>返回<code>c</code>中下标是<code>n</code>的元素的引用,<code>n</code>时候一个无符号证书。若<code>n&gt;=c.size()</code>,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.at(n)</code></td> +<td>返回下标为<code>n</code>的元素引用。如果下标越界,则抛出<code>out_of_range</code>异常</td> +</tr> +</tbody> +</table> +<ul> +<li>访问成员函数返回的是引用。</li> +<li><code>at</code>和下标操作只适用于<code>string</code>、<code>vector</code>、<code>deque</code>、<code>array</code>。</li> +<li><code>back</code>不适用于<code>forward_list</code>。</li> +<li>如果希望下标是合法的,可以使用<code>at</code>函数。</li> +</ul> +<h3 id="删除元素"> + <a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0">#</a> + 删除元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.pop_back()</code></td> +<td>删除<code>c</code>中尾元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.pop_front()</code></td> +<td>删除<code>c</code>中首元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.erase(p)</code></td> +<td>删除迭代器<code>p</code>指向的元素,返回一个指向被删除元素之后的元素的迭代器,若<code>p</code>本身是尾后迭代器,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.erase(b, e)</code></td> +<td>删除迭代器<code>b</code>和<code>e</code>范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若<code>e</code>本身就是尾后迭代器,则返回尾后迭代器</td> +</tr> +<tr> +<td><code>c.clear()</code></td> +<td>删除<code>c</code>中所有元素,返回<code>void</code></td> +</tr> +</tbody> +</table> +<ul> +<li>会改变容器大小,不适用于<code>array</code>。</li> +<li><code>forward_list</code>有特殊版本的<code>erase</code></li> +<li><code>forward_list</code>不支持<code>pop_back</code></li> +<li><code>vector</code>和<code>string</code>不支持<code>pop_front</code></li> +</ul> +<h3 id="特殊的-forwad_list操作"> + <a href="#%e7%89%b9%e6%ae%8a%e7%9a%84-forwad_list%e6%93%8d%e4%bd%9c">#</a> + 特殊的 <code>forwad_list</code>操作 +</h3><ul> +<li>链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。</li> +<li><code>forward_list</code>定义了<code>before_begin</code>,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。</li> +</ul> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>lst.before_begin()</code></td> +<td>返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。</td> +</tr> +<tr> +<td><code>lst.cbefore_begin()</code></td> +<td>同上,但是返回的是常量迭代器。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象</td> +</tr> +<tr> +<td><code>lst.insert_after(p, n, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象,<code>n</code>是数量。若<code>n</code>是0则函数行为未定义</td> +</tr> +<tr> +<td><code>lst.insert_after(p, b, e)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由迭代器<code>b</code>和<code>e</code>指定范围。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, il)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由<code>il</code>指定初始化列表。</td> +</tr> +<tr> +<td><code>emplace_after(p, args)</code></td> +<td>使用<code>args</code>在<code>p</code>之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若<code>p</code>为尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(p)</code></td> +<td>删除<code>p</code>指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若<code>p</code>指向<code>lst</code>的尾元素或者是一个尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(b, e)</code></td> +<td>类似上面,删除对象换成从<code>b</code>到<code>e</code>指定的范围。</td> +</tr> +</tbody> +</table> +<h3 id="改变容器大小"> + <a href="#%e6%94%b9%e5%8f%98%e5%ae%b9%e5%99%a8%e5%a4%a7%e5%b0%8f">#</a> + 改变容器大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.resize(n)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,若<code>n&lt;c.size()</code>,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化</td> +</tr> +<tr> +<td><code>c.resize(n, t)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,任何新添加的元素都初始化为值<code>t</code></td> +</tr> +</tbody> +</table> +<h3 id="获取迭代器"> + <a href="#%e8%8e%b7%e5%8f%96%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 获取迭代器 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.begin()</code>, <code>c.end()</code></td> +<td>返回指向<code>c</code>的首元素和尾元素之后位置的迭代器</td> +</tr> +<tr> +<td><code>c.cbegin()</code>, <code>c.cend()</code></td> +<td>返回<code>const_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>以<code>c</code>开头的版本是C++11新标准引入的</li> +<li>当不需要写访问时,应该使用<code>cbegin</code>和<code>cend</code>。</li> +</ul> +<h3 id="反向容器的额外成员"> + <a href="#%e5%8f%8d%e5%90%91%e5%ae%b9%e5%99%a8%e7%9a%84%e9%a2%9d%e5%a4%96%e6%88%90%e5%91%98">#</a> + 反向容器的额外成员 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>reverse_iterator</code></td> +<td>按逆序寻址元素的迭代器</td> +</tr> +<tr> +<td><code>const_reverse_iterator</code></td> +<td>不能修改元素的逆序迭代器</td> +</tr> +<tr> +<td><code>c.rbegin()</code>, <code>c.rend()</code></td> +<td>返回指向<code>c</code>的尾元素和首元素之前位置的迭代器</td> +</tr> +<tr> +<td><code>c.crbegin()</code>, <code>c.crend()</code></td> +<td>返回<code>const_reverse_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>不支持<code>forward_list</code></li> +</ul> + + + + C++ Primer Ch08 + https://3000ye.com/p/c-primer-ch08/ + Sun, 10 Dec 2023 15:24:27 +0800 + + https://3000ye.com/p/c-primer-ch08/ + <img src="https://3000ye.com/p/c-primer-ch08/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch08" /><h1 id="io-库"> + <a href="#io-%e5%ba%93">#</a> + IO 库 +</h1><p>我们的程序已经使用了很多 IO 库设施:</p> +<ul> +<li><code>istream</code> (输入流)类型,提供输入操作。</li> +<li><code>ostream</code> (输出流)类型,提供输出操作。</li> +<li><code>cin</code>,一个 <code>istream</code> 对象,从标准输入读取数据。</li> +<li><code>cout</code>,一个 <code>ostream</code> 对象,想标准输出写入数据。</li> +<li><code>cerr</code>,一个 <code>ostream</code> 对象,通常用于输出程序错误信息,写入到标准错误。</li> +<li><code>&gt;&gt;</code> 运算符,用来从一个 <code>istream</code> 对象中读取输入数据。</li> +<li><code>&lt;&lt;</code> 运算符,用来向一个 <code>ostream</code> 对象中写入输出数据。</li> +<li><code>getline</code> 函数,从一个给定的 <code>istream</code> 对象中读取一行数据,存入到一个给定的 <code>string</code> 对象中。</li> +</ul> +<h2 id="io-库-1"> + <a href="#io-%e5%ba%93-1">#</a> + IO 库 +</h2><p>IO 类型和对象一般都是操纵 <code>char</code> 数据的,但有些使用需要对文件、<code>string</code> 进行操作,因此分别定义了三个头文件:</p> +<ul> +<li><code>iostream</code> 头文件:从标准流中读写数据,<code>istream</code>、<code>ostream</code> 等。</li> +<li><code>fstream</code> 头文件:从文件中读写数据,<code>ifstream</code>、<code>ofstream</code> 等。</li> +<li><code>sstream</code> 头文件:从字符串中读写数据,<code>istringstream</code>、<code>ostringstream</code> 等。</li> +</ul> + + + + C++ Primer Ch07 + https://3000ye.com/p/c-primer-ch07/ + Sun, 10 Dec 2023 14:35:42 +0800 + + https://3000ye.com/p/c-primer-ch07/ + <img src="https://3000ye.com/p/c-primer-ch07/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch07" /><h1 id="类"> + <a href="#%e7%b1%bb">#</a> + 类 +</h1><p>类的基本思想是 <strong>数据抽象(data abstraction)</strong> 和 <strong>封装(encapsulation)</strong>。数据抽象是一种依赖于 <strong>接口(interface)</strong> 和 <strong>实现(implementation)</strong> 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。</p> +<p>封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。</p> +<p>类要想实现数据抽象和封装,首先需要定义一个 <strong>抽象数据类型(abstract data type)</strong>。</p> +<h2 id="定义抽象数据类型"> + <a href="#%e5%ae%9a%e4%b9%89%e6%8a%bd%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#</a> + 定义抽象数据类型 +</h2><h2 id="访问控制与封装"> + <a href="#%e8%ae%bf%e9%97%ae%e6%8e%a7%e5%88%b6%e4%b8%8e%e5%b0%81%e8%a3%85">#</a> + 访问控制与封装 +</h2><p>我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 <code>C++</code> 中使用 <strong>访问说明符(access specifiers)</strong> 加强类的封装:</p> +<ul> +<li>定义在 <code>public</code> 说明符之后的成员,在整个程序内都可被访问。</li> +<li>定义在 <code>private</code> 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。</li> +</ul> +<p>定义新的 <code>Sales_data</code> 类:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sales_data</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bookNo</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="n">units_sold</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">revenue</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="nf">avg_price</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">units_sold</span> <span class="o">?</span> <span class="n">revenue</span> <span class="o">/</span> <span class="nl">units_sold</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">p</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="n">units_sold</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">revenue</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">istream</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">isbn</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">bookNo</span><span class="p">;</span> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span> <span class="o">&amp;</span><span class="n">combine</span><span class="p">(</span><span class="k">const</span> <span class="n">Sales_data</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch06 + https://3000ye.com/p/c-primer-ch06/ + Fri, 08 Dec 2023 21:13:28 +0800 + + https://3000ye.com/p/c-primer-ch06/ + <img src="https://3000ye.com/p/c-primer-ch06/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch06" /><h1 id="函数"> + <a href="#%e5%87%bd%e6%95%b0">#</a> + 函数 +</h1><p>函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。</p> +<h2 id="函数基础"> + <a href="#%e5%87%bd%e6%95%b0%e5%9f%ba%e7%a1%80">#</a> + 函数基础 +</h2><p>一个典型的 <strong>函数(function)</strong> 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 <strong>形参(parameter)</strong> 组成的列表以及函数体。</p> +<p>我们通过 <strong>调用运算符(call operator)</strong> 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 <strong>实参(argument)</strong> 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。</p> +<p><strong>编写函数</strong>:例如编写一个求 <code>n</code> 的阶乘的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fact</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">*=</span> <span class="n">n</span> <span class="o">--</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><strong>调用函数</strong>:要调用 <code>fact</code> 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;5! = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,<strong>主调函数(calling funciton)</strong> 的执行暂停,<strong>被调函数(called funciton)</strong> 开始执行。</p> +<h3 id="局部对象"> + <a href="#%e5%b1%80%e9%83%a8%e5%af%b9%e8%b1%a1">#</a> + 局部对象 +</h3><p>在 <code>C++</code> 语言中,名字有作用域,对象有 <strong>生命周期(lifetime)</strong>:</p> +<ul> +<li>名字的作用域是程序文本的一部分,名字在其中可见。</li> +<li>对象的生命周期是程序执行过程中该对象存在的一段时间。</li> +</ul> +<p>在函数体内,形参和内部定义的变量统称为 <strong>局部变量(local variable)</strong>,它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。</p> +<p><strong>局部静态对象</strong>:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 <code>static</code> 类型从而获得这样的对象,<strong>局部静态对象(local static object)</strong> 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。</p> +<h2 id="参数传递"> + <a href="#%e5%8f%82%e6%95%b0%e4%bc%a0%e9%80%92">#</a> + 参数传递 +</h2> + + + C++ Primer Ch04 + https://3000ye.com/p/c-primer-ch04/ + Tue, 28 Nov 2023 08:49:13 +0800 + + https://3000ye.com/p/c-primer-ch04/ + <img src="https://3000ye.com/p/c-primer-ch04/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch04" /><h1 id="表达式"> + <a href="#%e8%a1%a8%e8%be%be%e5%bc%8f">#</a> + 表达式 +</h1><p>表达式由一个或多个 <strong>运算对象(operand)</strong> 组成对表达式求值将得到一个 <strong>结果(result)</strong>。</p> +<h2 id="基础"> + <a href="#%e5%9f%ba%e7%a1%80">#</a> + 基础 +</h2><h3 id="基本概念"> + <a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a> + 基本概念 +</h3><p><code>C++</code> 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。</p> +<p>对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。</p> +<p>在表达式求值过程中,小整数类型(<code>bool</code>、<code>char</code>、<code>short</code>)等通常会被 <strong>提升(promoted)</strong> 成较大的整数类型(<code>int</code>)。</p> +<p>当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 <strong>重载运算符(overloaded operator)</strong>。</p> +<p><code>C++</code> 的表达式要不然是 <strong>右值(rvalue)</strong>,要不然就是 <strong>左值(lvalue)</strong>,这两个名词是从 <code>C</code> 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p> +<h3 id="求值顺序"> + <a href="#%e6%b1%82%e5%80%bc%e9%a1%ba%e5%ba%8f">#</a> + 求值顺序 +</h3><p>在大多数情况下,表达式求值的顺序是没有明确指定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">f1</span><span class="p">()</span> <span class="o">*</span> <span class="n">f2</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们知道 <code>f1</code> 和 <code>f2</code> 一定会在执行乘法之前被调用,但是无法知道是 <code>f1</code> 先被调用还是 <code>f2</code> 先被调用。对于没有指定调用顺序的程序来说,如果 <code>f1</code> 和 <code>f2</code> 同时修改了同一个对象,将会引发错误并产生未定义的行为。</p> +<h2 id="算术运算符"> + <a href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 算术运算符 +</h2><p><strong>溢出</strong>:当计算的结果超出该类型所能表示的范围时就会产生溢出。</p> +<p><strong><code>bool</code> 类型不应该参与计算</strong>。</p> +<p><strong>取余运算</strong>:<code>m % n</code> 的结果的符号与 <code>m</code> 相同。</p> +<h2 id="逻辑运算符"> + <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 逻辑运算符 +</h2><p><strong>短路求值</strong>:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。</p> +<h2 id="赋值运算符"> + <a href="#%e8%b5%8b%e5%80%bc%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 赋值运算符 +</h2> + + + C++ Primer Ch03 + https://3000ye.com/p/c-primer-ch03/ + Tue, 14 Nov 2023 22:14:23 +0800 + + https://3000ye.com/p/c-primer-ch03/ + <img src="https://3000ye.com/p/c-primer-ch03/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch03" /><h1 id="字符串向量和数组"> + <a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%90%91%e9%87%8f%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 字符串、向量和数组 +</h1><h2 id="命名空间的-using-声明"> + <a href="#%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e7%9a%84-using-%e5%a3%b0%e6%98%8e">#</a> + 命名空间的 <code>using</code> 声明 +</h2><p>我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用<code>using</code>进行声明:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">;</span> <span class="c1">// 单独使用某个函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 批量声明 std 中所有函数 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>头文件中不应该包含 <code>using</code> 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。</p> +</blockquote> +<h2 id="标准库类型-string"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-string">#</a> + 标准库类型 <code>string</code> +</h2><p>标准库类型 <code>string</code> 表示可变长的字符序列,使用 <code>string</code> 类型必须首先包含 <code>string</code> 头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="定义和初始化-string-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-string-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>string</code> 对象 +</h3><p>初始化 <code>string</code> 对象的方式:</p> +<table> +<thead> +<tr> +<th>方式</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>string s1</code></td> +<td>默认初始化,<code>s1</code>是个空字符串</td> +</tr> +<tr> +<td><code>string s2(s1)</code></td> +<td><code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s2 = s1</code></td> +<td>等价于<code>s2(s1)</code>,<code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s3(&quot;value&quot;)</code></td> +<td><code>s3</code>是字面值“value”的副本,除了字面值最后的那个空字符外</td> +</tr> +<tr> +<td><code>string s3 = &quot;value&quot;</code></td> +<td>等价于<code>s3(&quot;value&quot;)</code>,<code>s3</code>是字面值&quot;value&quot;的副本</td> +</tr> +<tr> +<td><code>string s4(n, 'c')</code></td> +<td>把<code>s4</code>初始化为由连续<code>n</code>个字符<code>c</code>组成的串</td> +</tr> +</tbody> +</table> +<p>拷贝初始化(copy initialization):使用 <code>=</code> 将一个已有的对象拷贝到正在创建的对象。</p> +<p>直接初始化(direct initialization):通过括号给对象赋值。</p> +<h3 id="string-对象的操作"> + <a href="#string-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>string</code> 对象的操作 +</h3><p><code>string</code>的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>os &lt;&lt; s</code></td> +<td>将<code>s</code>写到输出流<code>os</code>当中,返回<code>os</code></td> +</tr> +<tr> +<td><code>is &gt;&gt; s</code></td> +<td>从<code>is</code>中读取字符串赋给<code>s</code>,字符串以空白分割,返回<code>is</code></td> +</tr> +<tr> +<td><code>getline(is, s)</code></td> +<td>从<code>is</code>中读取一行赋给<code>s</code>,返回<code>is</code></td> +</tr> +<tr> +<td><code>s.empty()</code></td> +<td><code>s</code>为空返回<code>true</code>,否则返回<code>false</code></td> +</tr> +<tr> +<td><code>s.size()</code></td> +<td>返回<code>s</code>中字符的个数</td> +</tr> +<tr> +<td><code>s[n]</code></td> +<td>返回<code>s</code>中第<code>n</code>个字符的引用,位置<code>n</code>从0计起</td> +</tr> +<tr> +<td><code>s1+s2</code></td> +<td>返回<code>s1</code>和<code>s2</code>连接后的结果</td> +</tr> +<tr> +<td><code>s1=s2</code></td> +<td>用<code>s2</code>的副本代替<code>s1</code>中原来的字符</td> +</tr> +<tr> +<td><code>s1==s2</code></td> +<td>如果<code>s1</code>和<code>s2</code>中所含的字符完全一样,则它们相等;<code>string</code>对象的相等性判断对字母的大小写敏感</td> +</tr> +<tr> +<td><code>s1!=s2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td> +<td>利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)</td> +</tr> +</tbody> +</table> +<p>读取 <code>string</code> 对象:</p> +<ul> +<li>使用 <code>IO</code> 操作符 <code>&gt;&gt;</code> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。</li> +<li>使用 <code>getline()</code> 函数读取:将一整行读取为 <code>string</code> 对象,包括空白。</li> +</ul> +<p><code>s.size()</code> 返回 <code>string::size_type</code> 类型,是 <strong>无符号</strong> 类型的值,不能和 <code>int</code> 混用。</p> +<p><code>s1 + s2</code> 使用时,必须保证至少其中一个为 <code>string</code> 类型。例如:<code>string s = &quot;hello&quot; + &quot;world&quot;</code> 错误,其 <code>+</code> 两边都为字符串字面值。</p> +<p><strong>字符串字面值</strong> 和 <code>string</code> 是不同的类型。</p> +<h3 id="处理-string-对象中的字符"> + <a href="#%e5%a4%84%e7%90%86-string-%e5%af%b9%e8%b1%a1%e4%b8%ad%e7%9a%84%e5%ad%97%e7%ac%a6">#</a> + 处理 <code>string</code> 对象中的字符 +</h3><p><code>C++</code> 修改了 <code>c</code> 的标准库 <code>ctype.h</code> 为 <code>cctype</code>,其中定义了一组标准函数:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>isalnum(c)</code></td> +<td>当<code>c</code>是字母或数字时为真</td> +</tr> +<tr> +<td><code>isalpha(c)</code></td> +<td>当<code>c</code>是字母时为真</td> +</tr> +<tr> +<td><code>iscntrl(c)</code></td> +<td>当<code>c</code>是控制字符时为真</td> +</tr> +<tr> +<td><code>isdigit(c)</code></td> +<td>当<code>c</code>是数字时为真</td> +</tr> +<tr> +<td><code>isgraph(c)</code></td> +<td>当<code>c</code>不是空格但可以打印时为真</td> +</tr> +<tr> +<td><code>islower(c)</code></td> +<td>当<code>c</code>是小写字母时为真</td> +</tr> +<tr> +<td><code>isprint(c)</code></td> +<td>当<code>c</code>是可打印字符时为真</td> +</tr> +<tr> +<td><code>ispunct(c)</code></td> +<td>当<code>c</code>是标点符号时为真</td> +</tr> +<tr> +<td><code>isspace(c)</code></td> +<td>当<code>c</code>是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)</td> +</tr> +<tr> +<td><code>isupper(c)</code></td> +<td>当<code>c</code>是大写字母时为真</td> +</tr> +<tr> +<td><code>isxdigit(c)</code></td> +<td>当<code>c</code>是十六进制数字时为真</td> +</tr> +<tr> +<td><code>tolower(c)</code></td> +<td>当<code>c</code>是大写字母,输出对应的小写字母;否则原样输出<code>c</code></td> +</tr> +<tr> +<td><code>toupper(c)</code></td> +<td>当<code>c</code>是小写字母,输出对应的大写字母;否则原样输出<code>c</code></td> +</tr> +</tbody> +</table> +<p>遍历字符串:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">c</span> <span class="p">:</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>str[idx]</code> 中的 <code>idx</code> 为 <code>string::size_type</code> 类型,如果使用 <code>int</code> 会隐式转换为该类型。</p> +<h2 id="标准库类型-vector"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-vector">#</a> + 标准库类型 <code>vector</code> +</h2><p>标准库类型 <code>vector</code> 表示对象的集合,其中给所有对象的类型都相同。因为 <code>vector</code> 容纳着其他对象,所以称其为 <strong>容器(container)</strong>,使用 <code>vector</code> 必须包含其头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>vector</code> 同时也是 <strong>类模板(class template)</strong>,模板本身不是类或函数,但可以使用模板创建类,这个过程称为 <strong>实例化(instantiation)</strong>。</p> +<p>当使用模板时,需要指出编译器应把类或函数实例化成何种类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// ls 保存 int 类型的对象 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">files</span><span class="p">;</span> <span class="c1">// 该向量中的元素是 vector 对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p><code>vector</code> 是模板,<code>vector&lt;int&gt;</code> 是类型。</p> +</blockquote> +<h3 id="定义和初始化-vector-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-vector-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>vector</code> 对象 +</h3><p>初始化<code>vector</code>对象的方法:</p> +<table> +<thead> +<tr> +<th>方法</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector&lt;T&gt; v1</code></td> +<td><code>v1</code>是一个空<code>vector</code>,它潜在的元素是<code>T</code>类型的,执行默认初始化</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2(v1)</code></td> +<td><code>v2</code>中包含有<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2 = v1</code></td> +<td>等价于<code>v2(v1)</code>,<code>v2</code>中包含<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v3(n, val)</code></td> +<td><code>v3</code>包含了n个重复的元素,每个元素的值都是<code>val</code></td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v4(n)</code></td> +<td><code>v4</code>包含了n个重复地执行了值初始化的对象</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5{a, b, c...}</code></td> +<td><code>v5</code>包含了初始值个数的元素,每个元素被赋予相应的初始值</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5={a, b, c...}</code></td> +<td>等价于<code>v5{a, b, c...}</code></td> +</tr> +</tbody> +</table> +<h3 id="vector-对象的操作"> + <a href="#vector-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>vector</code> 对象的操作: +</h3><p><code>vector</code>支持的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>v.emtpy()</code></td> +<td>如果<code>v</code>不含有任何元素,返回真;否则返回假</td> +</tr> +<tr> +<td><code>v.size()</code></td> +<td>返回<code>v</code>中元素的个数</td> +</tr> +<tr> +<td><code>v.push_back(t)</code></td> +<td>向<code>v</code>的尾端添加一个值为<code>t</code>的元素</td> +</tr> +<tr> +<td><code>v[n]</code></td> +<td>返回<code>v</code>中第<code>n</code>个位置上元素的<strong>引用</strong></td> +</tr> +<tr> +<td><code>v1 = v2</code></td> +<td>用<code>v2</code>中的元素拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 = {a,b,c...}</code></td> +<td>用列表中元素的拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 == v2</code></td> +<td><code>v1</code>和<code>v2</code>相等当且仅当它们的元素数量相同且对应位置的元素值都相同</td> +</tr> +<tr> +<td><code>v1 != v2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>,<code>&lt;=</code>,<code>&gt;</code>, <code>&gt;=</code></td> +<td>以字典顺序进行比较</td> +</tr> +</tbody> +</table> +<h2 id="迭代器介绍"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e4%bb%8b%e7%bb%8d">#</a> + 迭代器介绍 +</h2><p>除了下标运算符外,<strong>迭代器(iterator)</strong> 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。</p> +<h3 id="使用迭代器"> + <a href="#%e4%bd%bf%e7%94%a8%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 使用迭代器 +</h3><p>拥有迭代器的类型都具有 <code>begin</code> 和 <code>end</code> 成员,其中 <code>begin</code> 成员返回指向第一个元素的迭代器:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">e</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="c1">// b 和 e 类型相同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><code>end</code> 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 <code>end</code> 指向容器的 <strong>尾后(off the end)</strong> 元素。这样的迭代器通常没有意义,只是作为标记,被称为 <strong>尾后迭代器(off-the-end iterator)</strong> 或 <strong>尾迭代器(end iterator)</strong>。</p> +<blockquote> +<p>若容器为空,则 <code>begin</code> 和 <code>end</code> 都返回尾后迭代器。</p> +</blockquote> +<p>标准容器迭代器的运算符:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>*iter</code></td> +<td>返回迭代器<code>iter</code>所指向的<strong>元素的引用</strong></td> +</tr> +<tr> +<td><code>iter-&gt;mem</code></td> +<td>等价于<code>(*iter).mem</code></td> +</tr> +<tr> +<td><code>++iter</code></td> +<td>令<code>iter</code>指示容器中的下一个元素</td> +</tr> +<tr> +<td><code>--iter</code></td> +<td>令<code>iter</code>指示容器中的上一个元素</td> +</tr> +<tr> +<td><code>iter1 == iter2</code></td> +<td>判断两个迭代器是否相等</td> +</tr> +</tbody> +</table> +<blockquote> +<p>泛型编程:尽量使用 <code>!=</code> 来对迭代器进行判断</p> +</blockquote> +<p>迭代器也拥有自己的类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="p">;</span> <span class="c1">// it 是 vector&lt;int&gt; 类型的迭代器,可以读写元素 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">const_iterator</span> <span class="n">it2</span><span class="p">;</span> <span class="c1">// it2 只能读,不能写 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果容器中的值为常量,则 <code>begin</code> 和 <code>end</code> 返回 <code>const_iterator</code>,否则返回 <code>iterator</code>。</p> +<p>解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="s">&#34;str1&#34;</span><span class="p">,</span> <span class="s">&#34;str2&#34;</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">ls</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> <span class="c1">// s 为 &#34;str1&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">).</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 解引用访问 string 成员 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 作用同上 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="迭代器运算"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e8%bf%90%e7%ae%97">#</a> + 迭代器运算 +</h3><p><code>string</code> 和 <code>vector</code> 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 <strong>迭代器运算(iterator arithmetic)</strong>:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iter + n</code></td> +<td>迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter - n</code></td> +<td>迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter1 += n</code></td> +<td>迭代器加法的复合赋值语句,将<code>iter1</code>加n的结果赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 -= n</code></td> +<td>迭代器减法的复合赋值语句,将<code>iter2</code>减n的加过赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 - iter2</code></td> +<td>两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。</td> +</tr> +<tr> +<td><code>&gt;</code>、<code>&gt;=</code>、<code>&lt;</code>、<code>&lt;=</code></td> +<td>迭代器的关系运算符,如果某迭代器</td> +</tr> +</tbody> +</table> +<p>当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 <code>difference_type</code> 类型,是带符号整数型。</p> +<h2 id="数组"> + <a href="#%e6%95%b0%e7%bb%84">#</a> + 数组 +</h2><p>数组可以看做 <code>vector</code> 的低配版,其 <strong>长度固定</strong>。</p> +<h3 id="定义和初始化内置数组"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96%e5%86%85%e7%bd%ae%e6%95%b0%e7%bb%84">#</a> + 定义和初始化内置数组 +</h3><p>数组的声明和定义形如 <code>a[d]</code>,其中 <code>a</code> 是数组的名字,<code>d</code> 是数组的维度(大于 0):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="n">cnt2</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">arr2</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整型指针的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr3</span><span class="p">[</span><span class="n">cnt</span><span class="p">];</span> <span class="c1">// 报错,cnt 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr4</span><span class="p">[</span><span class="n">cnt2</span><span class="p">];</span> <span class="c1">// 含有 42 和整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> <span class="c1">// 自动计算长度的数组 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> <span class="c1">// 报错,长度不够 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a2</span><span class="p">[]</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组来初始化数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组进行赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="访问数组元素"> + <a href="#%e8%ae%bf%e9%97%ae%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0">#</a> + 访问数组元素 +</h3><p>数组的下标为 <code>size_t</code> 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。</p> +<blockquote> +<p>下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。</p> +</blockquote> +<h3 id="指针和数组"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 指针和数组 +</h3><p>使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">ls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ls</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// 数组的元素的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// 等价于 *p2 = &amp;ls[0] +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="c-风格字符串"> + <a href="#c-%e9%a3%8e%e6%a0%bc%e5%ad%97%e7%ac%a6%e4%b8%b2">#</a> + <code>C</code> 风格字符串 +</h3><p>字符串字面值是一种通用结构的实例,这种结构是 <code>C++</code> 由 <code>C</code> 继承而来的 <strong><code>C</code> 风格字符串(C-style character string)</strong> 。 +按此习惯书写的字符串存放在字符数组中并以 <strong>空字符结束(null terminated)</strong>。</p> +<blockquote> +<p>在 <code>C++</code> 程序中尽量不要使用 <code>C</code> 风格字符串,容易引起安全漏洞且不方便。</p> +</blockquote> +<p>C标准库String函数,定义在<code>&lt;cstring&gt;</code> 中:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>strlen(p)</code></td> +<td>返回<code>p</code>的长度,空字符不计算在内</td> +</tr> +<tr> +<td><code>strcmp(p1, p2)</code></td> +<td>比较<code>p1</code>和<code>p2</code>的相等性。如果<code>p1==p2</code>,返回0;如果<code>p1&gt;p2</code>,返回一个正值;如果<code>p1&lt;p2</code>,返回一个负值。</td> +</tr> +<tr> +<td><code>strcat(p1, p2)</code></td> +<td>将<code>p2</code>附加到<code>p1</code>之后,返回<code>p1</code></td> +</tr> +<tr> +<td><code>strcpy(p1, p2)</code></td> +<td>将<code>p2</code>拷贝给<code>p1</code>,返回<code>p1</code></td> +</tr> +</tbody> +</table> +<h2 id="多维数组"> + <a href="#%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 多维数组 +</h2><p>严格来说,<code>C++</code> 语言中没有多维数组,所谓的多维数组实际是数组的数组。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用范围-for-语句处理多维数组"> + <a href="#%e4%bd%bf%e7%94%a8%e8%8c%83%e5%9b%b4-for-%e8%af%ad%e5%8f%a5%e5%a4%84%e7%90%86%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 使用范围 <code>for</code> 语句处理多维数组 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">row</span><span class="p">:</span> <span class="n">arr</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">col</span><span class="p">:</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">col</span> <span class="o">=</span> <span class="n">cnt</span><span class="p">;</span> <span class="n">cnt</span> <span class="o">++</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch02 + https://3000ye.com/p/c-primer-ch02/ + Mon, 06 Nov 2023 22:34:55 +0800 + + https://3000ye.com/p/c-primer-ch02/ + <img src="https://3000ye.com/p/c-primer-ch02/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch02" /><h1 id="变量和基本类型"> + <a href="#%e5%8f%98%e9%87%8f%e5%92%8c%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b">#</a> + 变量和基本类型 +</h1><p>变量提供一个具名的、可供程序操作的存储空间。<code>C++</code>中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。</p> +<h2 id="变量的声明和定义"> + <a href="#%e5%8f%98%e9%87%8f%e7%9a%84%e5%a3%b0%e6%98%8e%e5%92%8c%e5%ae%9a%e4%b9%89">#</a> + 变量的声明和定义 +</h2><p>为了允许把程序拆分成多个逻辑部分来编写,<code>C++</code>语言支持<strong>分离式编译(separate complication)</strong> 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。</p> +<p>为了支持分离式编译,<code>C++</code>语言将声明和定义区分开来。<strong>声明(declaration)</strong> 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 <strong>定义(definition)</strong> 负责创建与名字关联的实体。</p> +<p>变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 声明 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义并赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义而非声明,extern失效 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><strong>注意:</strong> 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。</p> +<h3 id="标识符"> + <a href="#%e6%a0%87%e8%af%86%e7%ac%a6">#</a> + 标识符 +</h3><p><code>C++</code>的 <strong>标识符(identifier)</strong> 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。</p> +<h3 id="作用域"> + <a href="#%e4%bd%9c%e7%94%a8%e5%9f%9f">#</a> + 作用域 +</h3><p><strong>作用域(scope)</strong> 是程序的一部分,在其中名字有特定的含义,<code>C++</code>语言中大多数作用域都以花括号分隔。</p> +<p>作用域能彼此包含,被包含(嵌套)的作用域称为 <strong>内层作用域(inner scope)</strong>,包含着别的作用域的作用域称为 <strong>外层作用域(outer scope)</strong>。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 全局作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">inner</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span> <span class="c1">// 内层作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用全局变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 局部重新定义,覆盖全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用局部变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 显式访问全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">::</span><span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="复合类型"> + <a href="#%e5%a4%8d%e5%90%88%e7%b1%bb%e5%9e%8b">#</a> + 复合类型 +</h2><p><strong>复合类型(compound type)</strong> 是指基于其他类型定义的类型,主要介绍引用和指针。</p> +<h3 id="引用"> + <a href="#%e5%bc%95%e7%94%a8">#</a> + 引用 +</h3><p><strong>引用(reference)</strong> 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 <strong>绑定(bind)</strong> 在一起,而不是将初始者拷贝给引用。</p> +<blockquote> +<p>引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。</p> +</blockquote> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 引用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 修改 y 的值,实际是修改 x 的值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针"> + <a href="#%e6%8c%87%e9%92%88">#</a> + 指针 +</h3><p><strong>指针(pointer)</strong> 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:</p> +<ul> +<li>指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。</li> +<li>指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ip1</span><span class="p">,</span> <span class="o">*</span><span class="n">ip2</span><span class="p">;</span> <span class="c1">// ip1 和 ip2 都是指向 int 类型对象的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">dp</span><span class="p">,</span> <span class="o">*</span><span class="n">dp2</span><span class="p">;</span> <span class="c1">// dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>指针存放某个对象的地址,想获取该地址,需要使用 <strong>取地址符(<code>&amp;</code>)</strong>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。</p> +</blockquote> +<p>指针的值(即地址)应属于下列 4 种状态之一:</p> +<ul> +<li>指向一个对象。</li> +<li>指向紧邻对象所占空间的下一个位置。</li> +<li>空指针,意味着指针没有指向任何对象。</li> +<li>无效指针,也就是上述情况之外的其他值。</li> +</ul> +<p>如果指针指向了一个对象,则允许使用 <strong>解引用符(<code>*</code>)</strong> 来访问该对象:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">p</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 由符号 * 得到指针 p 所指的对象,输出 42 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>解引用操作仅适用于有效指针,无效指针无法解引用。</p> +</blockquote> +<p><strong>空指针(null pointer)</strong> 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。</p> +<p>生成空指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> <span class="c1">// 等价于 int *p1 = 0 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。</p> +<p>指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi 为空指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">;</span> <span class="c1">// pi2 存放 i 的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi3</span><span class="p">;</span> <span class="c1">// pi3 的值无法确定 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="n">pi3</span> <span class="o">=</span> <span class="n">pi2</span><span class="p">;</span> <span class="c1">// pi3 和 pi2 指向同一个对象 i +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi2 变为空指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="const-限定符"> + <a href="#const-%e9%99%90%e5%ae%9a%e7%ac%a6">#</a> + const 限定符 +</h2><p>有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字<code>const</code>对变量的类型加以限定:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> <span class="c1">// 限定缓冲区大小为 512 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>默认情况下,<code>const</code>对象被设定为仅在单个文件内有效。当多个文件中出现了同名的<code>const</code>变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上<code>extern</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 定义文件 xxx.cpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 声明文件 xxx.hpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针和-const"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c-const">#</a> + 指针和 const +</h3><p>与引用一样,也可以令指针指向常量与非常量。<strong>指向常量的指针(pointer to const)</strong> 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// val 为常量对像,其值不能改变 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 报错,pi 为普通指针,不能存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 使用指向常量的指针存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// 报错,不能给指向常量的指针赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="处理类型"> + <a href="#%e5%a4%84%e7%90%86%e7%b1%bb%e5%9e%8b">#</a> + 处理类型 +</h2><p>随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。</p> +<h3 id="类型别名"> + <a href="#%e7%b1%bb%e5%9e%8b%e5%88%ab%e5%90%8d">#</a> + 类型别名 +</h3><p><strong>类型别名(type alias)</strong> 是一个名字,它是某种类型的同义词。</p> +<p>使用关键字<code>typedef</code>定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用 <strong>别名声明(alias declaration)</strong> 定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="auto-类型说明符"> + <a href="#auto-%e7%b1%bb%e5%9e%8b%e8%af%b4%e6%98%8e%e7%ac%a6">#</a> + auto 类型说明符 +</h3><p>编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此<code>C++11</code>引入了<code>auto</code>类型说明符,它能自动分析表达式结果的类型。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">item</span> <span class="o">=</span> <span class="n">val1</span> <span class="o">+</span> <span class="n">val2</span><span class="p">;</span> <span class="c1">// item 初始化为 val1 和 val2 相加的结果 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>auto</code>定义或声明多个变量时,所有变量的类型必须一致:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mf">4.2</span><span class="p">;</span> <span class="c1">// 报错,同一行的变量类型必须一致 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="decltype-类型指示符"> + <a href="#decltype-%e7%b1%bb%e5%9e%8b%e6%8c%87%e7%a4%ba%e7%ac%a6">#</a> + decltype 类型指示符 +</h3><p>有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。</p> +<p>因此<code>C++11</code>引入了<code>decltype</code>类型指示符,它的作用是返回操作数的数据类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">decltype</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// z 为 int 类型 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="自定义数据结构"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84">#</a> + 自定义数据结构 +</h2><p>内置的数据类型并不能满足所有的需求,因此<code>c++</code>提供了自定义数据类型的方式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">jack</span><span class="p">{</span><span class="s">&#34;jack&#34;</span><span class="p">,</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 先声明后赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">castor</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="n">castor</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;castor&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">sex</span> <span class="o">=</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">gender</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义数据结构使用别名"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%bd%bf%e7%94%a8%e5%88%ab%e5%90%8d">#</a> + 自定义数据结构使用别名 +</h3><p>和内置数据类型一样,自定义数据结构也能使用别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="n">studeng</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 或直接在定义时使用别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编写自己的头文件"> + <a href="#%e7%bc%96%e5%86%99%e8%87%aa%e5%b7%b1%e7%9a%84%e5%a4%b4%e6%96%87%e4%bb%b6">#</a> + 编写自己的头文件 +</h3><p>当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// studeng.h +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp">#define STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;string&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="cp">#endif +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/categories/c++-primer/page/1/index.html b/categories/c++-primer/page/1/index.html new file mode 100644 index 0000000..4c29a1c --- /dev/null +++ b/categories/c++-primer/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/c++-primer/ + + + + + + diff --git a/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg b/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg differ diff --git a/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg b/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2a47b2d Binary files /dev/null and b/categories/cppjson/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg differ diff --git a/categories/cppjson/JSON-Tutorial.jpg b/categories/cppjson/JSON-Tutorial.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/categories/cppjson/JSON-Tutorial.jpg differ diff --git a/categories/cppjson/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg b/categories/cppjson/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..29d92e5 Binary files /dev/null and b/categories/cppjson/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg differ diff --git a/categories/cppjson/index.html b/categories/cppjson/index.html new file mode 100644 index 0000000..3319930 --- /dev/null +++ b/categories/cppjson/index.html @@ -0,0 +1,567 @@ + + + + +Category: CppJson - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Categories + +

+ +
+
+

2 pages

+

CppJson

+ +

CppJson

+ +
+
+ + +
+ +
+
+ +
+ + + + + +
+ + + +
+
+ + + + + diff --git a/categories/cppjson/index.xml b/categories/cppjson/index.xml new file mode 100644 index 0000000..54e97ab --- /dev/null +++ b/categories/cppjson/index.xml @@ -0,0 +1,637 @@ + + + + CppJson on 3000ye's Blog + https://3000ye.com/categories/cppjson/ + Recent content in CppJson on 3000ye's Blog + Hugo -- gohugo.io + en-us + Mon, 01 Jan 2024 23:18:32 +0800 + CppJson Ch02 + https://3000ye.com/p/cppjson-ch02/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/p/cppjson-ch02/ + <img src="https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch02" /><h1 id="cppjson-第二章节number-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%ba%8c%e7%ab%a0%e8%8a%82number-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第二章节:<code>number</code> 值解析 +</h1> + + + CppJson Ch01 + https://3000ye.com/p/cppjson-ch01/ + Sat, 16 Dec 2023 23:33:44 +0800 + + https://3000ye.com/p/cppjson-ch01/ + <img src="https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch01" /><h1 id="cppjson-第一章节自动测试null-和-bool-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%b8%80%e7%ab%a0%e8%8a%82%e8%87%aa%e5%8a%a8%e6%b5%8b%e8%af%95null-%e5%92%8c-bool-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第一章节:自动测试,<code>NULL</code> 和 <code>bool</code> 值解析 +</h1><h2 id="json-是什么"> + <a href="#json-%e6%98%af%e4%bb%80%e4%b9%88">#</a> + JSON 是什么 +</h2><p>JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为<a class="link" href="https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" target="_blank" rel="noopener" + >ECMA-404</a>。</p> +<p>虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。</p> +<p>例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;title&#34;</span><span class="o">:</span> <span class="s2">&#34;Design Patterns&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;subtitle&#34;</span><span class="o">:</span> <span class="s2">&#34;Elements of Reusable Object-Oriented Software&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;author&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Erich Gamma&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Richard Helm&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Ralph Johnson&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;John Vlissides&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;year&#34;</span><span class="o">:</span> <span class="mi">2009</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;weight&#34;</span><span class="o">:</span> <span class="mf">1.8</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;hardcover&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;publisher&#34;</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Company&#34;</span><span class="o">:</span> <span class="s2">&#34;Pearson Education&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Country&#34;</span><span class="o">:</span> <span class="s2">&#34;India&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;website&#34;</span><span class="o">:</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。</p> +<p>从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:</p> +<ul> +<li>null: 表示为 null</li> +<li>boolean: 表示为 true 或 false</li> +<li>number: 一般的浮点数表示方式,在下一单元详细说明</li> +<li>string: 表示为 &ldquo;&hellip;&rdquo;</li> +<li>array: 表示为 [ &hellip; ]</li> +<li>object: 表示为 { &hellip; }</li> +</ul> +<p>我们要实现的 JSON 库,主要是完成 3 个需求:</p> +<ol> +<li>把 JSON 文本解析为一个树状数据结构(parse)。</li> +<li>提供接口访问该数据结构(access)。</li> +<li>把数据结构转换成 JSON 文本(stringify)。</li> +</ol> +<p><img src="https://3000ye.com/p/cppjson-ch01/assets/requirement.png" + width="440" + height="78" + srcset="https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png 480w, https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + + class="gallery-image" + data-flex-grow="564" + data-flex-basis="1353px" + +></p> +<p>我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。</p> +<h2 id="搭建编译环境"> + <a href="#%e6%90%ad%e5%bb%ba%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#</a> + 搭建编译环境 +</h2><p>我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。</p> +<p>我们的 JSON 库名为 CppJson,代码文件只有 3 个:</p> +<ol> +<li><code>include/cppjson.hpp</code>:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。</li> +<li><code>cppjson.cpp</code>:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。</li> +<li><code>cppjsonTest.cpp</code>:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。</li> +</ol> +<p>为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 <a class="link" href="https://cmake.org/" target="_blank" rel="noopener" + >CMake</a>。</p> +<p>在 OS X 平台中,在命令行通过命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir build +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build +</span></span><span class="line"><span class="cl">cmake -DCMAKE_BUILD_TYPE<span class="o">=</span>Debug .. +</span></span><span class="line"><span class="cl">make +</span></span></code></pre></td></tr></table> +</div> +</div><p>将 Debug 改成 Release 就会生成 Release 配置的 makefile。</p> +<p>在 Vscode 中,可以通过配置 <code>tasks.json</code> 文件来进行自动 build:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//.vscode/tasks.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdir&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-p&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;-DCMAKE_BUILD_TYPE=Debug&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//在此处添加其它CMAKE选项 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;..&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-j16&#34;</span><span class="p">,],</span> <span class="c1">//根据机器cpu核心数量自行调整 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOrder&#34;</span><span class="p">:</span> <span class="s2">&#34;sequence&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOn&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> <span class="s2">&#34;make&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后执行 build 生成的文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ./build/cppjson_test_ch01 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">16/16 <span class="o">(</span>100.00%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。</p> +<h2 id="头文件与-api-设计"> + <a href="#%e5%a4%b4%e6%96%87%e4%bb%b6%e4%b8%8e-api-%e8%ae%be%e8%ae%a1">#</a> + 头文件与 API 设计 +</h2><p><code>Cpp</code> 语言有头文件的概念,需要使用 <code>#include</code>去引入头文件中的类型声明和函数声明。但由于头文件也可以 <code>#include</code> 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#pragma once +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonType</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NULL</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_TRUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_FALSE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NUMBER</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_STRING</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_ARRAY</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_OBJECT</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 <code>cppjson_value</code> 结构体表示,我们会称它为一个 JSON 值(JSON value)。</p> +<p>在此单元中,我们只需要实现 <code>null</code>, <code>true</code> 和 <code>false</code> 的解析,因此该结构体只需要存储一个 <code>cppjsonType</code>,之后的单元会逐步加入其他数据。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjsonType</span> <span class="n">type</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_value</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,我们现在只需要两个 API 函数,一个是解析 JSON:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>传入的 JSON 文本是一个 <code>string</code> 字符串,由于我们不应该改动这个输入字符串,所以使用 <code>const std::string</code> 类型。</p> +<p>返回值是以下这些枚举类中的值,无错误会返回 <code>cppjsonParseCode::OK</code>,其他值在下节解释。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonParseCode</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">OK</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">INVALID_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">ROOT_NOT_SINGULAR</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时我们只需要一个访问结果的函数,就是获取其类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonType</span> <span class="nf">cppjson_get_type</span><span class="p">(</span><span class="k">const</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="json-语法子集"> + <a href="#json-%e8%af%ad%e6%b3%95%e5%ad%90%e9%9b%86">#</a> + JSON 语法子集 +</h2><p>下面是此单元的 JSON 语法子集,使用 <a class="link" href="https://tools.ietf.org/html/rfc7159" target="_blank" rel="noopener" + >RFC7159</a> 中的 <a class="link" href="https://tools.ietf.org/html/rfc5234" target="_blank" rel="noopener" + >ABNF</a> 表示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">JSON-text</span> <span class="err">=</span> <span class="err">ws</span> <span class="err">value</span> <span class="err">ws</span> +</span></span><span class="line"><span class="cl"><span class="err">ws</span> <span class="err">=</span> <span class="err">*(%x</span><span class="mi">20</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">09</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">A</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">D)</span> +</span></span><span class="line"><span class="cl"><span class="err">value</span> <span class="err">=</span> <span class="kc">null</span> <span class="err">/</span> <span class="kc">false</span> <span class="err">/</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="kc">null</span> <span class="err">=</span> <span class="s2">&#34;null&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">false</span> <span class="err">=</span> <span class="s2">&#34;false&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">true</span> <span class="err">=</span> <span class="s2">&#34;true&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当中 <code>%xhh</code> 表示以 16 进制表示的字符,<code>/</code> 是多选一,<code>*</code> 是零或多个,<code>()</code> 用于分组。</p> +<p>那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。</p> +<p>第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。</p> +<p>第三行是说,我们现时的值只可以是 <code>null</code>、<code>false</code> 或 <code>true</code>,它们分别有对应的字面值(literal)。</p> +<p>我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。</p> +<p>在这个 JSON 语法子集下,我们定义 3 种错误码:</p> +<ul> +<li>若一个 JSON 只含有空白,传回 <code>LEPT_PARSE_EXPECT_VALUE</code>。</li> +<li>若一个值之后,在空白之后还有其他字符,传回 <code>LEPT_PARSE_ROOT_NOT_SINGULAR</code>。</li> +<li>若值不是那三种字面值,传回 <code>LEPT_PARSE_INVALID_VALUE</code>。</li> +</ul> +<h2 id="单元测试"> + <a href="#%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95">#</a> + 单元测试 +</h2><p>许多同学在做练习题时,都是以 <code>printf</code>/<code>cout</code> 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。</p> +<p>常用的单元测试框架有 xUnit 系列,如 C++ 的 <a class="link" href="https://github.com/google/googletest" target="_blank" rel="noopener" + >Google Test</a>、C# 的 <a class="link" href="https://www.nunit.org/" target="_blank" rel="noopener" + >NUnit</a>。我们为了简单起见,会编写一个极简单的单元测试方式。</p> +<p>一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:</p> +<ol> +<li>加入一个测试。</li> +<li>运行所有测试,新的测试应该会失败。</li> +<li>编写实现代码。</li> +<li>运行所有测试,若有测试失败回到3。</li> +<li>重构代码。</li> +<li>回到 1。</li> +</ol> +<p>TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。</p> +<p>但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。</p> +<p>回到 CppJson 项目,<code>cppjsonTest.cpp</code> 包含了一个极简的单元测试框架:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;include/cppjson.hpp&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;typeinfo&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">main_ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 测试宏接口 +</span></span></span><span class="line"><span class="cl"><span class="c1">// flag 表示测试点是否通过,如果未通过则打印异常信息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define EXPECT_BASE(flag, expect, actual) \ +</span></span></span><span class="line"><span class="cl"><span class="cp"> do {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> test_count ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> if (flag) test_pass ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> else {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> fprintf(stderr, &#34;%s:%d, expect = %s(%d), actual = %s(%d)\n&#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast&lt;int&gt;(expect), typeid(actual).name(), static_cast&lt;int&gt;(actual));\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> main_ret = 1;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> }\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> } while(0) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse_null</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_value</span> <span class="n">v</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_FALSE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">,</span> <span class="n">cppjson_parse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">,</span> <span class="s">&#34;null&#34;</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">,</span> <span class="n">cppjson_get_type</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse_null</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d/%d (%3.2f%%) passed</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">test_pass</span><span class="p">,</span> <span class="n">test_count</span><span class="p">,</span> <span class="n">test_pass</span> <span class="o">*</span> <span class="mf">100.0</span> <span class="o">/</span> <span class="n">test_count</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">main_ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时只提供了一个 <code>EXPECT_TYPE(expect, actual)</code> 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。</p> +<p>若按照 TDD 的步骤,我们先写一个测试,如上面的 <code>test_parse_null()</code>,而 <code>cppjson_parse()</code> 只返回 <code>cppjsonParseCode::OK</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/CppJson/ch01/cppjsonTest.cpp:30, <span class="nv">expect</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>0<span class="o">)</span>, <span class="nv">actual</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>2<span class="o">)</span> +</span></span><span class="line"><span class="cl">15/16 <span class="o">(</span>93.75%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>为通过的测试是因为 <code>cppjson_parse()</code> 没有把 <code>v.type</code> 改成 <code>cppjsonType::CPPJSON_NULL</code>,造成失败。我们再实现 <code>lept_parse()</code> 令到它能通过测试。</p> +<p>然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。</p> +<h2 id="实现解析器"> + <a href="#%e5%ae%9e%e7%8e%b0%e8%a7%a3%e6%9e%90%e5%99%a8">#</a> + 实现解析器 +</h2><p>有了 API 的设计、单元测试,终于要实现解析器了。</p> +<p>首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 <code>cppjson_context</code> 结构体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_context</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// json 解析函数:ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span> <span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_context</span> <span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 分别解析 ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">cppjson_parse_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span> <span class="o">?</span> <span class="n">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:</p> +<ul> +<li>n ➔ null</li> +<li>t ➔ true</li> +<li>f ➔ false</li> +<li>&quot; ➔ string</li> +<li>0-9/- ➔ number</li> +<li>[ ➔ array</li> +<li>{ ➔ object</li> +</ul> +<p>所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39; &#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\t&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\r&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span> <span class="o">++</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 ws2 之后是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">ROOT_NOT_SINGULAR</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 检测 null 值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_null</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">head</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">head</span> <span class="o">!=</span> <span class="s">&#34;null&#34;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 value +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_value</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;n&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_null</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;t&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_true</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;f&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_false</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;\0&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">EXPECT_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/categories/cppjson/page/1/index.html b/categories/cppjson/page/1/index.html new file mode 100644 index 0000000..4db24dd --- /dev/null +++ b/categories/cppjson/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/cppjson/ + + + + + + diff --git a/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475.jpg b/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475.jpg new file mode 100644 index 0000000..b231665 Binary files /dev/null and b/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475.jpg differ diff --git a/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_250x150_fill_q75_box_smart1.jpg b/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..c29a7be Binary files /dev/null and b/categories/data-structure/dataStructures.5ab4a21b4f01dedb0ccf17dba3921475_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_250x150_fill_q75_box_smart1.jpg differ diff --git a/categories/data-structure/dataStructures.jpg b/categories/data-structure/dataStructures.jpg new file mode 100644 index 0000000..b231665 Binary files /dev/null and b/categories/data-structure/dataStructures.jpg differ diff --git a/categories/data-structure/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg b/categories/data-structure/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4ea68f3 Binary files /dev/null and b/categories/data-structure/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg differ diff --git a/categories/data-structure/index.html b/categories/data-structure/index.html new file mode 100644 index 0000000..4a2709b --- /dev/null +++ b/categories/data-structure/index.html @@ -0,0 +1,547 @@ + + + + +Category: Data Structure - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Categories + +

+ +
+
+

1 page

+

Data Structure

+ +

The way to store and organize data in computer systems.

+ +
+
+ + +
+ +
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git a/categories/data-structure/index.xml b/categories/data-structure/index.xml new file mode 100644 index 0000000..ff7cc66 --- /dev/null +++ b/categories/data-structure/index.xml @@ -0,0 +1,49 @@ + + + + Data Structure on 3000ye's Blog + https://3000ye.com/categories/data-structure/ + Recent content in Data Structure on 3000ye's Blog + Hugo -- gohugo.io + en-us + Wed, 21 Feb 2024 19:14:32 +0800 + Linked List + https://3000ye.com/p/linked-list/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/p/linked-list/ + <img src="https://3000ye.com/p/linked-list/assets/dataStructures.jpg" alt="Featured image of post Linked List" /><h1 id="数据结构链表"> + <a href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e9%93%be%e8%a1%a8">#</a> + 数据结构:链表 +</h1><h2 id="线性表"> + <a href="#%e7%ba%bf%e6%80%a7%e8%a1%a8">#</a> + 线性表 +</h2><p>线性表是具有<strong>相同</strong>数据类型的 $n(n \ge 0)$ 个<strong>数据元素</strong>的<strong>有限序列</strong>,其中 $n$ 为<strong>表长</strong>,当 $n = 0$ 时线性表是一个<strong>空表</strong>。若用 $L$ 命名线性表,则其一般表示为:</p> +<p>$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$</p> +<p>几个概念:</p> +<ul> +<li>$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的<strong>位序</strong>,位序从 $1$ 开始,数组下标从 $0$ 开始。</li> +<li>$a_1$ 是<strong>表头元素</strong>,$a_n$ 是<strong>表尾元素</strong>。</li> +</ul> +<p>除第一个元素外,每个元素有且仅有一个<strong>直接前驱</strong>;除最后一个元素外,每个元素有且仅有一个<strong>直接后继</strong>。</p> +<p>线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。</p> +<h2 id="链表"> + <a href="#%e9%93%be%e8%a1%a8">#</a> + 链表 +</h2><p>链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。</p> +<p>这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。</p> +<p>链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。</p> +<h3 id="单向链表"> + <a href="#%e5%8d%95%e5%90%91%e9%93%be%e8%a1%a8">#</a> + 单向链表 +</h3><p>单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。</p> +<div style='display: flex; justify-content: center;'> +<img src='https://oi-wiki.org/ds/images/list.svg' alt='img' style='zoom:100%;' /> +</div> + + + + + diff --git a/categories/data-structure/page/1/index.html b/categories/data-structure/page/1/index.html new file mode 100644 index 0000000..f8b1954 --- /dev/null +++ b/categories/data-structure/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/data-structure/ + + + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..1d07b80 --- /dev/null +++ b/categories/index.html @@ -0,0 +1,616 @@ + + + + +Categories + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

5 pages

+

Categories

+ +
+
+
+ +
+ + + + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/categories/index.xml b/categories/index.xml new file mode 100644 index 0000000..411daae --- /dev/null +++ b/categories/index.xml @@ -0,0 +1,51 @@ + + + + Categories on 3000ye's Blog + https://3000ye.com/categories/ + Recent content in Categories on 3000ye's Blog + Hugo -- gohugo.io + en-us + Wed, 21 Feb 2024 19:14:32 +0800 + Data Structure + https://3000ye.com/categories/data-structure/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/categories/data-structure/ + <img src="https://3000ye.com/categories/data-structure/dataStructures.jpg" alt="Featured image of post Data Structure" /> + + + Note tools + https://3000ye.com/categories/note-tools/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/categories/note-tools/ + <img src="https://3000ye.com/categories/note-tools/note-tools.jpg" alt="Featured image of post Note tools" /> + + + CppJson + https://3000ye.com/categories/cppjson/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/categories/cppjson/ + <img src="https://3000ye.com/categories/cppjson/JSON-Tutorial.jpg" alt="Featured image of post CppJson" /> + + + C++ Primer + https://3000ye.com/categories/c++-primer/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/categories/c++-primer/ + <img src="https://3000ye.com/categories/c++-primer/c++primer.jpg" alt="Featured image of post C++ Primer" /> + + + Test Category + https://3000ye.com/categories/test-category/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/categories/test-category/ + <img src="https://3000ye.com/categories/test-category/nord.jpg" alt="Featured image of post Test Category" /> + + + + diff --git a/categories/note-tools/index.html b/categories/note-tools/index.html new file mode 100644 index 0000000..6d27cde --- /dev/null +++ b/categories/note-tools/index.html @@ -0,0 +1,607 @@ + + + + +Category: Note tools - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Categories + +

+ +
+
+

4 pages

+

Note tools

+ +

Some useful tools for notes.

+ +
+
+ + +
+ +
+
+ +
+ + + + + + + + + +
+ + + +
+
+ + + + + diff --git a/categories/note-tools/index.xml b/categories/note-tools/index.xml new file mode 100644 index 0000000..fba166c --- /dev/null +++ b/categories/note-tools/index.xml @@ -0,0 +1,837 @@ + + + + Note tools on 3000ye's Blog + https://3000ye.com/categories/note-tools/ + Recent content in Note tools on 3000ye's Blog + Hugo -- gohugo.io + en-us + Fri, 16 Feb 2024 23:33:47 +0800 + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + Slides + https://3000ye.com/p/slides/ + Fri, 20 Oct 2023 10:04:55 +0800 + + https://3000ye.com/p/slides/ + <img src="https://3000ye.com/p/slides/assets/latex.jpg" alt="Featured image of post Slides" /><h1 id="使用-latex-绘制-slides"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e7%bb%98%e5%88%b6-slides">#</a> + 使用 $\LaTeX$ 绘制 Slides +</h1> + + + dhuBachelor + https://3000ye.com/p/dhubachelor/ + Thu, 12 Oct 2023 16:41:55 +0800 + + https://3000ye.com/p/dhubachelor/ + <img src="https://3000ye.com/p/dhubachelor/assets/latex.jpg" alt="Featured image of post dhuBachelor" /><h1 id="东华大学学士毕业论文-latex-模板使用手册"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e5%a3%ab%e6%af%95%e4%b8%9a%e8%ae%ba%e6%96%87-latex-%e6%a8%a1%e6%9d%bf%e4%bd%bf%e7%94%a8%e6%89%8b%e5%86%8c">#</a> + 东华大学学士毕业论文 LaTeX 模板使用手册 +</h1><h2 id="导入模板"> + <a href="#%e5%af%bc%e5%85%a5%e6%a8%a1%e6%9d%bf">#</a> + 导入模板 +</h2><h3 id="使用-git-克隆仓库"> + <a href="#%e4%bd%bf%e7%94%a8-git-%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93">#</a> + 使用 Git 克隆仓库 +</h3><p>将仓库克隆到你需要的位置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@github.com:3000ye/dhuBachelor.git +</span></span><span class="line"><span class="cl"><span class="c1"># 或</span> +</span></span><span class="line"><span class="cl">git clone https://github.com/3000ye/dhuBachelor.git +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="下载-zip-文件"> + <a href="#%e4%b8%8b%e8%bd%bd-zip-%e6%96%87%e4%bb%b6">#</a> + 下载 Zip 文件 +</h3><p>如果你不会使用<code>Git</code>,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载<code>Zip</code>文件:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/zip.png" alt="img" style="zoom:100%"/> +</div> +<h3 id="微信公众号获取"> + <a href="#%e5%be%ae%e4%bf%a1%e5%85%ac%e4%bc%97%e5%8f%b7%e8%8e%b7%e5%8f%96">#</a> + 微信公众号获取 +</h3><p>如果你没有科学代理,无法进入<code>Github</code>,请扫码关注公众号:<code>3000ye Blog</code>然后回复<code>latex模板</code>获取百度网盘分享链接。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/weixin.jpg" alt="img" style="zoom:100%"/> +</div> +<h2 id="开始使用"> + <a href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8">#</a> + 开始使用 +</h2><h3 id="安装字体"> + <a href="#%e5%ae%89%e8%a3%85%e5%ad%97%e4%bd%93">#</a> + 安装字体 +</h3><p>在使用模板之前,请先找到并打开<code>fonts</code>文件夹,安装里面的所有字体。</p> +<h3 id="配置-tex-环境"> + <a href="#%e9%85%8d%e7%bd%ae-tex-%e7%8e%af%e5%a2%83">#</a> + 配置 TeX 环境 +</h3><p>如果你是 $\LaTeX$ 小白,请先行阅读:<a class="link" href="https://3000ye.com/p/elegant-latex/" target="_blank" rel="noopener" + >使用 LaTeX 优雅地完成创作</a>,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。</p> +<h3 id="尝试编译"> + <a href="#%e5%b0%9d%e8%af%95%e7%bc%96%e8%af%91">#</a> + 尝试编译 +</h3><p>使用你喜欢的编辑器,打开<code>dhuBachelor.tex</code>文件,选择<code>xelatex</code>命令编译文件。如果没有出现报错,同目录下会生成一个<code>dhuBachelor.pdf</code>文件,这就是我们的论文。</p> +<h2 id="各个组件说明"> + <a href="#%e5%90%84%e4%b8%aa%e7%bb%84%e4%bb%b6%e8%af%b4%e6%98%8e">#</a> + 各个组件说明 +</h2><p>看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。</p> +<p>本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。</p> +<h3 id="论文题目"> + <a href="#%e8%ae%ba%e6%96%87%e9%a2%98%e7%9b%ae">#</a> + 论文题目 +</h3><p>根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reTitle</span><span class="nb">{</span>中文题目<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\reTitleEN</span><span class="nb">{</span>英文题目<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="摘要"> + <a href="#%e6%91%98%e8%a6%81">#</a> + 摘要 +</h3><p>根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reAbstract</span> <span class="c">% 中文摘要 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reAbstractEN</span> <span class="c">% 英文摘要 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>摘要内容直接写在摘要下方,首行缩进两字符。</p> +<h3 id="关键词"> + <a href="#%e5%85%b3%e9%94%ae%e8%af%8d">#</a> + 关键词 +</h3><p>中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。</p> +<p>英文关键词:Times New Roman(标题加黑)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reKeyword</span><span class="nb">{</span>关键词1,关键词2,关键词3,关键词4<span class="nb">}</span> <span class="c">% 中文关键词 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reKeywordEN</span><span class="nb">{</span>Keyword1, Keyword2, Keyword3<span class="nb">}</span> <span class="c">% 英文关键词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="目录"> + <a href="#%e7%9b%ae%e5%bd%95">#</a> + 目录 +</h3><p>目录标题需居中显示,添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\tableofcontents</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="各级标题"> + <a href="#%e5%90%84%e7%ba%a7%e6%a0%87%e9%a2%98">#</a> + 各级标题 +</h3><p>规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reSection</span><span class="nb">{</span>一级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsection</span><span class="nb">{</span>二级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsubsection</span><span class="nb">{</span>三级标题<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="有序列表"> + <a href="#%e6%9c%89%e5%ba%8f%e5%88%97%e8%a1%a8">#</a> + 有序列表 +</h3><p>规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\orderedList</span><span class="nb">{</span> <span class="c">% 使用 (i) 排序,缩进 2 字符 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> <span class="c">% \par的作用是将内容换行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> +</span></span><span class="line"><span class="cl"> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="数学公式"> + <a href="#%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 数学公式 +</h3><p>行内公式:<code>$f(x) = x + 1$</code></p> +<p>跨行公式:跨行公式请使用<code>equation</code>环境,默认按照章节自动编号。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> x = a<span class="nb">_</span>0 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>1 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>2 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>3 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>4<span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改<code>width</code>大小和图片路径,并根据实际更改图例和索引。</p> +<p>注意:图片需要保存在<code>assets/</code>目录中才能被正确插入,读者可以自己新建其他目录实现插入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] <span class="c">% 图片位于文字下方 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> <span class="c">% 居中 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="c">% 设置图片占页面宽度的比例(默认0.8) +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片标题<span class="nb">}</span> <span class="c">% 图例,按章节编号 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>fig: 数据结构2<span class="nb">}</span> <span class="c">% 图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>有时可能需要多图并排,模板使用<code>minipage</code>来实现并排展示,使用时可以修改子图所占比例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\hspace</span><span class="nb">{</span>1em<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入表格"> + <a href="#%e6%8f%92%e5%85%a5%e8%a1%a8%e6%a0%bc">#</a> + 插入表格 +</h3><p>使用 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a> 工具生成表格代码后,需要手动添加分割线(<code>\toprule, \midrule, \bottomrule</code>),以达到三线表的格式要求。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||l<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> parameter <span class="nb">&amp;</span> Description <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">I</span><span class="s">$</span> <span class="nb">&amp;</span> Land area collection <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">J</span><span class="s">$</span> <span class="nb">&amp;</span> Flower pollination demand set <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">D_j</span><span class="s">$</span> <span class="nb">&amp;</span> Number of pollinating bees required for flower pollination <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">T_k</span><span class="s">$</span> <span class="nb">&amp;</span> Honeycomb size grade, <span class="s">$</span><span class="nb">k </span><span class="o">=</span><span class="nb"> </span><span class="m">1</span><span class="nb">, </span><span class="m">2</span><span class="nb">, </span><span class="nv">\cdots</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">B</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum number of hive <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">R_{ik}</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum influence radius of a single honeycomb <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 一个表<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>对于需要多表并排的情况,和图片的方式类似,使用<code>minipage</code>来实现:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格1标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格2标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bb%a3%e7%a0%81">#</a> + 插入代码 +</h3><p>可以直接在<code>.tex</code>文件中编写代码,并指定语言和标题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>lstlisting<span class="nb">}</span>[language=c++,title=<span class="nb">{</span>code.cpp<span class="nb">}</span>] +</span></span><span class="line"><span class="cl">#include &#34;bits/stdc++.h&#34; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">using namespace std; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">int main() <span class="nb">{</span> +</span></span><span class="line"><span class="cl"> cout &lt;&lt; &#34;3000ye 的 LaTeX 模板!&#34; &lt;&lt; endl; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> return 0; +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>lstlisting<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在<code>assets/</code>目录下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\lstinputlisting</span><span class="na">[language=c++, title=code.cpp]</span><span class="nb">{</span>code/code.cpp<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入伪代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bc%aa%e4%bb%a3%e7%a0%81">#</a> + 插入伪代码 +</h3><p>使用宏包<code>algorithm, algorithmic</code>来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>Example Pseudocode<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="m">0</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\IF</span> <span class="nb">{</span><span class="s">$</span><span class="nb">x</span><span class="nv">\leq</span><span class="nb"> </span><span class="m">0</span><span class="s">$</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">+</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ELSE</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">-</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ENDIF</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="参考文献"> + <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae">#</a> + 参考文献 +</h3><p>参考文献使用<code>\bibitem</code>来添加,添加时需要手动更改<code>{RNi}</code>索引(<code>i</code>是你文献的序号)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reference</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN1<span class="nb">}</span> 参考文献1 +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN2<span class="nb">}</span> 参考文献2 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="致谢"> + <a href="#%e8%87%b4%e8%b0%a2">#</a> + 致谢 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reThanks</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> 致谢,3000ye 的 <span class="k">\LaTeX</span> 模板! +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Elegant LaTeX + https://3000ye.com/p/elegant-latex/ + Mon, 09 Oct 2023 17:18:55 +0800 + + https://3000ye.com/p/elegant-latex/ + <img src="https://3000ye.com/p/elegant-latex/assets/latex.jpg" alt="Featured image of post Elegant LaTeX" /><h1 id="使用-latex-优雅地完成创作"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e4%bc%98%e9%9b%85%e5%9c%b0%e5%ae%8c%e6%88%90%e5%88%9b%e4%bd%9c">#</a> + 使用 $\LaTeX$ 优雅地完成创作 +</h1><p>$\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\LaTeX$ 使用 $\TeX$ 作为它的排版引擎,学习 $\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。</p> +<h2 id="从安装-tex-引擎开始"> + <a href="#%e4%bb%8e%e5%ae%89%e8%a3%85-tex-%e5%bc%95%e6%93%8e%e5%bc%80%e5%a7%8b">#</a> + 从安装 $\TeX$ 引擎开始 +</h2><p>$\TeX$ 引擎类似于 <code>gcc/g++</code> 或 <code>Python</code>,用于编译 $\LaTeX$ 文档。</p> +<p>不同平台中 $\TeX$ 的安装方法不尽相同,本文提供:<code>Windows11</code>、<code>Linux(Ubuntu 22.04)</code>、<code>MacOs(12.7)</code>、<code>Windows11-wsl2(Ubuntu22.04)</code>的安装方法。</p> +<p>如果你只是想简单体验 $\LaTeX$,可以使用 <a class="link" href="https://www.overleaf.com/" target="_blank" rel="noopener" + >overleaf</a> 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。</p> +<h3 id="windows11"> + <a href="#windows11">#</a> + Windows11 +</h3><p>进入网站<a class="link" href="https://tug.org/texlive/windows.html#install" target="_blank" rel="noopener" + >tug.org for windows</a>,点击<code>install-tl-windows.exe</code>下载 $\TeX$ 安装器,然后运行安装即可。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/windowsInstall.png" alt="img" style="zoom:100%"/> +</div> +<p>不过,这种方法需要一直联网安装,网速不好的环境可以直接下载<code>iso</code>镜像进行本地安装。本文给出清华源镜像地址:<a class="link" href="https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/" target="_blank" rel="noopener" + >mirrors.tuna</a>,下载后缀为<code>.iso</code>的文件(只用下载一个)。</p> +<p>下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击<code>install-tl-windows.bat</code>文件,使用管理员打开,然后按照指引安装即可。</p> +<p>最新版本的安装器会自动添加环境变量,安装完成后打开<code>cmd</code>然后输入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/W32TeX<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="linuxubuntu2204"> + <a href="#linuxubuntu2204">#</a> + Linux(Ubuntu22.04) +</h3><p>打开终端,然后执行安装命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update +</span></span><span class="line"><span class="cl">sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install texlive-full +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/Debian<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="macos127"> + <a href="#macos127">#</a> + MacOs(12.7) +</h3><p>打开终端,然后执行安装命令(推荐安装无窗体版本):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install mactex-no-gui +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="windows11-wsl2ubuntu2204"> + <a href="#windows11-wsl2ubuntu2204">#</a> + Windows11-wsl2(Ubuntu22.04) +</h3><p>在<code>wsl2</code>中安装方式与在<code>Linux</code>中一样。</p> +<h2 id="找到属于你的编辑器"> + <a href="#%e6%89%be%e5%88%b0%e5%b1%9e%e4%ba%8e%e4%bd%a0%e7%9a%84%e7%bc%96%e8%be%91%e5%99%a8">#</a> + 找到属于你的编辑器 +</h2><p>市面上有很多 $\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:</p> +<ul> +<li>全平台通用: +<ul> +<li><code>Vs Code</code>:作为地表最强编辑器,<code>Vs Code</code>拥有非常丰富的 $\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。</li> +<li><code>Jetbrains</code>: 与<code>Vs Code</code>相对应的是<code>Jetbrains</code>系列, 其虽然也有 $\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。</li> +<li><code>Neovim</code>:如果说<code>Vs Code</code>是编辑器中的王后,那么<code>nvim</code>就是国王。<code>nvim</code>可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。</li> +<li><code>sublime text</code>:<code>nvim</code>固然强大,但其难以上手的特点使得很多人对其望而却步。<code>sublime</code>打破了这个束缚,其界面优雅程度不亚于<code>nvim</code>,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。</li> +<li><code>TexStudio</code>:<code>texlive</code>默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。</li> +</ul> +</li> +<li><code>Windows</code>独占: +<ul> +<li><code>Winedt 11</code>:如果不考虑跨平台,那么<code>Winedt 11</code>就是<code>Windows</code>上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。</li> +</ul> +</li> +</ul> +<p>综上所述,笔者最推荐<code>Vs Code</code>,但如果你只有<code>Windows</code>平台的使用需求并不介意一点费用的话,请果断购买<code>Winedt 11</code>。同样的,<code>MacOs</code>也拥有独占编辑器,但笔者没用过,在此不做评价。</p> +<p>关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。</p> +<h2 id="一本教程入门-latex-语法"> + <a href="#%e4%b8%80%e6%9c%ac%e6%95%99%e7%a8%8b%e5%85%a5%e9%97%a8-latex-%e8%af%ad%e6%b3%95">#</a> + 一本教程入门 $\LaTeX$ 语法 +</h2><p>对于所有初学者来说,<a class="link" href="https://mirror-hk.koddos.net/CTAN/info/lshort/chinese/lshort-zh-cn.pdf" target="_blank" rel="noopener" + >Ishort-zh-cn</a>都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。</p> +<p>如果你已经完成了所有的案例,相信你已经对 $\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。</p> +<h3 id="打印中文"> + <a href="#%e6%89%93%e5%8d%b0%e4%b8%ad%e6%96%87">#</a> + 打印中文 +</h3><p>$\LaTeX$ 默认只打印英文,如果没有合理的设置,<code>.tex</code>文件中的中文将无法正确打印。</p> +<p>从下图可以看出,目前支持全平台通用的方案只有<code>XeLaTeX</code>和<code>LuaTeX</code>。因此,主流方案是一般使用<code>xelatex+ctex</code>编译方案,底层调用<code>xeCJK</code>字符集来实现中文打印。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/xeCJK.png' alt='img' style='zoom:100%;' /> +</div> +<p>使用时只需在导言区加入,编译器会使用默认字体进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8]</span><span class="nb">{</span>ctex<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果要指定字体,则需分别设置<code>font-family</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8, fontset=none]</span><span class="nb">{</span>ctex<span class="nb">}</span> <span class="c">% 清除默认字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmainfont</span><span class="nb">{</span>SimSun<span class="nb">}</span>[AutoFakeBold=true, BoldFont=<span class="nb">{</span>SimHei<span class="nb">}</span>, ItalicFont=<span class="nb">{</span>KaiTi<span class="nb">}</span>] <span class="c">% 正文字体(宋体,黑体,楷体) +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKsansfont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>KaiTi<span class="nb">}</span> <span class="c">% 无衬线字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmonofont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>SimHei<span class="nb">}</span> <span class="c">% 等宽字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>同样的,英文也可以自定义字体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\setmainfont</span><span class="nb">{</span>Times New Roman<span class="nb">}</span> <span class="c">% 设置英文字体为新罗马体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>详细配置可以参考:<a class="link" href="https://zhuanlan.zhihu.com/p/538459335" target="_blank" rel="noopener" + >LaTex 中文字体配置指南</a></p> +</blockquote> +<h3 id="打印数学公式"> + <a href="#%e6%89%93%e5%8d%b0%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 打印数学公式 +</h3><p>你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\LaTeX$,这也是 $\LaTeX$ 为什么被奉为珍宝的原因之一。</p> +<p>但是笔者并不建议读者专门花时间来学习如何编写 $\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。</p> +<ul> +<li>在线数学公式生成平台:<a class="link" href="https://www.latexlive.com/" target="_blank" rel="noopener" + >latexlive</a>可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。</li> +<li><code>MathType</code>:如果你是<code>Windows</code>用户,那么强烈建议使用<code>MathType</code>来生成数学公式的 $\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。</li> +<li><code>mathpix</code>:这是一款专为 $\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。</li> +</ul> +<h3 id="绘制表格"> + <a href="#%e7%bb%98%e5%88%b6%e8%a1%a8%e6%a0%bc">#</a> + 绘制表格 +</h3><p>你一定使用过<code>Excel</code>来绘制表格,但是在 $\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。</p> +<p>但不用担心,本文为你介绍开源项目 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a>。这是一款<code>Excel</code>插件,可以将你在<code>Excel</code>中绘制的表格自动转译为 $\LaTeX$ 代码。</p> +<p>但是这个插件并不是万能的,比如绘制三线表,即使是在<code>Excel</code>中也较为繁琐。因此,最好的处理方式是使用<code>excel2latex</code>插件生成表格主体,然后再自己添加分隔格式。</p> +<h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>绘制表格和插入图片并称为 $\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。</p> +<h2 id="学会使用代码片段"> + <a href="#%e5%ad%a6%e4%bc%9a%e4%bd%bf%e7%94%a8%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + 学会使用代码片段 +</h2><p>阅读到这里,相信你已经能够使用 $\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写<code>.sty</code>和<code>.cls</code>文件),但这对初学者来说太难了。</p> +<p>因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。</p> +<p>代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。</p> +<h3 id="vs-code配置代码片段"> + <a href="#vs-code%e9%85%8d%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Vs Code配置代码片段 +</h3><p>点击左下角的设置按钮,然后点击设置用户代码片段:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/snippets.jpg' alt='img' style='zoom:100%;' /> +</div> +<p>在弹出的窗口中输入<code>latex</code>然后选中即可跳转到<code>latex.json</code>文件,我们可以在这里设置我们的代码片段。</p> +<p>比如这段设置,保存文件后我们只需要在<code>.tex</code>后缀的文件中输入<code>insertImg</code>然后回车就会自动填充以下代码,并且使用<code>tab</code>来依次输入参数。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;insertImg&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;prefix&#34;</span><span class="p">:</span> <span class="s2">&#34;insertImg&#34;</span><span class="p">,</span> <span class="c1">// 代码片段别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;body&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="c1">// 代码片段主体 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\begin{figure}[H]&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\centering&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\includegraphics[width=0.8\\textwidth]{$1}&#34;</span><span class="p">,</span> <span class="c1">// 参数1:图片路径 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\caption{$2}&#34;</span><span class="p">,</span> <span class="c1">// 参数2:图片标题 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\label{$3}&#34;</span><span class="p">,</span> <span class="c1">// 参数3:图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\end{figure}$0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;insert one img with 0.8 width&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="winedt-设置代码片段"> + <a href="#winedt-%e8%ae%be%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Winedt 设置代码片段 +</h3><p>依次点击<code>Option -&gt; Options Interface -&gt; Menus and Toolbar -&gt; Main Menu</code>,修改或添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">ITEM</span><span class="o">=</span><span class="s">&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> CAPTION=&#34;&amp;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> IMAGE=&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> MACRO=&#34;Exe(&#39;%b\Menus\Insert\Image.edt&#39;);&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> SHORTCUT=&#34;49222::Ctrl+Alt+F&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> REQ_DOCUMENT=1 </span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后就可以使用快捷键<code>Ctrl+Alt+F</code>填充插入图片的代码片段。</p> +<h2 id="使用模板来专注内容"> + <a href="#%e4%bd%bf%e7%94%a8%e6%a8%a1%e6%9d%bf%e6%9d%a5%e4%b8%93%e6%b3%a8%e5%86%85%e5%ae%b9">#</a> + 使用模板来专注内容 +</h2><p>使用 $\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\LaTeX$ 的初衷。</p> +<p>因此,常见期刊都会提供对应的 $\LaTeX$ 风格模板和示例,其中主要文件通常为:</p> +<ul> +<li><code>.sty</code>:$\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。</li> +<li><code>.cls</code>:$\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。</li> +<li><code>.tex</code>:示例文件,通常会包括论文中会用到的所有样式的示例代码。</li> +</ul> +<p>阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。</p> + + + + + diff --git a/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47.jpg b/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47.jpg new file mode 100644 index 0000000..77bd1fc Binary files /dev/null and b/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47.jpg differ diff --git a/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47_hue8fc5337cd4130144b40a68732c7ca7b_203450_250x150_fill_q75_box_smart1.jpg b/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47_hue8fc5337cd4130144b40a68732c7ca7b_203450_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..186b9b7 Binary files /dev/null and b/categories/note-tools/note-tools.36d82f8521cb0f68b73f81759506fc47_hue8fc5337cd4130144b40a68732c7ca7b_203450_250x150_fill_q75_box_smart1.jpg differ diff --git a/categories/note-tools/note-tools.jpg b/categories/note-tools/note-tools.jpg new file mode 100644 index 0000000..77bd1fc Binary files /dev/null and b/categories/note-tools/note-tools.jpg differ diff --git a/categories/note-tools/note-tools_hue8fc5337cd4130144b40a68732c7ca7b_203450_120x120_fill_q75_box_smart1.jpg b/categories/note-tools/note-tools_hue8fc5337cd4130144b40a68732c7ca7b_203450_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2453308 Binary files /dev/null and b/categories/note-tools/note-tools_hue8fc5337cd4130144b40a68732c7ca7b_203450_120x120_fill_q75_box_smart1.jpg differ diff --git a/categories/note-tools/page/1/index.html b/categories/note-tools/page/1/index.html new file mode 100644 index 0000000..a1d48fa --- /dev/null +++ b/categories/note-tools/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/note-tools/ + + + + + + diff --git a/categories/page/1/index.html b/categories/page/1/index.html new file mode 100644 index 0000000..9d001b6 --- /dev/null +++ b/categories/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/ + + + + + + diff --git a/categories/test-category/index.html b/categories/test-category/index.html new file mode 100644 index 0000000..f390654 --- /dev/null +++ b/categories/test-category/index.html @@ -0,0 +1,547 @@ + + + + +Category: Test Category - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Categories + +

+ +
+
+

1 page

+

Test Category

+ +

Test pages in test category.

+ +
+
+ + +
+ +
+
+ +
+ + + +
+ + + +
+
+ + + + + diff --git a/categories/test-category/index.xml b/categories/test-category/index.xml new file mode 100644 index 0000000..58daa99 --- /dev/null +++ b/categories/test-category/index.xml @@ -0,0 +1,78 @@ + + + + Test Category on 3000ye's Blog + https://3000ye.com/categories/test-category/ + Recent content in Test Category on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 08 Oct 2023 14:30:49 +0000 + Test + https://3000ye.com/p/test/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/test/ + <img src="https://3000ye.com/p/test/nord.jpg" alt="Featured image of post Test" /><h1 id="test-page"> + <a href="#test-page">#</a> + Test page +</h1><h2 id="picture"> + <a href="#picture">#</a> + picture +</h2><p><img src="https://3000ye.com/p/test/nord.jpg" + width="5472" + height="2976" + srcset="https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg 480w, https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="nord" + + + class="gallery-image" + data-flex-grow="183" + data-flex-basis="441px" + +></p> +<h2 id="math"> + <a href="#math">#</a> + math +</h2><p>$$ +\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } +$$</p> +<h2 id="code"> + <a href="#code">#</a> + code +</h2><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;bits/stdc++.h&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello World!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="test-latex"> + <a href="#test-latex">#</a> + Test $\LaTeX$ +</h2><h2 id="frac23"> + <a href="#frac23">#</a> + $\frac{2}{3}$ +</h2> + + + + diff --git a/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa.jpg b/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa.jpg new file mode 100644 index 0000000..9776f86 Binary files /dev/null and b/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa.jpg differ diff --git a/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa_hu694f0055e3faf079179eb7eb8ca96cd4_877866_250x150_fill_q75_box_smart1.jpg b/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa_hu694f0055e3faf079179eb7eb8ca96cd4_877866_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..32621f1 Binary files /dev/null and b/categories/test-category/nord.51518c74ad814eb34226593dbbee97aa_hu694f0055e3faf079179eb7eb8ca96cd4_877866_250x150_fill_q75_box_smart1.jpg differ diff --git a/categories/test-category/nord.jpg b/categories/test-category/nord.jpg new file mode 100644 index 0000000..9776f86 Binary files /dev/null and b/categories/test-category/nord.jpg differ diff --git a/categories/test-category/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg b/categories/test-category/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2b077ad Binary files /dev/null and b/categories/test-category/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg differ diff --git a/categories/test-category/page/1/index.html b/categories/test-category/page/1/index.html new file mode 100644 index 0000000..dbd27c2 --- /dev/null +++ b/categories/test-category/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/categories/test-category/ + + + + + + diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..e53125d Binary files /dev/null and b/favicon.png differ diff --git a/friends/index.html b/friends/index.html new file mode 100644 index 0000000..370c95d --- /dev/null +++ b/friends/index.html @@ -0,0 +1,541 @@ + + + + +Friends + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + +
+

+ Friends +

+ + +
+ + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + +
+ + + +
+ + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/friends/logos/fatzard.png b/friends/logos/fatzard.png new file mode 100644 index 0000000..82dadd7 Binary files /dev/null and b/friends/logos/fatzard.png differ diff --git a/img/CC_huc1c3a87a225e3496993fd33136e7af5c_6423131_300x0_resize_q75_box.jpg b/img/CC_huc1c3a87a225e3496993fd33136e7af5c_6423131_300x0_resize_q75_box.jpg new file mode 100644 index 0000000..e33ed28 Binary files /dev/null and b/img/CC_huc1c3a87a225e3496993fd33136e7af5c_6423131_300x0_resize_q75_box.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..917ee3e --- /dev/null +++ b/index.html @@ -0,0 +1,1068 @@ + + + + + +3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+
+ + Featured image of post Clear C++ Ch09: Function application + + +
+ + +
+ + +
+

+ Clear C++ Ch09: Function application +

+ + +

+ Clear C++ Ch09: Function application +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+ + +
+

+ Threads +

+ + +

+ MulThread and Thread Pool +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Linked List + + +
+ + +
+ + + + +
+

+ Linked List +

+ + +

+ Data Structure: Linked List (CPP and Python) +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post dhuBeamer + + +
+ + +
+ + + + +
+

+ dhuBeamer +

+ + +

+ Donghua University LaTeX Beamer Template +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post CppJson Ch02 + + +
+ + +
+ + + + +
+

+ CppJson Ch02 +

+ + +

+ My CppJson tutorial ch02: parse number +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post 2023 Summary + + +
+ + +
+ + +
+

+ 2023 Summary +

+ + +

+ Wrap up 2023 with desktop changes +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Happy Geek PRD + + +
+ + +
+ + +
+

+ Happy Geek PRD +

+ + +

+ Make PRD that Geek happy to read +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + + + +
+
+ + + + + diff --git a/index.xml b/index.xml new file mode 100644 index 0000000..4e9876e --- /dev/null +++ b/index.xml @@ -0,0 +1,4762 @@ + + + + 3000ye's Blog + https://3000ye.com/ + Recent content on 3000ye's Blog + Hugo -- gohugo.io + en-us + Thu, 21 Mar 2024 23:12:32 +0800 + Clear C++ Ch09: Function application + https://3000ye.com/p/clear-c-ch09-function-application/ + Thu, 21 Mar 2024 23:12:32 +0800 + + https://3000ye.com/p/clear-c-ch09-function-application/ + <img src="https://3000ye.com/p/clear-c-ch09-function-application/assets/clearcpp.jpg" alt="Featured image of post Clear C++ Ch09: Function application" /><h1 id="函数的应用"> + <a href="#%e5%87%bd%e6%95%b0%e7%9a%84%e5%ba%94%e7%94%a8">#</a> + 函数的应用 +</h1><h2 id="函数模板"> + <a href="#%e5%87%bd%e6%95%b0%e6%a8%a1%e6%9d%bf">#</a> + 函数模板 +</h2> + + + Threads + https://3000ye.com/p/threads/ + Mon, 18 Mar 2024 21:06:19 +0800 + + https://3000ye.com/p/threads/ + <h1 id="线程管理"> + <a href="#%e7%ba%bf%e7%a8%8b%e7%ae%a1%e7%90%86">#</a> + 线程管理 +</h1><p>在程序开发中,很多时候都是程序都是串行处理,这没有什么问题。然而,在某些重复工作较多,且性能要求较高的场景,串行处理所需时间往往过于漫长。</p> +<p>因此,合理地使用线程管理有助于我们程序的更好运行。但是请注意,不是一味地使用多线程或线程池就一定是好的,适合运行场景的处理方式才是最好的。</p> +<h2 id="单线程"> + <a href="#%e5%8d%95%e7%ba%bf%e7%a8%8b">#</a> + 单线程 +</h2><p>在本文中,我们考虑这样一个场景:有一个非常耗时的计算函数,其计算一次需要 <code>time</code> 秒。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">waitTime</span><span class="p">(</span><span class="kt">int</span> <span class="n">time</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">seconds</span> <span class="n">duration</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">duration</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;wait for &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">time</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; seconds&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>按照常规的做法,我们串行地对批量任务进行处理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以预见,这种处理方法会非常耗时。</p> +<h2 id="多线程"> + <a href="#%e5%a4%9a%e7%ba%bf%e7%a8%8b">#</a> + 多线程 +</h2><p>为了加速程序运行和处理的速度,我们可以使用多线程来并行处理。多线程的思想是:先将要进行的任务放入队列中,然后让这些任务同时运行,最终实现加速程序运行的效果。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mulTreads</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">,</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;&gt;</span> <span class="n">fetchingThreads</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">l</span> <span class="o">=</span> <span class="n">todoList</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">l</span><span class="p">;</span> <span class="n">i</span> <span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span><span class="p">(</span><span class="n">waitTime</span><span class="p">,</span> <span class="n">todoList</span><span class="p">[</span><span class="n">i</span><span class="p">]));</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">front</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">begin</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">threadPtr</span> <span class="p">:</span> <span class="n">fetchingThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">threadPtr</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">mulTreads</span><span class="p">(</span><span class="n">todoList</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="线程池"> + <a href="#%e7%ba%bf%e7%a8%8b%e6%b1%a0">#</a> + 线程池 +</h2><p>多线程虽好,但是频繁地创建和删除线程,同样会造成时间和空间的浪费。因此,线程池出现了,在每次任务完成之后,保留现有线程并继续处理下一个任务。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ThreadPool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span><span class="p">(</span><span class="n">size_t</span> <span class="n">numThreads</span><span class="p">)</span> <span class="o">:</span> <span class="n">stop</span><span class="p">(</span><span class="nb">false</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numThreads</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">workers</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">([</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;</span> <span class="n">task</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">wait</span><span class="p">(</span><span class="n">lock</span><span class="p">,</span> <span class="p">[</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stop</span> <span class="o">||</span> <span class="o">!</span><span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">();</span> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">stop</span> <span class="o">&amp;&amp;</span> <span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">task</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">tasks</span><span class="p">.</span><span class="n">front</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">task</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 添加任务到线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">F</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="n">enqueue</span><span class="p">(</span><span class="n">F</span><span class="o">&amp;&amp;</span> <span class="n">task</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">emplace</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span><span class="p">(</span><span class="n">task</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_one</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">~</span><span class="n">ThreadPool</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">stop</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_all</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span> <span class="o">&amp;</span><span class="nl">worker</span> <span class="p">:</span> <span class="n">workers</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">worker</span><span class="p">.</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span> <span class="n">workers</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;&gt;</span> <span class="n">tasks</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">mutex</span> <span class="n">queueMutex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">condition_variable</span> <span class="n">condition</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">stop</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">int</span> <span class="n">numThreads</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span> <span class="n">pool</span><span class="p">(</span><span class="n">numThreads</span><span class="p">);</span> <span class="c1">// 创建包含7个线程的线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 向线程池中添加任务 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">pool</span><span class="p">.</span><span class="n">enqueue</span><span class="p">([</span><span class="n">time</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Linked List + https://3000ye.com/p/linked-list/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/p/linked-list/ + <img src="https://3000ye.com/p/linked-list/assets/dataStructures.jpg" alt="Featured image of post Linked List" /><h1 id="数据结构链表"> + <a href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e9%93%be%e8%a1%a8">#</a> + 数据结构:链表 +</h1><h2 id="线性表"> + <a href="#%e7%ba%bf%e6%80%a7%e8%a1%a8">#</a> + 线性表 +</h2><p>线性表是具有<strong>相同</strong>数据类型的 $n(n \ge 0)$ 个<strong>数据元素</strong>的<strong>有限序列</strong>,其中 $n$ 为<strong>表长</strong>,当 $n = 0$ 时线性表是一个<strong>空表</strong>。若用 $L$ 命名线性表,则其一般表示为:</p> +<p>$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$</p> +<p>几个概念:</p> +<ul> +<li>$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的<strong>位序</strong>,位序从 $1$ 开始,数组下标从 $0$ 开始。</li> +<li>$a_1$ 是<strong>表头元素</strong>,$a_n$ 是<strong>表尾元素</strong>。</li> +</ul> +<p>除第一个元素外,每个元素有且仅有一个<strong>直接前驱</strong>;除最后一个元素外,每个元素有且仅有一个<strong>直接后继</strong>。</p> +<p>线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。</p> +<h2 id="链表"> + <a href="#%e9%93%be%e8%a1%a8">#</a> + 链表 +</h2><p>链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。</p> +<p>这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。</p> +<p>链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。</p> +<h3 id="单向链表"> + <a href="#%e5%8d%95%e5%90%91%e9%93%be%e8%a1%a8">#</a> + 单向链表 +</h3><p>单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。</p> +<div style='display: flex; justify-content: center;'> +<img src='https://oi-wiki.org/ds/images/list.svg' alt='img' style='zoom:100%;' /> +</div> + + + + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + CppJson Ch02 + https://3000ye.com/p/cppjson-ch02/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/p/cppjson-ch02/ + <img src="https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch02" /><h1 id="cppjson-第二章节number-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%ba%8c%e7%ab%a0%e8%8a%82number-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第二章节:<code>number</code> 值解析 +</h1> + + + 2023 Summary + https://3000ye.com/p/2023-summary/ + Sun, 31 Dec 2023 15:40:26 +0800 + + https://3000ye.com/p/2023-summary/ + <img src="https://3000ye.com/p/2023-summary/assets/wakatime.png" alt="Featured image of post 2023 Summary" /><h1 id="用桌面变化来总结-2023"> + <a href="#%e7%94%a8%e6%a1%8c%e9%9d%a2%e5%8f%98%e5%8c%96%e6%9d%a5%e6%80%bb%e7%bb%93-2023">#</a> + 用桌面变化来总结 2023 +</h1><h2 id="3月16日我有-5-块屏幕"> + <a href="#3%e6%9c%8816%e6%97%a5%e6%88%91%e6%9c%89-5-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 3月16日,我有 5 块屏幕 +</h2><p>这段时间是我同时使用屏幕数量最多的时候,主屏幕写代码,副屏幕看网页;surface 和工控屏做直播推流;iPad 负责预览直播画面:</p> +<ul> +<li>主屏幕(中间):27 寸 2k,副屏幕(最左):15.6 寸 2k</li> +<li>surface(最右),工控屏(最小):7 寸 1080p</li> +<li>iPad(非统一背景)</li> +</ul> +<p>几乎每天都在宿舍,买了本《明解 C++》一边学语言一边学算法。虽然算法已经忘得差不多了,但坚实的 <code>C++</code> 基础对我帮助很大,甚至我现在实习的主要内容就是 <code>C++</code> 开发。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/03-16.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="5月10日我有-2-块屏幕"> + <a href="#5%e6%9c%8810%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 5月10日,我有 2 块屏幕 +</h2><p>学完了 <code>C++</code> 语言基础后,我开始意识到数据结构对编程的重要性。同时也有为考研做准备的打算,我开始每天往返图书馆,刷王道考研的 CS408 课程。</p> +<p>为了方便往返图书馆并尽可能满足我多屏幕的需求,买了一台可以 180 度开合的笔记本(ThinkBook 14)和一个立式支架,搭配 ThinkPad 的 GaN 100W 充电器和雷电线,只需 2 根线完成所有的连接。这样的好处是可以上面写代码下面看课程,同时使用外接键盘不会很突兀。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/05-10.jpg' alt='img' style='zoom:15%;' /> +</div> +<h2 id="6月18日我有-2-块屏幕"> + <a href="#6%e6%9c%8818%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 6月18日,我有 2 块屏幕 +</h2><p>春季学期结束后,我也放弃了考研的打算,开始往机器学习和 Pytorch 的方向学习。翻出以前买的西瓜书和南瓜书,对着 Github 上的开源笔记边看边学。由于宿舍桌子太窄了,放三块屏幕过于拥挤,于是将 15.6 寸的副屏收了起来。</p> +<p>趁着 618 换了罗技的 master 3s 鼠标,手感确实比雷蛇的 click Pro 好很多。同时每天都坚持录视频,之前买的麦克风也派上了用场。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/06-18.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="7月19日我有-1-块屏幕"> + <a href="#7%e6%9c%8819%e6%97%a5%e6%88%91%e6%9c%89-1-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 7月19日,我有 1 块屏幕 +</h2><p>随着 Pytorch 学习的深入,笔记本的核显已经无法满足需求。而此时矿潮已经开始逐渐褪去,随着 4090 的发布老显卡的价格开始走低,索性在咸鱼上淘了一张服务器版的 2080Ti:</p> +<ul> +<li>300A 核心,三星显存</li> +<li>纯铜涡轮散热,尾部供电</li> +</ul> +<p>其 11G 的显存足以满足入门需求,同时后期还可以加焊 22G 显存,是一张性能和成长空间都不错的显卡。</p> +<p>同时为了最大限度使用显卡,直接把机器刷成了 Ubuntu server,将之前的监控屏用来监控显卡状态。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/07-19.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="7月20日我有-3-块屏幕"> + <a href="#7%e6%9c%8820%e6%97%a5%e6%88%91%e6%9c%89-3-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 7月20日,我有 3 块屏幕 +</h2><p>搞深度学习,真的是屏幕越多越好,有太多的资料和数据集看不过来:</p> +<ul> +<li>主显示器用来看资料</li> +<li>副显示器用来看数据集(竖直摆放)</li> +<li>笔记本屏幕用来写代码</li> +</ul> +<div style='display: flex; justify-content: center;'> +<img src='assets/07-20.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="9月6日我有-1-块屏幕"> + <a href="#9%e6%9c%886%e6%97%a5%e6%88%91%e6%9c%89-1-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 9月6日,我有 1 块屏幕 +</h2><p>秋季学期重回宿舍后,开始迷上了 Minecraft 和泡茶,一杯茶一个种子就是一天。这段时间是最放松的时间,每天都在搭方块。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/09-06.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="10月19日我有-3-块屏幕"> + <a href="#10%e6%9c%8819%e6%97%a5%e6%88%91%e6%9c%89-3-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 10月19日,我有 3 块屏幕 +</h2><p>开始实习后,第一次用上了 MacOS。但尴尬的是,工作用的编译和测试环境还是 Windows,所以基本上都是在 Mac 上面通过 SSH 远程 Windows 进行开发(VsCode 大法好):</p> +<ul> +<li>开发环境:MacBookPro(左边)和主屏幕:28 寸 4k</li> +<li>编译环境:dell Windows 主机(无屏幕)</li> +<li>测试环境:hp | ThinkPad(右边)</li> +</ul> +<p>同时,我也负责服务器上的容器部署等相关工作,所以我有幸同时使用 3 大主流操作系统。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/10-19.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="12月31日我有-2-块屏幕"> + <a href="#12%e6%9c%8831%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 12月31日,我有 2 块屏幕 +</h2><p>适应实习的节奏后,晚上回宿舍的几个小时是每天最舒服的,因为可以不用管公司的事情专注自己喜欢的项目。</p> +<p>趁着放假买了一块树莓派 4B 在宿舍折腾,初步搭了 Seafile 和 Gitlab。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/12-31.jpg' alt='img' style='zoom:50%;' /> +</div> + + + Happy Geek PRD + https://3000ye.com/p/happy-geek-prd/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/p/happy-geek-prd/ + <img src="https://3000ye.com/p/happy-geek-prd/assets/prd.jpg" alt="Featured image of post Happy Geek PRD" /><h1 id="创作极客喜欢的-prd"> + <a href="#%e5%88%9b%e4%bd%9c%e6%9e%81%e5%ae%a2%e5%96%9c%e6%ac%a2%e7%9a%84-prd">#</a> + 创作极客喜欢的 PRD +</h1><p>产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。</p> +<p>产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。</p> +<p>在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:</p> +<ul> +<li>产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。</li> +<li>在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。</li> +<li>开发完成后,项目结果远远不如预期。</li> +</ul> +<p>因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/PRD 结构.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="产品简介"> + <a href="#%e4%ba%a7%e5%93%81%e7%ae%80%e4%bb%8b">#</a> + 产品简介 +</h2><h2 id="行业概要"> + <a href="#%e8%a1%8c%e4%b8%9a%e6%a6%82%e8%a6%81">#</a> + 行业概要 +</h2><h2 id="发行版本"> + <a href="#%e5%8f%91%e8%a1%8c%e7%89%88%e6%9c%ac">#</a> + 发行版本 +</h2><h3 id="排期表"> + <a href="#%e6%8e%92%e6%9c%9f%e8%a1%a8">#</a> + 排期表 +</h3><h3 id="产品设计"> + <a href="#%e4%ba%a7%e5%93%81%e8%ae%be%e8%ae%a1">#</a> + 产品设计 +</h3><h4 id="实体关系图"> + <a href="#%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb%e5%9b%be">#</a> + 实体关系图 +</h4><h4 id="用户角色权限表"> + <a href="#%e7%94%a8%e6%88%b7%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e8%a1%a8">#</a> + 用户角色权限表 +</h4><table> +<thead> +<tr> +<th>function</th> +<th>user1</th> +<th>user2</th> +</tr> +</thead> +<tbody> +<tr> +<td>func 01</td> +<td>get</td> +<td>get</td> +</tr> +</tbody> +</table> + + + + SSH Key Agent + https://3000ye.com/p/ssh-key-agent/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/p/ssh-key-agent/ + <img src="https://3000ye.com/p/ssh-key-agent/assets/gitssh.png" alt="Featured image of post SSH Key Agent" /><h1 id="为不同的-git-服务生成不同的-ssh-key"> + <a href="#%e4%b8%ba%e4%b8%8d%e5%90%8c%e7%9a%84-git-%e6%9c%8d%e5%8a%a1%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 为不同的 Git 服务生成不同的 SSH Key +</h1><p>在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:</p> +<ul> +<li>我有多个 Git 平台(Github, Gitlab, &hellip;)的账号,每个平台的账号都需要一个 SSH Key。</li> +<li>在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。</li> +<li>不同平台的账号,可能使用相同邮箱进行注册。</li> +</ul> +<p>这时,如何生成和管理 SSH Key 成为一个问题。</p> +<h2 id="设置局部-git-信息"> + <a href="#%e8%ae%be%e7%bd%ae%e5%b1%80%e9%83%a8-git-%e4%bf%a1%e6%81%af">#</a> + 设置局部 Git 信息 +</h2><p>在不同文件夹下,都可以设置不同的局部 Git 信息:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config user.email <span class="s2">&#34;email&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="开启-ssh-agent"> + <a href="#%e5%bc%80%e5%90%af-ssh-agent">#</a> + 开启 SSH Agent +</h2><p>开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="生成不同的-ssh-key"> + <a href="#%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 生成不同的 SSH Key +</h2><p>指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -f ~/.ssh/id_rsa_name -C <span class="s2">&#34;your_email@example.com&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>将生成的私钥添加进代理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-add ~/.ssh/id_rsa_name +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># MacOS</span> +</span></span><span class="line"><span class="cl">ssh-add --apple-use-keychain ~/.ssh/id_rsa_name +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="将公钥添加到-git-平台"> + <a href="#%e5%b0%86%e5%85%ac%e9%92%a5%e6%b7%bb%e5%8a%a0%e5%88%b0-git-%e5%b9%b3%e5%8f%b0">#</a> + 将公钥添加到 Git 平台 +</h2><p>首先复制 SSH 公钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat ~/.ssh/id_rsa_name.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后到 Git 平台中,添加该公钥。</p> + + + + Feishu Robot + https://3000ye.com/p/feishu-robot/ + Fri, 22 Dec 2023 13:47:39 +0800 + + https://3000ye.com/p/feishu-robot/ + <img src="https://3000ye.com/p/feishu-robot/assets/feishuGitlab.png" alt="Featured image of post Feishu Robot" /><h1 id="飞书机器人监听-gitlab-项目"> + <a href="#%e9%a3%9e%e4%b9%a6%e6%9c%ba%e5%99%a8%e4%ba%ba%e7%9b%91%e5%90%ac-gitlab-%e9%a1%b9%e7%9b%ae">#</a> + 飞书机器人监听 Gitlab 项目 +</h1><p>在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 <code>push</code> 到 Gitlab。</p> +<p>使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/feishu.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="准备工作"> + <a href="#%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c">#</a> + 准备工作 +</h2><p>实现这个功能,你需要准备:</p> +<ul> +<li>电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。</li> +<li>飞书账号:用于创建机器人监听 Gitlab 项目。</li> +<li>服务器(Linux):用于部署任务,并自动 <code>push</code> 到 Gitlab。</li> +</ul> +<h2 id="创建-gitlab-账号并新建项目"> + <a href="#%e5%88%9b%e5%bb%ba-gitlab-%e8%b4%a6%e5%8f%b7%e5%b9%b6%e6%96%b0%e5%bb%ba%e9%a1%b9%e7%9b%ae">#</a> + 创建 Gitlab 账号并新建项目 +</h2><p>使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/</p> +<p>注册完成后新建一个新项目,并添加一个 <code>README.md</code> 文件。</p> +<h2 id="配置-ssh-秘钥"> + <a href="#%e9%85%8d%e7%bd%ae-ssh-%e7%a7%98%e9%92%a5">#</a> + 配置 SSH 秘钥 +</h2><p>使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install git +</span></span><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config --global user.email <span class="s2">&#34;email&#34;</span> +</span></span><span class="line"><span class="cl">ssh-keygen -C <span class="s2">&#34;email&#34;</span> -t rsa +</span></span><span class="line"><span class="cl">cat ~/.ssh/id_rsa.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中 <code>name</code> 为 Gitlab 账号的名称,<code>email</code> 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。</p> +<p>然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。</p> +<h2 id="clone-项目并配置任务"> + <a href="#clone-%e9%a1%b9%e7%9b%ae%e5%b9%b6%e9%85%8d%e7%bd%ae%e4%bb%bb%e5%8a%a1">#</a> + Clone 项目并配置任务 +</h2><p>在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img3.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后在 Terminal 中找到合适的位置,Clone 项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git +</span></span></code></pre></td></tr></table> +</div> +</div><p>编写你的任务脚本(<code>shell</code> 或 <code>Python</code> 等),然后确保其能正常运行。</p> +<h2 id="新建飞书指令"> + <a href="#%e6%96%b0%e5%bb%ba%e9%a3%9e%e4%b9%a6%e6%8c%87%e4%bb%a4">#</a> + 新建飞书指令 +</h2><p>使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img4.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img5.png' alt='img' style='zoom:50%;' /> +</div> +<div style='display: flex; justify-content: center;'> +<img src='assets/img6.png' alt='img' style='zoom:50%;' /> +</div> +<p>点击完成后,启用机器人即可。</p> +<h2 id="创建定时任务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 创建定时任务 +</h2><p>首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。</p> +<p>本文推荐使用 <code>systemctl</code> 和 <code>systemd</code> 的 <code>timer</code> 来创建定时任务。</p> +<h3 id="创建定时器配置"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e9%85%8d%e7%bd%ae">#</a> + 创建定时器配置 +</h3><p>创建一个 <code>task.timer</code> 文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Timer Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span> +</span></span><span class="line"><span class="cl"><span class="c1"># OnCalendar设置定时规则,这里是每天10点</span> +</span></span><span class="line"><span class="cl"><span class="na">OnCalendar</span><span class="o">=</span><span class="s">*-*-* 10:00:00</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Install]</span> +</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建定时器服务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e6%9c%8d%e5%8a%a1">#</a> + 创建定时器服务 +</h3><p>创建一个 <code>task.service</code> 文件(默认使用 root 用户,建议指定 <code>User</code> 用户):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Service Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Service]</span> +</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">simple</span> +</span></span><span class="line"><span class="cl"><span class="na">User</span><span class="o">=</span><span class="s">user</span> +</span></span><span class="line"><span class="cl"><span class="c1"># 建议使用 shell 脚本</span> +</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/path/to/your/script.sh</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加定时任务"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 添加定时任务 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo cp your_timer_name.timer /etc/systemd/system/ +</span></span><span class="line"><span class="cl">sudo cp your_service_name.service /etc/systemd/system/ +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">sudo systemctl daemon-reload +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 手动启用任务,测试任务是否正常运行</span> +</span></span><span class="line"><span class="cl">sudo systemctl start your_timer_name.service +</span></span><span class="line"><span class="cl"><span class="c1"># 输出任务运行日志</span> +</span></span><span class="line"><span class="cl">sudo journalctl -u your_timer_name.service +</span></span></code></pre></td></tr></table> +</div> +</div><p>任务运行成功后,使用以下命令启用定时任务:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> your_timer_name.timer +</span></span><span class="line"><span class="cl">sudo systemctl list-timers +</span></span></code></pre></td></tr></table> +</div> +</div> + + + CppJson Ch01 + https://3000ye.com/p/cppjson-ch01/ + Sat, 16 Dec 2023 23:33:44 +0800 + + https://3000ye.com/p/cppjson-ch01/ + <img src="https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch01" /><h1 id="cppjson-第一章节自动测试null-和-bool-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%b8%80%e7%ab%a0%e8%8a%82%e8%87%aa%e5%8a%a8%e6%b5%8b%e8%af%95null-%e5%92%8c-bool-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第一章节:自动测试,<code>NULL</code> 和 <code>bool</code> 值解析 +</h1><h2 id="json-是什么"> + <a href="#json-%e6%98%af%e4%bb%80%e4%b9%88">#</a> + JSON 是什么 +</h2><p>JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为<a class="link" href="https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" target="_blank" rel="noopener" + >ECMA-404</a>。</p> +<p>虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。</p> +<p>例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;title&#34;</span><span class="o">:</span> <span class="s2">&#34;Design Patterns&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;subtitle&#34;</span><span class="o">:</span> <span class="s2">&#34;Elements of Reusable Object-Oriented Software&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;author&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Erich Gamma&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Richard Helm&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Ralph Johnson&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;John Vlissides&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;year&#34;</span><span class="o">:</span> <span class="mi">2009</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;weight&#34;</span><span class="o">:</span> <span class="mf">1.8</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;hardcover&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;publisher&#34;</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Company&#34;</span><span class="o">:</span> <span class="s2">&#34;Pearson Education&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Country&#34;</span><span class="o">:</span> <span class="s2">&#34;India&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;website&#34;</span><span class="o">:</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。</p> +<p>从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:</p> +<ul> +<li>null: 表示为 null</li> +<li>boolean: 表示为 true 或 false</li> +<li>number: 一般的浮点数表示方式,在下一单元详细说明</li> +<li>string: 表示为 &ldquo;&hellip;&rdquo;</li> +<li>array: 表示为 [ &hellip; ]</li> +<li>object: 表示为 { &hellip; }</li> +</ul> +<p>我们要实现的 JSON 库,主要是完成 3 个需求:</p> +<ol> +<li>把 JSON 文本解析为一个树状数据结构(parse)。</li> +<li>提供接口访问该数据结构(access)。</li> +<li>把数据结构转换成 JSON 文本(stringify)。</li> +</ol> +<p><img src="https://3000ye.com/p/cppjson-ch01/assets/requirement.png" + width="440" + height="78" + srcset="https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png 480w, https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + + class="gallery-image" + data-flex-grow="564" + data-flex-basis="1353px" + +></p> +<p>我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。</p> +<h2 id="搭建编译环境"> + <a href="#%e6%90%ad%e5%bb%ba%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#</a> + 搭建编译环境 +</h2><p>我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。</p> +<p>我们的 JSON 库名为 CppJson,代码文件只有 3 个:</p> +<ol> +<li><code>include/cppjson.hpp</code>:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。</li> +<li><code>cppjson.cpp</code>:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。</li> +<li><code>cppjsonTest.cpp</code>:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。</li> +</ol> +<p>为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 <a class="link" href="https://cmake.org/" target="_blank" rel="noopener" + >CMake</a>。</p> +<p>在 OS X 平台中,在命令行通过命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir build +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build +</span></span><span class="line"><span class="cl">cmake -DCMAKE_BUILD_TYPE<span class="o">=</span>Debug .. +</span></span><span class="line"><span class="cl">make +</span></span></code></pre></td></tr></table> +</div> +</div><p>将 Debug 改成 Release 就会生成 Release 配置的 makefile。</p> +<p>在 Vscode 中,可以通过配置 <code>tasks.json</code> 文件来进行自动 build:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//.vscode/tasks.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdir&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-p&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;-DCMAKE_BUILD_TYPE=Debug&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//在此处添加其它CMAKE选项 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;..&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-j16&#34;</span><span class="p">,],</span> <span class="c1">//根据机器cpu核心数量自行调整 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOrder&#34;</span><span class="p">:</span> <span class="s2">&#34;sequence&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOn&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> <span class="s2">&#34;make&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后执行 build 生成的文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ./build/cppjson_test_ch01 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">16/16 <span class="o">(</span>100.00%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。</p> +<h2 id="头文件与-api-设计"> + <a href="#%e5%a4%b4%e6%96%87%e4%bb%b6%e4%b8%8e-api-%e8%ae%be%e8%ae%a1">#</a> + 头文件与 API 设计 +</h2><p><code>Cpp</code> 语言有头文件的概念,需要使用 <code>#include</code>去引入头文件中的类型声明和函数声明。但由于头文件也可以 <code>#include</code> 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#pragma once +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonType</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NULL</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_TRUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_FALSE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NUMBER</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_STRING</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_ARRAY</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_OBJECT</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 <code>cppjson_value</code> 结构体表示,我们会称它为一个 JSON 值(JSON value)。</p> +<p>在此单元中,我们只需要实现 <code>null</code>, <code>true</code> 和 <code>false</code> 的解析,因此该结构体只需要存储一个 <code>cppjsonType</code>,之后的单元会逐步加入其他数据。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjsonType</span> <span class="n">type</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_value</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,我们现在只需要两个 API 函数,一个是解析 JSON:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>传入的 JSON 文本是一个 <code>string</code> 字符串,由于我们不应该改动这个输入字符串,所以使用 <code>const std::string</code> 类型。</p> +<p>返回值是以下这些枚举类中的值,无错误会返回 <code>cppjsonParseCode::OK</code>,其他值在下节解释。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonParseCode</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">OK</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">INVALID_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">ROOT_NOT_SINGULAR</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时我们只需要一个访问结果的函数,就是获取其类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonType</span> <span class="nf">cppjson_get_type</span><span class="p">(</span><span class="k">const</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="json-语法子集"> + <a href="#json-%e8%af%ad%e6%b3%95%e5%ad%90%e9%9b%86">#</a> + JSON 语法子集 +</h2><p>下面是此单元的 JSON 语法子集,使用 <a class="link" href="https://tools.ietf.org/html/rfc7159" target="_blank" rel="noopener" + >RFC7159</a> 中的 <a class="link" href="https://tools.ietf.org/html/rfc5234" target="_blank" rel="noopener" + >ABNF</a> 表示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">JSON-text</span> <span class="err">=</span> <span class="err">ws</span> <span class="err">value</span> <span class="err">ws</span> +</span></span><span class="line"><span class="cl"><span class="err">ws</span> <span class="err">=</span> <span class="err">*(%x</span><span class="mi">20</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">09</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">A</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">D)</span> +</span></span><span class="line"><span class="cl"><span class="err">value</span> <span class="err">=</span> <span class="kc">null</span> <span class="err">/</span> <span class="kc">false</span> <span class="err">/</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="kc">null</span> <span class="err">=</span> <span class="s2">&#34;null&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">false</span> <span class="err">=</span> <span class="s2">&#34;false&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">true</span> <span class="err">=</span> <span class="s2">&#34;true&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当中 <code>%xhh</code> 表示以 16 进制表示的字符,<code>/</code> 是多选一,<code>*</code> 是零或多个,<code>()</code> 用于分组。</p> +<p>那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。</p> +<p>第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。</p> +<p>第三行是说,我们现时的值只可以是 <code>null</code>、<code>false</code> 或 <code>true</code>,它们分别有对应的字面值(literal)。</p> +<p>我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。</p> +<p>在这个 JSON 语法子集下,我们定义 3 种错误码:</p> +<ul> +<li>若一个 JSON 只含有空白,传回 <code>LEPT_PARSE_EXPECT_VALUE</code>。</li> +<li>若一个值之后,在空白之后还有其他字符,传回 <code>LEPT_PARSE_ROOT_NOT_SINGULAR</code>。</li> +<li>若值不是那三种字面值,传回 <code>LEPT_PARSE_INVALID_VALUE</code>。</li> +</ul> +<h2 id="单元测试"> + <a href="#%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95">#</a> + 单元测试 +</h2><p>许多同学在做练习题时,都是以 <code>printf</code>/<code>cout</code> 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。</p> +<p>常用的单元测试框架有 xUnit 系列,如 C++ 的 <a class="link" href="https://github.com/google/googletest" target="_blank" rel="noopener" + >Google Test</a>、C# 的 <a class="link" href="https://www.nunit.org/" target="_blank" rel="noopener" + >NUnit</a>。我们为了简单起见,会编写一个极简单的单元测试方式。</p> +<p>一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:</p> +<ol> +<li>加入一个测试。</li> +<li>运行所有测试,新的测试应该会失败。</li> +<li>编写实现代码。</li> +<li>运行所有测试,若有测试失败回到3。</li> +<li>重构代码。</li> +<li>回到 1。</li> +</ol> +<p>TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。</p> +<p>但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。</p> +<p>回到 CppJson 项目,<code>cppjsonTest.cpp</code> 包含了一个极简的单元测试框架:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;include/cppjson.hpp&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;typeinfo&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">main_ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 测试宏接口 +</span></span></span><span class="line"><span class="cl"><span class="c1">// flag 表示测试点是否通过,如果未通过则打印异常信息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define EXPECT_BASE(flag, expect, actual) \ +</span></span></span><span class="line"><span class="cl"><span class="cp"> do {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> test_count ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> if (flag) test_pass ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> else {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> fprintf(stderr, &#34;%s:%d, expect = %s(%d), actual = %s(%d)\n&#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast&lt;int&gt;(expect), typeid(actual).name(), static_cast&lt;int&gt;(actual));\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> main_ret = 1;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> }\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> } while(0) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse_null</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_value</span> <span class="n">v</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_FALSE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">,</span> <span class="n">cppjson_parse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">,</span> <span class="s">&#34;null&#34;</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">,</span> <span class="n">cppjson_get_type</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse_null</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d/%d (%3.2f%%) passed</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">test_pass</span><span class="p">,</span> <span class="n">test_count</span><span class="p">,</span> <span class="n">test_pass</span> <span class="o">*</span> <span class="mf">100.0</span> <span class="o">/</span> <span class="n">test_count</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">main_ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时只提供了一个 <code>EXPECT_TYPE(expect, actual)</code> 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。</p> +<p>若按照 TDD 的步骤,我们先写一个测试,如上面的 <code>test_parse_null()</code>,而 <code>cppjson_parse()</code> 只返回 <code>cppjsonParseCode::OK</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/CppJson/ch01/cppjsonTest.cpp:30, <span class="nv">expect</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>0<span class="o">)</span>, <span class="nv">actual</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>2<span class="o">)</span> +</span></span><span class="line"><span class="cl">15/16 <span class="o">(</span>93.75%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>为通过的测试是因为 <code>cppjson_parse()</code> 没有把 <code>v.type</code> 改成 <code>cppjsonType::CPPJSON_NULL</code>,造成失败。我们再实现 <code>lept_parse()</code> 令到它能通过测试。</p> +<p>然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。</p> +<h2 id="实现解析器"> + <a href="#%e5%ae%9e%e7%8e%b0%e8%a7%a3%e6%9e%90%e5%99%a8">#</a> + 实现解析器 +</h2><p>有了 API 的设计、单元测试,终于要实现解析器了。</p> +<p>首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 <code>cppjson_context</code> 结构体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_context</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// json 解析函数:ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span> <span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_context</span> <span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 分别解析 ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">cppjson_parse_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span> <span class="o">?</span> <span class="n">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:</p> +<ul> +<li>n ➔ null</li> +<li>t ➔ true</li> +<li>f ➔ false</li> +<li>&quot; ➔ string</li> +<li>0-9/- ➔ number</li> +<li>[ ➔ array</li> +<li>{ ➔ object</li> +</ul> +<p>所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39; &#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\t&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\r&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span> <span class="o">++</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 ws2 之后是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">ROOT_NOT_SINGULAR</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 检测 null 值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_null</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">head</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">head</span> <span class="o">!=</span> <span class="s">&#34;null&#34;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 value +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_value</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;n&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_null</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;t&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_true</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;f&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_false</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;\0&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">EXPECT_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch09 + https://3000ye.com/p/c-primer-ch09/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/p/c-primer-ch09/ + <img src="https://3000ye.com/p/c-primer-ch09/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch09" /><h1 id="顺序容器"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8">#</a> + 顺序容器 +</h1><p>一个容器就是一些特定类型对象的集合,<strong>顺序容器(sequence container)</strong> 为程序员提供了控制元素存储和访问顺序的能力。</p> +<h2 id="顺序容器概述"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8%e6%a6%82%e8%bf%b0">#</a> + 顺序容器概述 +</h2><p>下面列出了标准库中的顺序容器,不同容器有不同的性能折中:</p> +<ul> +<li>想容器添加或从容器删除元素的代价。</li> +<li>非顺序访问容器中元素的代价。</li> +</ul> +<table> +<thead> +<tr> +<th>容器类型</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector</code></td> +<td>可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。</td> +</tr> +<tr> +<td><code>deque</code></td> +<td>双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。</td> +</tr> +<tr> +<td><code>list</code></td> +<td>双向链表。只支持双向顺序访问。在<code>list</code>中任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>forward_list</code></td> +<td>单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>array</code></td> +<td>固定大小数组。支持快速随机访问。不能添加或者删除元素。</td> +</tr> +<tr> +<td><code>string</code></td> +<td>与<code>vector</code>相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。</td> +</tr> +</tbody> +</table> +<ul> +<li>除了固定大小的 <code>array</code> 外,其他容器都提供高效、灵活的内存管理。</li> +<li>通常使用 <code>vector</code> 是最好的选择,除非你有很好的理由选择其他容器。</li> +<li>如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序要求随记访问元素,应使用 <code>vector</code> 或 <code>deque</code>。</li> +<li>如果程序要求在容器的中间插入或删除元素,应使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 <code>deque</code>。</li> +</ul> +<h2 id="容器库概念"> + <a href="#%e5%ae%b9%e5%99%a8%e5%ba%93%e6%a6%82%e5%bf%b5">#</a> + 容器库概念 +</h2><p>容器类型操作上形成了一种层次:</p> +<ul> +<li>某些操作是所有容器类型都提供的。</li> +<li>另一些操作仅针对顺序容器、关联容器或无序容器。</li> +</ul> +<h3 id="类型"> + <a href="#%e7%b1%bb%e5%9e%8b">#</a> + 类型 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iterator</code></td> +<td>此容器类型的迭代器类型</td> +</tr> +<tr> +<td><code>const_iterator</code></td> +<td>可以读取元素但不能修改元素的迭代器类型</td> +</tr> +<tr> +<td><code>size_type</code></td> +<td>无符号整数类型,足够保存此种容器类型最大可能的大小</td> +</tr> +<tr> +<td><code>difference_type</code></td> +<td>带符号整数类型,足够保存两个迭代器之间的距离</td> +</tr> +<tr> +<td><code>value_type</code></td> +<td>元素类型</td> +</tr> +<tr> +<td><code>reference</code></td> +<td>元素的左值类型;和<code>value_type &amp;</code>含义相同</td> +</tr> +<tr> +<td><code>const_reference</code></td> +<td>元素的<code>const</code>左值类型,即<code>const value_type &amp;</code></td> +</tr> +</tbody> +</table> +<h3 id="构造函数"> + <a href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0">#</a> + 构造函数 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>C c;</code></td> +<td>默认构造函数,构造空容器</td> +</tr> +<tr> +<td><code>C c1(c2);</code>或<code>C c1 = c2;</code></td> +<td>构造<code>c2</code>的拷贝<code>c1</code></td> +</tr> +<tr> +<td><code>C c(b, e)</code></td> +<td>构造<code>c</code>,将迭代器<code>b</code>和<code>e</code>指定范围内的所有元素拷贝到<code>c</code></td> +</tr> +<tr> +<td><code>C c(a, b, c...)</code></td> +<td>列表初始化<code>c</code></td> +</tr> +<tr> +<td><code>C c(n)</code></td> +<td>只支持顺序容器,且不包括<code>array</code>,包含<code>n</code>个元素,这些元素进行了值初始化</td> +</tr> +<tr> +<td><code>C c(n, t)</code></td> +<td>包含<code>n</code>个初始值为<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>只有顺序容器的构造函数才接受大小参数,关联容器并不支持。</li> +<li><code>array</code>具有固定大小。</li> +<li>和其他容器不同,默认构造的<code>array</code>是非空的。</li> +<li>直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。</li> +<li>使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。</li> +</ul> +<h3 id="赋值和swap"> + <a href="#%e8%b5%8b%e5%80%bc%e5%92%8cswap">#</a> + 赋值和<code>swap</code> +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c1 = c2;</code></td> +<td>将<code>c1</code>中的元素替换成<code>c2</code>中的元素</td> +</tr> +<tr> +<td><code>c1 = {a, b, c...}</code></td> +<td>将<code>c1</code>中的元素替换成列表中的元素(不适用于<code>array</code>)</td> +</tr> +<tr> +<td><code>c1.swap(c2)</code></td> +<td>交换<code>c1</code>和<code>c2</code>的元素</td> +</tr> +<tr> +<td><code>swap(c1, c2)</code></td> +<td>等价于<code>c1.swap(c2)</code></td> +</tr> +<tr> +<td><code>c.assign(b, e)</code></td> +<td>将<code>c</code>中的元素替换成迭代器<code>b</code>和<code>e</code>表示范围中的元素,<code>b</code>和<code>e</code>不能指向<code>c</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(il)</code></td> +<td>将<code>c</code>中的元素替换成初始化列表<code>il</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(n, r)</code></td> +<td>将<code>c</code>中的元素替换为<code>n</code>个值是<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>使用非成员版本的<code>swap</code>是一个好习惯。</li> +<li><code>assign</code>操作不适用于关联容器和<code>array</code></li> +</ul> +<h3 id="大小"> + <a href="#%e5%a4%a7%e5%b0%8f">#</a> + 大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.size()</code></td> +<td><code>c</code>中元素的数目(不支持<code>forward_list</code>)</td> +</tr> +<tr> +<td><code>c.max_size()</code></td> +<td><code>c</code>中可保存的最大元素数目</td> +</tr> +<tr> +<td><code>c.empty()</code></td> +<td>若<code>c</code>中存储了元素,返回<code>false</code>,否则返回<code>true</code></td> +</tr> +</tbody> +</table> +<h3 id="添加元素"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%85%83%e7%b4%a0">#</a> + 添加元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.push_back(t)</code></td> +<td>在<code>c</code>尾部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_back(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.push_front(t)</code></td> +<td>在<code>c</code>头部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_front(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前创建一个值是<code>t</code>的元素,返回指向新元素的迭代器</td> +</tr> +<tr> +<td><code>c.emplace(p, args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, n, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前插入<code>n</code>个值为<code>t</code>的元素,返回指向第一个新元素的迭代器;如果<code>n</code>是0,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, b, e)</code></td> +<td>将迭代器<code>b</code>和<code>e</code>范围内的元素,插入到<code>p</code>指向的元素之前;如果范围为空,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, il)</code></td> +<td><code>il</code>是一个花括号包围中的元素值列表,将其插入到<code>p</code>指向的元素之前;如果<code>il</code>是空,则返回<code>p</code></td> +</tr> +</tbody> +</table> +<ul> +<li>因为这些操作会改变大小,因此不适用于<code>array</code>。</li> +<li><code>forward_list</code>有自己专有版本的<code>insert</code>和<code>emplace</code>。</li> +<li><code>forward_list</code>不支持<code>push_back</code>和<code>emplace_back</code>。</li> +<li>当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。</li> +<li><code>emplace</code>开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。</li> +<li>传递给<code>emplace</code>的参数必须和元素类型的构造函数相匹配。</li> +</ul> +<h3 id="访问元素"> + <a href="#%e8%ae%bf%e9%97%ae%e5%85%83%e7%b4%a0">#</a> + 访问元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.back()</code></td> +<td>返回<code>c</code>中尾元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c.front()</code></td> +<td>返回<code>c</code>中头元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c[n]</code></td> +<td>返回<code>c</code>中下标是<code>n</code>的元素的引用,<code>n</code>时候一个无符号证书。若<code>n&gt;=c.size()</code>,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.at(n)</code></td> +<td>返回下标为<code>n</code>的元素引用。如果下标越界,则抛出<code>out_of_range</code>异常</td> +</tr> +</tbody> +</table> +<ul> +<li>访问成员函数返回的是引用。</li> +<li><code>at</code>和下标操作只适用于<code>string</code>、<code>vector</code>、<code>deque</code>、<code>array</code>。</li> +<li><code>back</code>不适用于<code>forward_list</code>。</li> +<li>如果希望下标是合法的,可以使用<code>at</code>函数。</li> +</ul> +<h3 id="删除元素"> + <a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0">#</a> + 删除元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.pop_back()</code></td> +<td>删除<code>c</code>中尾元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.pop_front()</code></td> +<td>删除<code>c</code>中首元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.erase(p)</code></td> +<td>删除迭代器<code>p</code>指向的元素,返回一个指向被删除元素之后的元素的迭代器,若<code>p</code>本身是尾后迭代器,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.erase(b, e)</code></td> +<td>删除迭代器<code>b</code>和<code>e</code>范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若<code>e</code>本身就是尾后迭代器,则返回尾后迭代器</td> +</tr> +<tr> +<td><code>c.clear()</code></td> +<td>删除<code>c</code>中所有元素,返回<code>void</code></td> +</tr> +</tbody> +</table> +<ul> +<li>会改变容器大小,不适用于<code>array</code>。</li> +<li><code>forward_list</code>有特殊版本的<code>erase</code></li> +<li><code>forward_list</code>不支持<code>pop_back</code></li> +<li><code>vector</code>和<code>string</code>不支持<code>pop_front</code></li> +</ul> +<h3 id="特殊的-forwad_list操作"> + <a href="#%e7%89%b9%e6%ae%8a%e7%9a%84-forwad_list%e6%93%8d%e4%bd%9c">#</a> + 特殊的 <code>forwad_list</code>操作 +</h3><ul> +<li>链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。</li> +<li><code>forward_list</code>定义了<code>before_begin</code>,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。</li> +</ul> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>lst.before_begin()</code></td> +<td>返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。</td> +</tr> +<tr> +<td><code>lst.cbefore_begin()</code></td> +<td>同上,但是返回的是常量迭代器。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象</td> +</tr> +<tr> +<td><code>lst.insert_after(p, n, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象,<code>n</code>是数量。若<code>n</code>是0则函数行为未定义</td> +</tr> +<tr> +<td><code>lst.insert_after(p, b, e)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由迭代器<code>b</code>和<code>e</code>指定范围。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, il)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由<code>il</code>指定初始化列表。</td> +</tr> +<tr> +<td><code>emplace_after(p, args)</code></td> +<td>使用<code>args</code>在<code>p</code>之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若<code>p</code>为尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(p)</code></td> +<td>删除<code>p</code>指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若<code>p</code>指向<code>lst</code>的尾元素或者是一个尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(b, e)</code></td> +<td>类似上面,删除对象换成从<code>b</code>到<code>e</code>指定的范围。</td> +</tr> +</tbody> +</table> +<h3 id="改变容器大小"> + <a href="#%e6%94%b9%e5%8f%98%e5%ae%b9%e5%99%a8%e5%a4%a7%e5%b0%8f">#</a> + 改变容器大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.resize(n)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,若<code>n&lt;c.size()</code>,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化</td> +</tr> +<tr> +<td><code>c.resize(n, t)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,任何新添加的元素都初始化为值<code>t</code></td> +</tr> +</tbody> +</table> +<h3 id="获取迭代器"> + <a href="#%e8%8e%b7%e5%8f%96%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 获取迭代器 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.begin()</code>, <code>c.end()</code></td> +<td>返回指向<code>c</code>的首元素和尾元素之后位置的迭代器</td> +</tr> +<tr> +<td><code>c.cbegin()</code>, <code>c.cend()</code></td> +<td>返回<code>const_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>以<code>c</code>开头的版本是C++11新标准引入的</li> +<li>当不需要写访问时,应该使用<code>cbegin</code>和<code>cend</code>。</li> +</ul> +<h3 id="反向容器的额外成员"> + <a href="#%e5%8f%8d%e5%90%91%e5%ae%b9%e5%99%a8%e7%9a%84%e9%a2%9d%e5%a4%96%e6%88%90%e5%91%98">#</a> + 反向容器的额外成员 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>reverse_iterator</code></td> +<td>按逆序寻址元素的迭代器</td> +</tr> +<tr> +<td><code>const_reverse_iterator</code></td> +<td>不能修改元素的逆序迭代器</td> +</tr> +<tr> +<td><code>c.rbegin()</code>, <code>c.rend()</code></td> +<td>返回指向<code>c</code>的尾元素和首元素之前位置的迭代器</td> +</tr> +<tr> +<td><code>c.crbegin()</code>, <code>c.crend()</code></td> +<td>返回<code>const_reverse_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>不支持<code>forward_list</code></li> +</ul> + + + + C++ Primer Ch08 + https://3000ye.com/p/c-primer-ch08/ + Sun, 10 Dec 2023 15:24:27 +0800 + + https://3000ye.com/p/c-primer-ch08/ + <img src="https://3000ye.com/p/c-primer-ch08/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch08" /><h1 id="io-库"> + <a href="#io-%e5%ba%93">#</a> + IO 库 +</h1><p>我们的程序已经使用了很多 IO 库设施:</p> +<ul> +<li><code>istream</code> (输入流)类型,提供输入操作。</li> +<li><code>ostream</code> (输出流)类型,提供输出操作。</li> +<li><code>cin</code>,一个 <code>istream</code> 对象,从标准输入读取数据。</li> +<li><code>cout</code>,一个 <code>ostream</code> 对象,想标准输出写入数据。</li> +<li><code>cerr</code>,一个 <code>ostream</code> 对象,通常用于输出程序错误信息,写入到标准错误。</li> +<li><code>&gt;&gt;</code> 运算符,用来从一个 <code>istream</code> 对象中读取输入数据。</li> +<li><code>&lt;&lt;</code> 运算符,用来向一个 <code>ostream</code> 对象中写入输出数据。</li> +<li><code>getline</code> 函数,从一个给定的 <code>istream</code> 对象中读取一行数据,存入到一个给定的 <code>string</code> 对象中。</li> +</ul> +<h2 id="io-库-1"> + <a href="#io-%e5%ba%93-1">#</a> + IO 库 +</h2><p>IO 类型和对象一般都是操纵 <code>char</code> 数据的,但有些使用需要对文件、<code>string</code> 进行操作,因此分别定义了三个头文件:</p> +<ul> +<li><code>iostream</code> 头文件:从标准流中读写数据,<code>istream</code>、<code>ostream</code> 等。</li> +<li><code>fstream</code> 头文件:从文件中读写数据,<code>ifstream</code>、<code>ofstream</code> 等。</li> +<li><code>sstream</code> 头文件:从字符串中读写数据,<code>istringstream</code>、<code>ostringstream</code> 等。</li> +</ul> + + + + C++ Primer Ch07 + https://3000ye.com/p/c-primer-ch07/ + Sun, 10 Dec 2023 14:35:42 +0800 + + https://3000ye.com/p/c-primer-ch07/ + <img src="https://3000ye.com/p/c-primer-ch07/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch07" /><h1 id="类"> + <a href="#%e7%b1%bb">#</a> + 类 +</h1><p>类的基本思想是 <strong>数据抽象(data abstraction)</strong> 和 <strong>封装(encapsulation)</strong>。数据抽象是一种依赖于 <strong>接口(interface)</strong> 和 <strong>实现(implementation)</strong> 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。</p> +<p>封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。</p> +<p>类要想实现数据抽象和封装,首先需要定义一个 <strong>抽象数据类型(abstract data type)</strong>。</p> +<h2 id="定义抽象数据类型"> + <a href="#%e5%ae%9a%e4%b9%89%e6%8a%bd%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#</a> + 定义抽象数据类型 +</h2><h2 id="访问控制与封装"> + <a href="#%e8%ae%bf%e9%97%ae%e6%8e%a7%e5%88%b6%e4%b8%8e%e5%b0%81%e8%a3%85">#</a> + 访问控制与封装 +</h2><p>我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 <code>C++</code> 中使用 <strong>访问说明符(access specifiers)</strong> 加强类的封装:</p> +<ul> +<li>定义在 <code>public</code> 说明符之后的成员,在整个程序内都可被访问。</li> +<li>定义在 <code>private</code> 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。</li> +</ul> +<p>定义新的 <code>Sales_data</code> 类:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sales_data</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bookNo</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="n">units_sold</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">revenue</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="nf">avg_price</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">units_sold</span> <span class="o">?</span> <span class="n">revenue</span> <span class="o">/</span> <span class="nl">units_sold</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">p</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="n">units_sold</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">revenue</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">istream</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">isbn</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">bookNo</span><span class="p">;</span> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span> <span class="o">&amp;</span><span class="n">combine</span><span class="p">(</span><span class="k">const</span> <span class="n">Sales_data</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch06 + https://3000ye.com/p/c-primer-ch06/ + Fri, 08 Dec 2023 21:13:28 +0800 + + https://3000ye.com/p/c-primer-ch06/ + <img src="https://3000ye.com/p/c-primer-ch06/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch06" /><h1 id="函数"> + <a href="#%e5%87%bd%e6%95%b0">#</a> + 函数 +</h1><p>函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。</p> +<h2 id="函数基础"> + <a href="#%e5%87%bd%e6%95%b0%e5%9f%ba%e7%a1%80">#</a> + 函数基础 +</h2><p>一个典型的 <strong>函数(function)</strong> 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 <strong>形参(parameter)</strong> 组成的列表以及函数体。</p> +<p>我们通过 <strong>调用运算符(call operator)</strong> 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 <strong>实参(argument)</strong> 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。</p> +<p><strong>编写函数</strong>:例如编写一个求 <code>n</code> 的阶乘的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fact</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">*=</span> <span class="n">n</span> <span class="o">--</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><strong>调用函数</strong>:要调用 <code>fact</code> 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;5! = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,<strong>主调函数(calling funciton)</strong> 的执行暂停,<strong>被调函数(called funciton)</strong> 开始执行。</p> +<h3 id="局部对象"> + <a href="#%e5%b1%80%e9%83%a8%e5%af%b9%e8%b1%a1">#</a> + 局部对象 +</h3><p>在 <code>C++</code> 语言中,名字有作用域,对象有 <strong>生命周期(lifetime)</strong>:</p> +<ul> +<li>名字的作用域是程序文本的一部分,名字在其中可见。</li> +<li>对象的生命周期是程序执行过程中该对象存在的一段时间。</li> +</ul> +<p>在函数体内,形参和内部定义的变量统称为 <strong>局部变量(local variable)</strong>,它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。</p> +<p><strong>局部静态对象</strong>:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 <code>static</code> 类型从而获得这样的对象,<strong>局部静态对象(local static object)</strong> 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。</p> +<h2 id="参数传递"> + <a href="#%e5%8f%82%e6%95%b0%e4%bc%a0%e9%80%92">#</a> + 参数传递 +</h2> + + + C++ Primer Ch04 + https://3000ye.com/p/c-primer-ch04/ + Tue, 28 Nov 2023 08:49:13 +0800 + + https://3000ye.com/p/c-primer-ch04/ + <img src="https://3000ye.com/p/c-primer-ch04/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch04" /><h1 id="表达式"> + <a href="#%e8%a1%a8%e8%be%be%e5%bc%8f">#</a> + 表达式 +</h1><p>表达式由一个或多个 <strong>运算对象(operand)</strong> 组成对表达式求值将得到一个 <strong>结果(result)</strong>。</p> +<h2 id="基础"> + <a href="#%e5%9f%ba%e7%a1%80">#</a> + 基础 +</h2><h3 id="基本概念"> + <a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a> + 基本概念 +</h3><p><code>C++</code> 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。</p> +<p>对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。</p> +<p>在表达式求值过程中,小整数类型(<code>bool</code>、<code>char</code>、<code>short</code>)等通常会被 <strong>提升(promoted)</strong> 成较大的整数类型(<code>int</code>)。</p> +<p>当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 <strong>重载运算符(overloaded operator)</strong>。</p> +<p><code>C++</code> 的表达式要不然是 <strong>右值(rvalue)</strong>,要不然就是 <strong>左值(lvalue)</strong>,这两个名词是从 <code>C</code> 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p> +<h3 id="求值顺序"> + <a href="#%e6%b1%82%e5%80%bc%e9%a1%ba%e5%ba%8f">#</a> + 求值顺序 +</h3><p>在大多数情况下,表达式求值的顺序是没有明确指定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">f1</span><span class="p">()</span> <span class="o">*</span> <span class="n">f2</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们知道 <code>f1</code> 和 <code>f2</code> 一定会在执行乘法之前被调用,但是无法知道是 <code>f1</code> 先被调用还是 <code>f2</code> 先被调用。对于没有指定调用顺序的程序来说,如果 <code>f1</code> 和 <code>f2</code> 同时修改了同一个对象,将会引发错误并产生未定义的行为。</p> +<h2 id="算术运算符"> + <a href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 算术运算符 +</h2><p><strong>溢出</strong>:当计算的结果超出该类型所能表示的范围时就会产生溢出。</p> +<p><strong><code>bool</code> 类型不应该参与计算</strong>。</p> +<p><strong>取余运算</strong>:<code>m % n</code> 的结果的符号与 <code>m</code> 相同。</p> +<h2 id="逻辑运算符"> + <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 逻辑运算符 +</h2><p><strong>短路求值</strong>:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。</p> +<h2 id="赋值运算符"> + <a href="#%e8%b5%8b%e5%80%bc%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 赋值运算符 +</h2> + + + C++ Primer Ch03 + https://3000ye.com/p/c-primer-ch03/ + Tue, 14 Nov 2023 22:14:23 +0800 + + https://3000ye.com/p/c-primer-ch03/ + <img src="https://3000ye.com/p/c-primer-ch03/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch03" /><h1 id="字符串向量和数组"> + <a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%90%91%e9%87%8f%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 字符串、向量和数组 +</h1><h2 id="命名空间的-using-声明"> + <a href="#%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e7%9a%84-using-%e5%a3%b0%e6%98%8e">#</a> + 命名空间的 <code>using</code> 声明 +</h2><p>我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用<code>using</code>进行声明:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">;</span> <span class="c1">// 单独使用某个函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 批量声明 std 中所有函数 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>头文件中不应该包含 <code>using</code> 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。</p> +</blockquote> +<h2 id="标准库类型-string"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-string">#</a> + 标准库类型 <code>string</code> +</h2><p>标准库类型 <code>string</code> 表示可变长的字符序列,使用 <code>string</code> 类型必须首先包含 <code>string</code> 头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="定义和初始化-string-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-string-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>string</code> 对象 +</h3><p>初始化 <code>string</code> 对象的方式:</p> +<table> +<thead> +<tr> +<th>方式</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>string s1</code></td> +<td>默认初始化,<code>s1</code>是个空字符串</td> +</tr> +<tr> +<td><code>string s2(s1)</code></td> +<td><code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s2 = s1</code></td> +<td>等价于<code>s2(s1)</code>,<code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s3(&quot;value&quot;)</code></td> +<td><code>s3</code>是字面值“value”的副本,除了字面值最后的那个空字符外</td> +</tr> +<tr> +<td><code>string s3 = &quot;value&quot;</code></td> +<td>等价于<code>s3(&quot;value&quot;)</code>,<code>s3</code>是字面值&quot;value&quot;的副本</td> +</tr> +<tr> +<td><code>string s4(n, 'c')</code></td> +<td>把<code>s4</code>初始化为由连续<code>n</code>个字符<code>c</code>组成的串</td> +</tr> +</tbody> +</table> +<p>拷贝初始化(copy initialization):使用 <code>=</code> 将一个已有的对象拷贝到正在创建的对象。</p> +<p>直接初始化(direct initialization):通过括号给对象赋值。</p> +<h3 id="string-对象的操作"> + <a href="#string-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>string</code> 对象的操作 +</h3><p><code>string</code>的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>os &lt;&lt; s</code></td> +<td>将<code>s</code>写到输出流<code>os</code>当中,返回<code>os</code></td> +</tr> +<tr> +<td><code>is &gt;&gt; s</code></td> +<td>从<code>is</code>中读取字符串赋给<code>s</code>,字符串以空白分割,返回<code>is</code></td> +</tr> +<tr> +<td><code>getline(is, s)</code></td> +<td>从<code>is</code>中读取一行赋给<code>s</code>,返回<code>is</code></td> +</tr> +<tr> +<td><code>s.empty()</code></td> +<td><code>s</code>为空返回<code>true</code>,否则返回<code>false</code></td> +</tr> +<tr> +<td><code>s.size()</code></td> +<td>返回<code>s</code>中字符的个数</td> +</tr> +<tr> +<td><code>s[n]</code></td> +<td>返回<code>s</code>中第<code>n</code>个字符的引用,位置<code>n</code>从0计起</td> +</tr> +<tr> +<td><code>s1+s2</code></td> +<td>返回<code>s1</code>和<code>s2</code>连接后的结果</td> +</tr> +<tr> +<td><code>s1=s2</code></td> +<td>用<code>s2</code>的副本代替<code>s1</code>中原来的字符</td> +</tr> +<tr> +<td><code>s1==s2</code></td> +<td>如果<code>s1</code>和<code>s2</code>中所含的字符完全一样,则它们相等;<code>string</code>对象的相等性判断对字母的大小写敏感</td> +</tr> +<tr> +<td><code>s1!=s2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td> +<td>利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)</td> +</tr> +</tbody> +</table> +<p>读取 <code>string</code> 对象:</p> +<ul> +<li>使用 <code>IO</code> 操作符 <code>&gt;&gt;</code> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。</li> +<li>使用 <code>getline()</code> 函数读取:将一整行读取为 <code>string</code> 对象,包括空白。</li> +</ul> +<p><code>s.size()</code> 返回 <code>string::size_type</code> 类型,是 <strong>无符号</strong> 类型的值,不能和 <code>int</code> 混用。</p> +<p><code>s1 + s2</code> 使用时,必须保证至少其中一个为 <code>string</code> 类型。例如:<code>string s = &quot;hello&quot; + &quot;world&quot;</code> 错误,其 <code>+</code> 两边都为字符串字面值。</p> +<p><strong>字符串字面值</strong> 和 <code>string</code> 是不同的类型。</p> +<h3 id="处理-string-对象中的字符"> + <a href="#%e5%a4%84%e7%90%86-string-%e5%af%b9%e8%b1%a1%e4%b8%ad%e7%9a%84%e5%ad%97%e7%ac%a6">#</a> + 处理 <code>string</code> 对象中的字符 +</h3><p><code>C++</code> 修改了 <code>c</code> 的标准库 <code>ctype.h</code> 为 <code>cctype</code>,其中定义了一组标准函数:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>isalnum(c)</code></td> +<td>当<code>c</code>是字母或数字时为真</td> +</tr> +<tr> +<td><code>isalpha(c)</code></td> +<td>当<code>c</code>是字母时为真</td> +</tr> +<tr> +<td><code>iscntrl(c)</code></td> +<td>当<code>c</code>是控制字符时为真</td> +</tr> +<tr> +<td><code>isdigit(c)</code></td> +<td>当<code>c</code>是数字时为真</td> +</tr> +<tr> +<td><code>isgraph(c)</code></td> +<td>当<code>c</code>不是空格但可以打印时为真</td> +</tr> +<tr> +<td><code>islower(c)</code></td> +<td>当<code>c</code>是小写字母时为真</td> +</tr> +<tr> +<td><code>isprint(c)</code></td> +<td>当<code>c</code>是可打印字符时为真</td> +</tr> +<tr> +<td><code>ispunct(c)</code></td> +<td>当<code>c</code>是标点符号时为真</td> +</tr> +<tr> +<td><code>isspace(c)</code></td> +<td>当<code>c</code>是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)</td> +</tr> +<tr> +<td><code>isupper(c)</code></td> +<td>当<code>c</code>是大写字母时为真</td> +</tr> +<tr> +<td><code>isxdigit(c)</code></td> +<td>当<code>c</code>是十六进制数字时为真</td> +</tr> +<tr> +<td><code>tolower(c)</code></td> +<td>当<code>c</code>是大写字母,输出对应的小写字母;否则原样输出<code>c</code></td> +</tr> +<tr> +<td><code>toupper(c)</code></td> +<td>当<code>c</code>是小写字母,输出对应的大写字母;否则原样输出<code>c</code></td> +</tr> +</tbody> +</table> +<p>遍历字符串:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">c</span> <span class="p">:</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>str[idx]</code> 中的 <code>idx</code> 为 <code>string::size_type</code> 类型,如果使用 <code>int</code> 会隐式转换为该类型。</p> +<h2 id="标准库类型-vector"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-vector">#</a> + 标准库类型 <code>vector</code> +</h2><p>标准库类型 <code>vector</code> 表示对象的集合,其中给所有对象的类型都相同。因为 <code>vector</code> 容纳着其他对象,所以称其为 <strong>容器(container)</strong>,使用 <code>vector</code> 必须包含其头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>vector</code> 同时也是 <strong>类模板(class template)</strong>,模板本身不是类或函数,但可以使用模板创建类,这个过程称为 <strong>实例化(instantiation)</strong>。</p> +<p>当使用模板时,需要指出编译器应把类或函数实例化成何种类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// ls 保存 int 类型的对象 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">files</span><span class="p">;</span> <span class="c1">// 该向量中的元素是 vector 对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p><code>vector</code> 是模板,<code>vector&lt;int&gt;</code> 是类型。</p> +</blockquote> +<h3 id="定义和初始化-vector-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-vector-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>vector</code> 对象 +</h3><p>初始化<code>vector</code>对象的方法:</p> +<table> +<thead> +<tr> +<th>方法</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector&lt;T&gt; v1</code></td> +<td><code>v1</code>是一个空<code>vector</code>,它潜在的元素是<code>T</code>类型的,执行默认初始化</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2(v1)</code></td> +<td><code>v2</code>中包含有<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2 = v1</code></td> +<td>等价于<code>v2(v1)</code>,<code>v2</code>中包含<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v3(n, val)</code></td> +<td><code>v3</code>包含了n个重复的元素,每个元素的值都是<code>val</code></td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v4(n)</code></td> +<td><code>v4</code>包含了n个重复地执行了值初始化的对象</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5{a, b, c...}</code></td> +<td><code>v5</code>包含了初始值个数的元素,每个元素被赋予相应的初始值</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5={a, b, c...}</code></td> +<td>等价于<code>v5{a, b, c...}</code></td> +</tr> +</tbody> +</table> +<h3 id="vector-对象的操作"> + <a href="#vector-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>vector</code> 对象的操作: +</h3><p><code>vector</code>支持的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>v.emtpy()</code></td> +<td>如果<code>v</code>不含有任何元素,返回真;否则返回假</td> +</tr> +<tr> +<td><code>v.size()</code></td> +<td>返回<code>v</code>中元素的个数</td> +</tr> +<tr> +<td><code>v.push_back(t)</code></td> +<td>向<code>v</code>的尾端添加一个值为<code>t</code>的元素</td> +</tr> +<tr> +<td><code>v[n]</code></td> +<td>返回<code>v</code>中第<code>n</code>个位置上元素的<strong>引用</strong></td> +</tr> +<tr> +<td><code>v1 = v2</code></td> +<td>用<code>v2</code>中的元素拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 = {a,b,c...}</code></td> +<td>用列表中元素的拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 == v2</code></td> +<td><code>v1</code>和<code>v2</code>相等当且仅当它们的元素数量相同且对应位置的元素值都相同</td> +</tr> +<tr> +<td><code>v1 != v2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>,<code>&lt;=</code>,<code>&gt;</code>, <code>&gt;=</code></td> +<td>以字典顺序进行比较</td> +</tr> +</tbody> +</table> +<h2 id="迭代器介绍"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e4%bb%8b%e7%bb%8d">#</a> + 迭代器介绍 +</h2><p>除了下标运算符外,<strong>迭代器(iterator)</strong> 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。</p> +<h3 id="使用迭代器"> + <a href="#%e4%bd%bf%e7%94%a8%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 使用迭代器 +</h3><p>拥有迭代器的类型都具有 <code>begin</code> 和 <code>end</code> 成员,其中 <code>begin</code> 成员返回指向第一个元素的迭代器:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">e</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="c1">// b 和 e 类型相同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><code>end</code> 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 <code>end</code> 指向容器的 <strong>尾后(off the end)</strong> 元素。这样的迭代器通常没有意义,只是作为标记,被称为 <strong>尾后迭代器(off-the-end iterator)</strong> 或 <strong>尾迭代器(end iterator)</strong>。</p> +<blockquote> +<p>若容器为空,则 <code>begin</code> 和 <code>end</code> 都返回尾后迭代器。</p> +</blockquote> +<p>标准容器迭代器的运算符:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>*iter</code></td> +<td>返回迭代器<code>iter</code>所指向的<strong>元素的引用</strong></td> +</tr> +<tr> +<td><code>iter-&gt;mem</code></td> +<td>等价于<code>(*iter).mem</code></td> +</tr> +<tr> +<td><code>++iter</code></td> +<td>令<code>iter</code>指示容器中的下一个元素</td> +</tr> +<tr> +<td><code>--iter</code></td> +<td>令<code>iter</code>指示容器中的上一个元素</td> +</tr> +<tr> +<td><code>iter1 == iter2</code></td> +<td>判断两个迭代器是否相等</td> +</tr> +</tbody> +</table> +<blockquote> +<p>泛型编程:尽量使用 <code>!=</code> 来对迭代器进行判断</p> +</blockquote> +<p>迭代器也拥有自己的类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="p">;</span> <span class="c1">// it 是 vector&lt;int&gt; 类型的迭代器,可以读写元素 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">const_iterator</span> <span class="n">it2</span><span class="p">;</span> <span class="c1">// it2 只能读,不能写 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果容器中的值为常量,则 <code>begin</code> 和 <code>end</code> 返回 <code>const_iterator</code>,否则返回 <code>iterator</code>。</p> +<p>解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="s">&#34;str1&#34;</span><span class="p">,</span> <span class="s">&#34;str2&#34;</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">ls</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> <span class="c1">// s 为 &#34;str1&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">).</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 解引用访问 string 成员 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 作用同上 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="迭代器运算"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e8%bf%90%e7%ae%97">#</a> + 迭代器运算 +</h3><p><code>string</code> 和 <code>vector</code> 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 <strong>迭代器运算(iterator arithmetic)</strong>:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iter + n</code></td> +<td>迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter - n</code></td> +<td>迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter1 += n</code></td> +<td>迭代器加法的复合赋值语句,将<code>iter1</code>加n的结果赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 -= n</code></td> +<td>迭代器减法的复合赋值语句,将<code>iter2</code>减n的加过赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 - iter2</code></td> +<td>两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。</td> +</tr> +<tr> +<td><code>&gt;</code>、<code>&gt;=</code>、<code>&lt;</code>、<code>&lt;=</code></td> +<td>迭代器的关系运算符,如果某迭代器</td> +</tr> +</tbody> +</table> +<p>当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 <code>difference_type</code> 类型,是带符号整数型。</p> +<h2 id="数组"> + <a href="#%e6%95%b0%e7%bb%84">#</a> + 数组 +</h2><p>数组可以看做 <code>vector</code> 的低配版,其 <strong>长度固定</strong>。</p> +<h3 id="定义和初始化内置数组"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96%e5%86%85%e7%bd%ae%e6%95%b0%e7%bb%84">#</a> + 定义和初始化内置数组 +</h3><p>数组的声明和定义形如 <code>a[d]</code>,其中 <code>a</code> 是数组的名字,<code>d</code> 是数组的维度(大于 0):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="n">cnt2</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">arr2</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整型指针的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr3</span><span class="p">[</span><span class="n">cnt</span><span class="p">];</span> <span class="c1">// 报错,cnt 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr4</span><span class="p">[</span><span class="n">cnt2</span><span class="p">];</span> <span class="c1">// 含有 42 和整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> <span class="c1">// 自动计算长度的数组 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> <span class="c1">// 报错,长度不够 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a2</span><span class="p">[]</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组来初始化数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组进行赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="访问数组元素"> + <a href="#%e8%ae%bf%e9%97%ae%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0">#</a> + 访问数组元素 +</h3><p>数组的下标为 <code>size_t</code> 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。</p> +<blockquote> +<p>下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。</p> +</blockquote> +<h3 id="指针和数组"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 指针和数组 +</h3><p>使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">ls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ls</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// 数组的元素的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// 等价于 *p2 = &amp;ls[0] +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="c-风格字符串"> + <a href="#c-%e9%a3%8e%e6%a0%bc%e5%ad%97%e7%ac%a6%e4%b8%b2">#</a> + <code>C</code> 风格字符串 +</h3><p>字符串字面值是一种通用结构的实例,这种结构是 <code>C++</code> 由 <code>C</code> 继承而来的 <strong><code>C</code> 风格字符串(C-style character string)</strong> 。 +按此习惯书写的字符串存放在字符数组中并以 <strong>空字符结束(null terminated)</strong>。</p> +<blockquote> +<p>在 <code>C++</code> 程序中尽量不要使用 <code>C</code> 风格字符串,容易引起安全漏洞且不方便。</p> +</blockquote> +<p>C标准库String函数,定义在<code>&lt;cstring&gt;</code> 中:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>strlen(p)</code></td> +<td>返回<code>p</code>的长度,空字符不计算在内</td> +</tr> +<tr> +<td><code>strcmp(p1, p2)</code></td> +<td>比较<code>p1</code>和<code>p2</code>的相等性。如果<code>p1==p2</code>,返回0;如果<code>p1&gt;p2</code>,返回一个正值;如果<code>p1&lt;p2</code>,返回一个负值。</td> +</tr> +<tr> +<td><code>strcat(p1, p2)</code></td> +<td>将<code>p2</code>附加到<code>p1</code>之后,返回<code>p1</code></td> +</tr> +<tr> +<td><code>strcpy(p1, p2)</code></td> +<td>将<code>p2</code>拷贝给<code>p1</code>,返回<code>p1</code></td> +</tr> +</tbody> +</table> +<h2 id="多维数组"> + <a href="#%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 多维数组 +</h2><p>严格来说,<code>C++</code> 语言中没有多维数组,所谓的多维数组实际是数组的数组。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用范围-for-语句处理多维数组"> + <a href="#%e4%bd%bf%e7%94%a8%e8%8c%83%e5%9b%b4-for-%e8%af%ad%e5%8f%a5%e5%a4%84%e7%90%86%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 使用范围 <code>for</code> 语句处理多维数组 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">row</span><span class="p">:</span> <span class="n">arr</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">col</span><span class="p">:</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">col</span> <span class="o">=</span> <span class="n">cnt</span><span class="p">;</span> <span class="n">cnt</span> <span class="o">++</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch02 + https://3000ye.com/p/c-primer-ch02/ + Mon, 06 Nov 2023 22:34:55 +0800 + + https://3000ye.com/p/c-primer-ch02/ + <img src="https://3000ye.com/p/c-primer-ch02/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch02" /><h1 id="变量和基本类型"> + <a href="#%e5%8f%98%e9%87%8f%e5%92%8c%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b">#</a> + 变量和基本类型 +</h1><p>变量提供一个具名的、可供程序操作的存储空间。<code>C++</code>中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。</p> +<h2 id="变量的声明和定义"> + <a href="#%e5%8f%98%e9%87%8f%e7%9a%84%e5%a3%b0%e6%98%8e%e5%92%8c%e5%ae%9a%e4%b9%89">#</a> + 变量的声明和定义 +</h2><p>为了允许把程序拆分成多个逻辑部分来编写,<code>C++</code>语言支持<strong>分离式编译(separate complication)</strong> 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。</p> +<p>为了支持分离式编译,<code>C++</code>语言将声明和定义区分开来。<strong>声明(declaration)</strong> 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 <strong>定义(definition)</strong> 负责创建与名字关联的实体。</p> +<p>变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 声明 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义并赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义而非声明,extern失效 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><strong>注意:</strong> 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。</p> +<h3 id="标识符"> + <a href="#%e6%a0%87%e8%af%86%e7%ac%a6">#</a> + 标识符 +</h3><p><code>C++</code>的 <strong>标识符(identifier)</strong> 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。</p> +<h3 id="作用域"> + <a href="#%e4%bd%9c%e7%94%a8%e5%9f%9f">#</a> + 作用域 +</h3><p><strong>作用域(scope)</strong> 是程序的一部分,在其中名字有特定的含义,<code>C++</code>语言中大多数作用域都以花括号分隔。</p> +<p>作用域能彼此包含,被包含(嵌套)的作用域称为 <strong>内层作用域(inner scope)</strong>,包含着别的作用域的作用域称为 <strong>外层作用域(outer scope)</strong>。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 全局作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">inner</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span> <span class="c1">// 内层作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用全局变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 局部重新定义,覆盖全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用局部变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 显式访问全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">::</span><span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="复合类型"> + <a href="#%e5%a4%8d%e5%90%88%e7%b1%bb%e5%9e%8b">#</a> + 复合类型 +</h2><p><strong>复合类型(compound type)</strong> 是指基于其他类型定义的类型,主要介绍引用和指针。</p> +<h3 id="引用"> + <a href="#%e5%bc%95%e7%94%a8">#</a> + 引用 +</h3><p><strong>引用(reference)</strong> 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 <strong>绑定(bind)</strong> 在一起,而不是将初始者拷贝给引用。</p> +<blockquote> +<p>引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。</p> +</blockquote> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 引用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 修改 y 的值,实际是修改 x 的值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针"> + <a href="#%e6%8c%87%e9%92%88">#</a> + 指针 +</h3><p><strong>指针(pointer)</strong> 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:</p> +<ul> +<li>指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。</li> +<li>指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ip1</span><span class="p">,</span> <span class="o">*</span><span class="n">ip2</span><span class="p">;</span> <span class="c1">// ip1 和 ip2 都是指向 int 类型对象的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">dp</span><span class="p">,</span> <span class="o">*</span><span class="n">dp2</span><span class="p">;</span> <span class="c1">// dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>指针存放某个对象的地址,想获取该地址,需要使用 <strong>取地址符(<code>&amp;</code>)</strong>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。</p> +</blockquote> +<p>指针的值(即地址)应属于下列 4 种状态之一:</p> +<ul> +<li>指向一个对象。</li> +<li>指向紧邻对象所占空间的下一个位置。</li> +<li>空指针,意味着指针没有指向任何对象。</li> +<li>无效指针,也就是上述情况之外的其他值。</li> +</ul> +<p>如果指针指向了一个对象,则允许使用 <strong>解引用符(<code>*</code>)</strong> 来访问该对象:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">p</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 由符号 * 得到指针 p 所指的对象,输出 42 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>解引用操作仅适用于有效指针,无效指针无法解引用。</p> +</blockquote> +<p><strong>空指针(null pointer)</strong> 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。</p> +<p>生成空指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> <span class="c1">// 等价于 int *p1 = 0 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。</p> +<p>指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi 为空指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">;</span> <span class="c1">// pi2 存放 i 的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi3</span><span class="p">;</span> <span class="c1">// pi3 的值无法确定 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="n">pi3</span> <span class="o">=</span> <span class="n">pi2</span><span class="p">;</span> <span class="c1">// pi3 和 pi2 指向同一个对象 i +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi2 变为空指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="const-限定符"> + <a href="#const-%e9%99%90%e5%ae%9a%e7%ac%a6">#</a> + const 限定符 +</h2><p>有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字<code>const</code>对变量的类型加以限定:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> <span class="c1">// 限定缓冲区大小为 512 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>默认情况下,<code>const</code>对象被设定为仅在单个文件内有效。当多个文件中出现了同名的<code>const</code>变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上<code>extern</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 定义文件 xxx.cpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 声明文件 xxx.hpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针和-const"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c-const">#</a> + 指针和 const +</h3><p>与引用一样,也可以令指针指向常量与非常量。<strong>指向常量的指针(pointer to const)</strong> 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// val 为常量对像,其值不能改变 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 报错,pi 为普通指针,不能存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 使用指向常量的指针存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// 报错,不能给指向常量的指针赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="处理类型"> + <a href="#%e5%a4%84%e7%90%86%e7%b1%bb%e5%9e%8b">#</a> + 处理类型 +</h2><p>随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。</p> +<h3 id="类型别名"> + <a href="#%e7%b1%bb%e5%9e%8b%e5%88%ab%e5%90%8d">#</a> + 类型别名 +</h3><p><strong>类型别名(type alias)</strong> 是一个名字,它是某种类型的同义词。</p> +<p>使用关键字<code>typedef</code>定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用 <strong>别名声明(alias declaration)</strong> 定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="auto-类型说明符"> + <a href="#auto-%e7%b1%bb%e5%9e%8b%e8%af%b4%e6%98%8e%e7%ac%a6">#</a> + auto 类型说明符 +</h3><p>编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此<code>C++11</code>引入了<code>auto</code>类型说明符,它能自动分析表达式结果的类型。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">item</span> <span class="o">=</span> <span class="n">val1</span> <span class="o">+</span> <span class="n">val2</span><span class="p">;</span> <span class="c1">// item 初始化为 val1 和 val2 相加的结果 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>auto</code>定义或声明多个变量时,所有变量的类型必须一致:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mf">4.2</span><span class="p">;</span> <span class="c1">// 报错,同一行的变量类型必须一致 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="decltype-类型指示符"> + <a href="#decltype-%e7%b1%bb%e5%9e%8b%e6%8c%87%e7%a4%ba%e7%ac%a6">#</a> + decltype 类型指示符 +</h3><p>有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。</p> +<p>因此<code>C++11</code>引入了<code>decltype</code>类型指示符,它的作用是返回操作数的数据类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">decltype</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// z 为 int 类型 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="自定义数据结构"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84">#</a> + 自定义数据结构 +</h2><p>内置的数据类型并不能满足所有的需求,因此<code>c++</code>提供了自定义数据类型的方式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">jack</span><span class="p">{</span><span class="s">&#34;jack&#34;</span><span class="p">,</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 先声明后赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">castor</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="n">castor</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;castor&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">sex</span> <span class="o">=</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">gender</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义数据结构使用别名"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%bd%bf%e7%94%a8%e5%88%ab%e5%90%8d">#</a> + 自定义数据结构使用别名 +</h3><p>和内置数据类型一样,自定义数据结构也能使用别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="n">studeng</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 或直接在定义时使用别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编写自己的头文件"> + <a href="#%e7%bc%96%e5%86%99%e8%87%aa%e5%b7%b1%e7%9a%84%e5%a4%b4%e6%96%87%e4%bb%b6">#</a> + 编写自己的头文件 +</h3><p>当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// studeng.h +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp">#define STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;string&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="cp">#endif +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + Slides + https://3000ye.com/p/slides/ + Fri, 20 Oct 2023 10:04:55 +0800 + + https://3000ye.com/p/slides/ + <img src="https://3000ye.com/p/slides/assets/latex.jpg" alt="Featured image of post Slides" /><h1 id="使用-latex-绘制-slides"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e7%bb%98%e5%88%b6-slides">#</a> + 使用 $\LaTeX$ 绘制 Slides +</h1> + + + dhuBachelor + https://3000ye.com/p/dhubachelor/ + Thu, 12 Oct 2023 16:41:55 +0800 + + https://3000ye.com/p/dhubachelor/ + <img src="https://3000ye.com/p/dhubachelor/assets/latex.jpg" alt="Featured image of post dhuBachelor" /><h1 id="东华大学学士毕业论文-latex-模板使用手册"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e5%a3%ab%e6%af%95%e4%b8%9a%e8%ae%ba%e6%96%87-latex-%e6%a8%a1%e6%9d%bf%e4%bd%bf%e7%94%a8%e6%89%8b%e5%86%8c">#</a> + 东华大学学士毕业论文 LaTeX 模板使用手册 +</h1><h2 id="导入模板"> + <a href="#%e5%af%bc%e5%85%a5%e6%a8%a1%e6%9d%bf">#</a> + 导入模板 +</h2><h3 id="使用-git-克隆仓库"> + <a href="#%e4%bd%bf%e7%94%a8-git-%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93">#</a> + 使用 Git 克隆仓库 +</h3><p>将仓库克隆到你需要的位置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@github.com:3000ye/dhuBachelor.git +</span></span><span class="line"><span class="cl"><span class="c1"># 或</span> +</span></span><span class="line"><span class="cl">git clone https://github.com/3000ye/dhuBachelor.git +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="下载-zip-文件"> + <a href="#%e4%b8%8b%e8%bd%bd-zip-%e6%96%87%e4%bb%b6">#</a> + 下载 Zip 文件 +</h3><p>如果你不会使用<code>Git</code>,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载<code>Zip</code>文件:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/zip.png" alt="img" style="zoom:100%"/> +</div> +<h3 id="微信公众号获取"> + <a href="#%e5%be%ae%e4%bf%a1%e5%85%ac%e4%bc%97%e5%8f%b7%e8%8e%b7%e5%8f%96">#</a> + 微信公众号获取 +</h3><p>如果你没有科学代理,无法进入<code>Github</code>,请扫码关注公众号:<code>3000ye Blog</code>然后回复<code>latex模板</code>获取百度网盘分享链接。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/weixin.jpg" alt="img" style="zoom:100%"/> +</div> +<h2 id="开始使用"> + <a href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8">#</a> + 开始使用 +</h2><h3 id="安装字体"> + <a href="#%e5%ae%89%e8%a3%85%e5%ad%97%e4%bd%93">#</a> + 安装字体 +</h3><p>在使用模板之前,请先找到并打开<code>fonts</code>文件夹,安装里面的所有字体。</p> +<h3 id="配置-tex-环境"> + <a href="#%e9%85%8d%e7%bd%ae-tex-%e7%8e%af%e5%a2%83">#</a> + 配置 TeX 环境 +</h3><p>如果你是 $\LaTeX$ 小白,请先行阅读:<a class="link" href="https://3000ye.com/p/elegant-latex/" target="_blank" rel="noopener" + >使用 LaTeX 优雅地完成创作</a>,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。</p> +<h3 id="尝试编译"> + <a href="#%e5%b0%9d%e8%af%95%e7%bc%96%e8%af%91">#</a> + 尝试编译 +</h3><p>使用你喜欢的编辑器,打开<code>dhuBachelor.tex</code>文件,选择<code>xelatex</code>命令编译文件。如果没有出现报错,同目录下会生成一个<code>dhuBachelor.pdf</code>文件,这就是我们的论文。</p> +<h2 id="各个组件说明"> + <a href="#%e5%90%84%e4%b8%aa%e7%bb%84%e4%bb%b6%e8%af%b4%e6%98%8e">#</a> + 各个组件说明 +</h2><p>看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。</p> +<p>本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。</p> +<h3 id="论文题目"> + <a href="#%e8%ae%ba%e6%96%87%e9%a2%98%e7%9b%ae">#</a> + 论文题目 +</h3><p>根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reTitle</span><span class="nb">{</span>中文题目<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\reTitleEN</span><span class="nb">{</span>英文题目<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="摘要"> + <a href="#%e6%91%98%e8%a6%81">#</a> + 摘要 +</h3><p>根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reAbstract</span> <span class="c">% 中文摘要 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reAbstractEN</span> <span class="c">% 英文摘要 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>摘要内容直接写在摘要下方,首行缩进两字符。</p> +<h3 id="关键词"> + <a href="#%e5%85%b3%e9%94%ae%e8%af%8d">#</a> + 关键词 +</h3><p>中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。</p> +<p>英文关键词:Times New Roman(标题加黑)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reKeyword</span><span class="nb">{</span>关键词1,关键词2,关键词3,关键词4<span class="nb">}</span> <span class="c">% 中文关键词 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reKeywordEN</span><span class="nb">{</span>Keyword1, Keyword2, Keyword3<span class="nb">}</span> <span class="c">% 英文关键词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="目录"> + <a href="#%e7%9b%ae%e5%bd%95">#</a> + 目录 +</h3><p>目录标题需居中显示,添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\tableofcontents</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="各级标题"> + <a href="#%e5%90%84%e7%ba%a7%e6%a0%87%e9%a2%98">#</a> + 各级标题 +</h3><p>规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reSection</span><span class="nb">{</span>一级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsection</span><span class="nb">{</span>二级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsubsection</span><span class="nb">{</span>三级标题<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="有序列表"> + <a href="#%e6%9c%89%e5%ba%8f%e5%88%97%e8%a1%a8">#</a> + 有序列表 +</h3><p>规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\orderedList</span><span class="nb">{</span> <span class="c">% 使用 (i) 排序,缩进 2 字符 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> <span class="c">% \par的作用是将内容换行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> +</span></span><span class="line"><span class="cl"> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="数学公式"> + <a href="#%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 数学公式 +</h3><p>行内公式:<code>$f(x) = x + 1$</code></p> +<p>跨行公式:跨行公式请使用<code>equation</code>环境,默认按照章节自动编号。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> x = a<span class="nb">_</span>0 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>1 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>2 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>3 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>4<span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改<code>width</code>大小和图片路径,并根据实际更改图例和索引。</p> +<p>注意:图片需要保存在<code>assets/</code>目录中才能被正确插入,读者可以自己新建其他目录实现插入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] <span class="c">% 图片位于文字下方 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> <span class="c">% 居中 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="c">% 设置图片占页面宽度的比例(默认0.8) +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片标题<span class="nb">}</span> <span class="c">% 图例,按章节编号 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>fig: 数据结构2<span class="nb">}</span> <span class="c">% 图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>有时可能需要多图并排,模板使用<code>minipage</code>来实现并排展示,使用时可以修改子图所占比例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\hspace</span><span class="nb">{</span>1em<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入表格"> + <a href="#%e6%8f%92%e5%85%a5%e8%a1%a8%e6%a0%bc">#</a> + 插入表格 +</h3><p>使用 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a> 工具生成表格代码后,需要手动添加分割线(<code>\toprule, \midrule, \bottomrule</code>),以达到三线表的格式要求。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||l<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> parameter <span class="nb">&amp;</span> Description <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">I</span><span class="s">$</span> <span class="nb">&amp;</span> Land area collection <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">J</span><span class="s">$</span> <span class="nb">&amp;</span> Flower pollination demand set <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">D_j</span><span class="s">$</span> <span class="nb">&amp;</span> Number of pollinating bees required for flower pollination <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">T_k</span><span class="s">$</span> <span class="nb">&amp;</span> Honeycomb size grade, <span class="s">$</span><span class="nb">k </span><span class="o">=</span><span class="nb"> </span><span class="m">1</span><span class="nb">, </span><span class="m">2</span><span class="nb">, </span><span class="nv">\cdots</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">B</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum number of hive <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">R_{ik}</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum influence radius of a single honeycomb <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 一个表<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>对于需要多表并排的情况,和图片的方式类似,使用<code>minipage</code>来实现:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格1标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格2标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bb%a3%e7%a0%81">#</a> + 插入代码 +</h3><p>可以直接在<code>.tex</code>文件中编写代码,并指定语言和标题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>lstlisting<span class="nb">}</span>[language=c++,title=<span class="nb">{</span>code.cpp<span class="nb">}</span>] +</span></span><span class="line"><span class="cl">#include &#34;bits/stdc++.h&#34; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">using namespace std; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">int main() <span class="nb">{</span> +</span></span><span class="line"><span class="cl"> cout &lt;&lt; &#34;3000ye 的 LaTeX 模板!&#34; &lt;&lt; endl; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> return 0; +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>lstlisting<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在<code>assets/</code>目录下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\lstinputlisting</span><span class="na">[language=c++, title=code.cpp]</span><span class="nb">{</span>code/code.cpp<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入伪代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bc%aa%e4%bb%a3%e7%a0%81">#</a> + 插入伪代码 +</h3><p>使用宏包<code>algorithm, algorithmic</code>来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>Example Pseudocode<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="m">0</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\IF</span> <span class="nb">{</span><span class="s">$</span><span class="nb">x</span><span class="nv">\leq</span><span class="nb"> </span><span class="m">0</span><span class="s">$</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">+</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ELSE</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">-</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ENDIF</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="参考文献"> + <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae">#</a> + 参考文献 +</h3><p>参考文献使用<code>\bibitem</code>来添加,添加时需要手动更改<code>{RNi}</code>索引(<code>i</code>是你文献的序号)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reference</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN1<span class="nb">}</span> 参考文献1 +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN2<span class="nb">}</span> 参考文献2 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="致谢"> + <a href="#%e8%87%b4%e8%b0%a2">#</a> + 致谢 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reThanks</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> 致谢,3000ye 的 <span class="k">\LaTeX</span> 模板! +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Elegant LaTeX + https://3000ye.com/p/elegant-latex/ + Mon, 09 Oct 2023 17:18:55 +0800 + + https://3000ye.com/p/elegant-latex/ + <img src="https://3000ye.com/p/elegant-latex/assets/latex.jpg" alt="Featured image of post Elegant LaTeX" /><h1 id="使用-latex-优雅地完成创作"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e4%bc%98%e9%9b%85%e5%9c%b0%e5%ae%8c%e6%88%90%e5%88%9b%e4%bd%9c">#</a> + 使用 $\LaTeX$ 优雅地完成创作 +</h1><p>$\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\LaTeX$ 使用 $\TeX$ 作为它的排版引擎,学习 $\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。</p> +<h2 id="从安装-tex-引擎开始"> + <a href="#%e4%bb%8e%e5%ae%89%e8%a3%85-tex-%e5%bc%95%e6%93%8e%e5%bc%80%e5%a7%8b">#</a> + 从安装 $\TeX$ 引擎开始 +</h2><p>$\TeX$ 引擎类似于 <code>gcc/g++</code> 或 <code>Python</code>,用于编译 $\LaTeX$ 文档。</p> +<p>不同平台中 $\TeX$ 的安装方法不尽相同,本文提供:<code>Windows11</code>、<code>Linux(Ubuntu 22.04)</code>、<code>MacOs(12.7)</code>、<code>Windows11-wsl2(Ubuntu22.04)</code>的安装方法。</p> +<p>如果你只是想简单体验 $\LaTeX$,可以使用 <a class="link" href="https://www.overleaf.com/" target="_blank" rel="noopener" + >overleaf</a> 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。</p> +<h3 id="windows11"> + <a href="#windows11">#</a> + Windows11 +</h3><p>进入网站<a class="link" href="https://tug.org/texlive/windows.html#install" target="_blank" rel="noopener" + >tug.org for windows</a>,点击<code>install-tl-windows.exe</code>下载 $\TeX$ 安装器,然后运行安装即可。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/windowsInstall.png" alt="img" style="zoom:100%"/> +</div> +<p>不过,这种方法需要一直联网安装,网速不好的环境可以直接下载<code>iso</code>镜像进行本地安装。本文给出清华源镜像地址:<a class="link" href="https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/" target="_blank" rel="noopener" + >mirrors.tuna</a>,下载后缀为<code>.iso</code>的文件(只用下载一个)。</p> +<p>下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击<code>install-tl-windows.bat</code>文件,使用管理员打开,然后按照指引安装即可。</p> +<p>最新版本的安装器会自动添加环境变量,安装完成后打开<code>cmd</code>然后输入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/W32TeX<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="linuxubuntu2204"> + <a href="#linuxubuntu2204">#</a> + Linux(Ubuntu22.04) +</h3><p>打开终端,然后执行安装命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update +</span></span><span class="line"><span class="cl">sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install texlive-full +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/Debian<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="macos127"> + <a href="#macos127">#</a> + MacOs(12.7) +</h3><p>打开终端,然后执行安装命令(推荐安装无窗体版本):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install mactex-no-gui +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="windows11-wsl2ubuntu2204"> + <a href="#windows11-wsl2ubuntu2204">#</a> + Windows11-wsl2(Ubuntu22.04) +</h3><p>在<code>wsl2</code>中安装方式与在<code>Linux</code>中一样。</p> +<h2 id="找到属于你的编辑器"> + <a href="#%e6%89%be%e5%88%b0%e5%b1%9e%e4%ba%8e%e4%bd%a0%e7%9a%84%e7%bc%96%e8%be%91%e5%99%a8">#</a> + 找到属于你的编辑器 +</h2><p>市面上有很多 $\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:</p> +<ul> +<li>全平台通用: +<ul> +<li><code>Vs Code</code>:作为地表最强编辑器,<code>Vs Code</code>拥有非常丰富的 $\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。</li> +<li><code>Jetbrains</code>: 与<code>Vs Code</code>相对应的是<code>Jetbrains</code>系列, 其虽然也有 $\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。</li> +<li><code>Neovim</code>:如果说<code>Vs Code</code>是编辑器中的王后,那么<code>nvim</code>就是国王。<code>nvim</code>可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。</li> +<li><code>sublime text</code>:<code>nvim</code>固然强大,但其难以上手的特点使得很多人对其望而却步。<code>sublime</code>打破了这个束缚,其界面优雅程度不亚于<code>nvim</code>,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。</li> +<li><code>TexStudio</code>:<code>texlive</code>默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。</li> +</ul> +</li> +<li><code>Windows</code>独占: +<ul> +<li><code>Winedt 11</code>:如果不考虑跨平台,那么<code>Winedt 11</code>就是<code>Windows</code>上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。</li> +</ul> +</li> +</ul> +<p>综上所述,笔者最推荐<code>Vs Code</code>,但如果你只有<code>Windows</code>平台的使用需求并不介意一点费用的话,请果断购买<code>Winedt 11</code>。同样的,<code>MacOs</code>也拥有独占编辑器,但笔者没用过,在此不做评价。</p> +<p>关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。</p> +<h2 id="一本教程入门-latex-语法"> + <a href="#%e4%b8%80%e6%9c%ac%e6%95%99%e7%a8%8b%e5%85%a5%e9%97%a8-latex-%e8%af%ad%e6%b3%95">#</a> + 一本教程入门 $\LaTeX$ 语法 +</h2><p>对于所有初学者来说,<a class="link" href="https://mirror-hk.koddos.net/CTAN/info/lshort/chinese/lshort-zh-cn.pdf" target="_blank" rel="noopener" + >Ishort-zh-cn</a>都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。</p> +<p>如果你已经完成了所有的案例,相信你已经对 $\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。</p> +<h3 id="打印中文"> + <a href="#%e6%89%93%e5%8d%b0%e4%b8%ad%e6%96%87">#</a> + 打印中文 +</h3><p>$\LaTeX$ 默认只打印英文,如果没有合理的设置,<code>.tex</code>文件中的中文将无法正确打印。</p> +<p>从下图可以看出,目前支持全平台通用的方案只有<code>XeLaTeX</code>和<code>LuaTeX</code>。因此,主流方案是一般使用<code>xelatex+ctex</code>编译方案,底层调用<code>xeCJK</code>字符集来实现中文打印。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/xeCJK.png' alt='img' style='zoom:100%;' /> +</div> +<p>使用时只需在导言区加入,编译器会使用默认字体进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8]</span><span class="nb">{</span>ctex<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果要指定字体,则需分别设置<code>font-family</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8, fontset=none]</span><span class="nb">{</span>ctex<span class="nb">}</span> <span class="c">% 清除默认字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmainfont</span><span class="nb">{</span>SimSun<span class="nb">}</span>[AutoFakeBold=true, BoldFont=<span class="nb">{</span>SimHei<span class="nb">}</span>, ItalicFont=<span class="nb">{</span>KaiTi<span class="nb">}</span>] <span class="c">% 正文字体(宋体,黑体,楷体) +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKsansfont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>KaiTi<span class="nb">}</span> <span class="c">% 无衬线字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmonofont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>SimHei<span class="nb">}</span> <span class="c">% 等宽字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>同样的,英文也可以自定义字体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\setmainfont</span><span class="nb">{</span>Times New Roman<span class="nb">}</span> <span class="c">% 设置英文字体为新罗马体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>详细配置可以参考:<a class="link" href="https://zhuanlan.zhihu.com/p/538459335" target="_blank" rel="noopener" + >LaTex 中文字体配置指南</a></p> +</blockquote> +<h3 id="打印数学公式"> + <a href="#%e6%89%93%e5%8d%b0%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 打印数学公式 +</h3><p>你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\LaTeX$,这也是 $\LaTeX$ 为什么被奉为珍宝的原因之一。</p> +<p>但是笔者并不建议读者专门花时间来学习如何编写 $\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。</p> +<ul> +<li>在线数学公式生成平台:<a class="link" href="https://www.latexlive.com/" target="_blank" rel="noopener" + >latexlive</a>可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。</li> +<li><code>MathType</code>:如果你是<code>Windows</code>用户,那么强烈建议使用<code>MathType</code>来生成数学公式的 $\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。</li> +<li><code>mathpix</code>:这是一款专为 $\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。</li> +</ul> +<h3 id="绘制表格"> + <a href="#%e7%bb%98%e5%88%b6%e8%a1%a8%e6%a0%bc">#</a> + 绘制表格 +</h3><p>你一定使用过<code>Excel</code>来绘制表格,但是在 $\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。</p> +<p>但不用担心,本文为你介绍开源项目 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a>。这是一款<code>Excel</code>插件,可以将你在<code>Excel</code>中绘制的表格自动转译为 $\LaTeX$ 代码。</p> +<p>但是这个插件并不是万能的,比如绘制三线表,即使是在<code>Excel</code>中也较为繁琐。因此,最好的处理方式是使用<code>excel2latex</code>插件生成表格主体,然后再自己添加分隔格式。</p> +<h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>绘制表格和插入图片并称为 $\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。</p> +<h2 id="学会使用代码片段"> + <a href="#%e5%ad%a6%e4%bc%9a%e4%bd%bf%e7%94%a8%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + 学会使用代码片段 +</h2><p>阅读到这里,相信你已经能够使用 $\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写<code>.sty</code>和<code>.cls</code>文件),但这对初学者来说太难了。</p> +<p>因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。</p> +<p>代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。</p> +<h3 id="vs-code配置代码片段"> + <a href="#vs-code%e9%85%8d%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Vs Code配置代码片段 +</h3><p>点击左下角的设置按钮,然后点击设置用户代码片段:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/snippets.jpg' alt='img' style='zoom:100%;' /> +</div> +<p>在弹出的窗口中输入<code>latex</code>然后选中即可跳转到<code>latex.json</code>文件,我们可以在这里设置我们的代码片段。</p> +<p>比如这段设置,保存文件后我们只需要在<code>.tex</code>后缀的文件中输入<code>insertImg</code>然后回车就会自动填充以下代码,并且使用<code>tab</code>来依次输入参数。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;insertImg&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;prefix&#34;</span><span class="p">:</span> <span class="s2">&#34;insertImg&#34;</span><span class="p">,</span> <span class="c1">// 代码片段别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;body&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="c1">// 代码片段主体 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\begin{figure}[H]&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\centering&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\includegraphics[width=0.8\\textwidth]{$1}&#34;</span><span class="p">,</span> <span class="c1">// 参数1:图片路径 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\caption{$2}&#34;</span><span class="p">,</span> <span class="c1">// 参数2:图片标题 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\label{$3}&#34;</span><span class="p">,</span> <span class="c1">// 参数3:图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\end{figure}$0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;insert one img with 0.8 width&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="winedt-设置代码片段"> + <a href="#winedt-%e8%ae%be%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Winedt 设置代码片段 +</h3><p>依次点击<code>Option -&gt; Options Interface -&gt; Menus and Toolbar -&gt; Main Menu</code>,修改或添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">ITEM</span><span class="o">=</span><span class="s">&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> CAPTION=&#34;&amp;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> IMAGE=&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> MACRO=&#34;Exe(&#39;%b\Menus\Insert\Image.edt&#39;);&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> SHORTCUT=&#34;49222::Ctrl+Alt+F&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> REQ_DOCUMENT=1 </span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后就可以使用快捷键<code>Ctrl+Alt+F</code>填充插入图片的代码片段。</p> +<h2 id="使用模板来专注内容"> + <a href="#%e4%bd%bf%e7%94%a8%e6%a8%a1%e6%9d%bf%e6%9d%a5%e4%b8%93%e6%b3%a8%e5%86%85%e5%ae%b9">#</a> + 使用模板来专注内容 +</h2><p>使用 $\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\LaTeX$ 的初衷。</p> +<p>因此,常见期刊都会提供对应的 $\LaTeX$ 风格模板和示例,其中主要文件通常为:</p> +<ul> +<li><code>.sty</code>:$\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。</li> +<li><code>.cls</code>:$\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。</li> +<li><code>.tex</code>:示例文件,通常会包括论文中会用到的所有样式的示例代码。</li> +</ul> +<p>阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。</p> + + + + Camera Calibration + https://3000ye.com/p/camera-calibration/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/camera-calibration/ + <img src="https://3000ye.com/p/camera-calibration/board.jpg" alt="Featured image of post Camera Calibration" /><h1 id="由浅到深理解相机标定"> + <a href="#%e7%94%b1%e6%b5%85%e5%88%b0%e6%b7%b1%e7%90%86%e8%a7%a3%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 由浅到深理解相机标定 +</h1><h2 id="何为相机标定"> + <a href="#%e4%bd%95%e4%b8%ba%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 何为相机标定 +</h2><p>在图像测量过程以及机器视觉应用中,为确定空间无哦表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。</p> +<p>在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为<strong>相机标定(或摄像头标定)</strong>。</p> +<p><strong>相机标定</strong>涉及的知识面很广:成像几何、镜头畸变、单应矩阵、非线性优化等。</p> +<p><strong>相机标定</strong>有自标定(找图像中特征点)、标定板标定(特征点易求,稳定性好),一般采用标定板标定。</p> +<p><strong>相机标定</strong>按照相机是否静止,可分为静态相机标定(标定板动,相机静止),动态相机标定(标定板静止,相机运动)。</p> +<h3 id="为什么需要标定"> + <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e6%a0%87%e5%ae%9a">#</a> + 为什么需要标定 +</h3><p>任何理论物理模型都是在特定假设上对真实事物的近似,然而在实际应用中存在误差,普通相机的成像模型也不例外(透视投影)。</p> +<p>实际中,普通相机成像误差的主要来源有两部分:</p> +<ul> +<li>第一是sensor(传感器)制造产生的误差,比如sensor成像单元不是正方形,sensor歪斜。</li> +<li>第二是镜头制造和安装产生的误差,镜头一般存在非线性的径向畸变;镜头与相机sensor安装不平行,还会产生切向畸变。</li> +</ul> +<h2 id="相机标定的目的和意义"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e7%9a%84%e7%9b%ae%e7%9a%84%e5%92%8c%e6%84%8f%e4%b9%89">#</a> + 相机标定的目的和意义 +</h2><p>我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。</p> +<p>相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp" + width="898" + height="615" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp 480w, https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="146" + data-flex-basis="350px" + +></p> +<p>这个逼近的过程就是「相机标定」,我们用简单的数学模型来表达复杂的成像过程,并且求出成像的反过程。标定之后的相机,可以进行三维场景的重建,即深度的感知。</p> +<h2 id="相关术语"> + <a href="#%e7%9b%b8%e5%85%b3%e6%9c%af%e8%af%ad">#</a> + 相关术语 +</h2><p><strong>焦点:<strong>在几何光学中有时也称为</strong>像点</strong>,是源头的光线经过物镜后汇聚的点。</p> +<p><strong>焦距:<strong>也称为</strong>焦长</strong>,是光学系统中衡量光的聚集或发散的度量方式,指从透镜中心到光聚集之焦点的距离。亦是照相机中,从镜片光学中心到底片、CCD或CMOS等成像平面的距离。</p> +<p>正透镜、负透镜、凹面镜和凸面镜的焦点<code>F</code>和焦距<code>f</code>:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/1.png" alt="img" style="zoom:50%;" /> +</div> +<p><strong>镜头(Lenses)</strong>:是将拍摄景物在传感器上成像的器件,它通常由几片透镜、光圈叶片、对焦马达等光学元件组成。</p> +<p><strong>传感器(Sensor)</strong>:是摄像头组成的核心,其作用是作为相机的感光元件。摄像头传感器主要有两种,一种是CCD传感器,一种是CMOS传感器,两者区别在于:CCD的优势在于成像质量好,但是由于制造工艺复杂,成本居高不下,特别是大型CCD,价格非常高昂。在相同分辨率下,CMOS价格比CCD便宜,但是CMOS器件产生的图像质量相比CCD来说要低一些。</p> +<p><strong>光心</strong>:凸透镜近轴光线中,入射线和与其对应且相平行的出射线构成共轭光线,其入射点跟出射点的连线与主光轴的交点,称为凸透镜的焦点,位于透镜中央的点叫光心。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/3.png" alt="img" style="zoom:50%;" /> +</div> +<p>从图中可知,<code>O</code>为光心,<code>F</code>为焦点。每个透镜主轴上都有一个特殊点,凡是通过该点的光,其传播方向不变,这个点叫光心。经过光心的光线的传播方向不会发生改变。</p> +<h2 id="相机标定原理模型"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e5%8e%9f%e7%90%86%e6%a8%a1%e5%9e%8b">#</a> + 相机标定原理模型 +</h2><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定.drawio" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<h3 id="针孔相机模型"> + <a href="#%e9%92%88%e5%ad%94%e7%9b%b8%e6%9c%ba%e6%a8%a1%e5%9e%8b">#</a> + 针孔相机模型 +</h3><p>我们通常将相机看成如下所示的透镜模型:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/2.png" alt="img" style="zoom:50%;" /> +</div> +<p>在实际分析时,通常将其简化为针孔模型(小孔成像):</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/4.png" alt="img" style="zoom:50%;" /> +</div> +<p>一般为了分析简单,将成像平面画在对称位置,这样图像不再颠倒:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/5.png" alt="img" style="zoom:50%;" /> +</div> +<h3 id="四个坐标系"> + <a href="#%e5%9b%9b%e4%b8%aa%e5%9d%90%e6%a0%87%e7%b3%bb">#</a> + 四个坐标系 +</h3><p><strong>世界坐标系</strong>:用户定义的三维世界的坐标系,用于描述目标物体在真实世界里的位置。单位通常为米(m)。该坐标系作用于三维空间。</p> +<p><strong>相机坐标系</strong>:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位通常为米(m)。相机坐标系的原点在光心,其 $X_c、Y_c$ 轴分别与像面的两边平行,其 $Z_c$ 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点(实际情况中可能存在<strong>主点偏移</strong>),相机坐标系与图像坐标系之间的距离为焦距 $f$。该坐标系作用于三维空间。</p> +<p><strong>图像坐标系</strong>:为了描述成像过程中物体从相机坐标系到图像坐标系的投影投射关系而引入,方便进一步得到像素坐标系下的坐标。其原点是相机光轴与像面的交点(称为主点),即<strong>图像的中心点</strong>。其 $x, y$ 轴和像素坐标系的 $u, v$ 轴平行,故图像坐标系和像素坐标系实际是平移关系。单位通常为毫米(mm)。该坐标系作用于二维空间。</p> +<p><strong>像素坐标系</strong>:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为像素。像素坐标平面和图像坐标系平面重合,但像素坐标系原点位于图像左上角。该坐标系作用于二维空间。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/6.png" + width="1248" + height="841" + srcset="https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="148" + data-flex-basis="356px" + +></p> +<h3 id="相机外参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%a4%96%e5%8f%82">#</a> + 相机外参 +</h3><p>将世界坐标系中的点映射到相机坐标系:相机坐标系是世界坐标系通过<strong>刚体变换</strong>得到的。</p> +<blockquote> +<p>刚体变换能够保持物体中各点的距离和角度,常见的刚体变换有:平移、旋转和镜像。</p> +</blockquote> +<p>我们先只考虑旋转,假设将坐标系以 $X$ 轴为中心进行旋转,即 $X$ 不变,旋转 $Y - Z$ 平面。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/7.png" alt="img" style="zoom: 33%;" /> +</div> +<p>假设旋转角度为 $\theta$,即 $\angle Y&rsquo; O Y = \angle Z&rsquo; O Z = \theta$。旋转前的坐标系为 $X - Y - Z$,旋转后的坐标系为 $X&rsquo; - Y&rsquo; - Z&rsquo;$。假设点 $P$ 在 $X - Y - Z$ 中的坐标为($X_w, Y_w, Z_w$),旋转后,其在 $X&rsquo; - Y&rsquo; - Z&rsquo;$ 中的坐标为($X_c, Y_c, Z_c$): +$$ +X_C = X_w +$$</p> +<p>$$ +\begin{array}{l} +Y_c &amp; = OC + CD = OA \cdot \sin \theta + BP \\ +&amp; = Z_w \cdot \sin \theta + AP \cdot \cos \theta \\ +&amp; = Z_w \sin \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>$$ +\begin{array}{l} +Z_c &amp; = PD = AC - AB \\ +&amp; = AO \cdot \cos \theta - AP \cdot \cos \theta \\ +&amp; = Z_w \cos \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>写成矩阵形式: +$$\displaystyle \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} = \mathbf{R_{cw}} \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} or \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} = \mathbf{R_{wc}} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} $$ +推广到每个方向,可得到 $\mathbf{R_{cw}}, \mathbf{R_{wc}}$ 为: +$$ +\mathbf{R_{cw}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; \sin \theta \\ +0 &amp; - \sin \theta &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; - \sin \theta \\ +0 &amp; \sin \theta &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; \sin \theta \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; - \sin \theta \\ +0 &amp; 1 &amp; 0 \\ +\sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; \sin \theta &amp; 0 \\ - \sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +, +\mathbf{R_{wc}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; - \sin \theta &amp; 0 \\ +\sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +$$</p> +<p>这里我们使用右手笛卡尔三维坐标系:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/14.png" alt="img" style="zoom: 25%;" /> +</div> +<p>旋转可分为<strong>主动旋转</strong>与<strong>被动旋转</strong>。<strong>主动旋转</strong>是指将向量逆时针围绕旋转轴所做出的旋转。<strong>被动旋转</strong>是对坐标轴本身进行的逆时针旋转,它相当于主动旋转的逆操作。关于右手笛卡尔坐标系的 $X, Y, Z$ 轴的旋转分别叫做<code>roll</code>,<code>pitch</code>和<code>yaw</code>旋转:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/15.png" alt="img" style="zoom: 20%;" /> +</div> +<p>因为逆时针和顺时针旋转会得到不一样的旋转矩阵,所以我们统一如下:</p> +<p>绕 $X$ 轴的主动旋转定义为($\theta_x$ 是<code>roll</code>角 ): +$$ +R(X_A, \theta_x) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta_x &amp; - \sin \theta_x \\ +0 &amp; \sin \theta_x &amp; \cos \theta_x +\end{bmatrix} = +\exp \left ( \theta_x +\begin{bmatrix} +0 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; -1\\ +0 &amp; 1 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Y$ 轴的主动旋转定义为($\theta_y$ 是<code>pitch</code>角): +$$ +R(Y_A, \theta_y) = +\begin{bmatrix} +\cos \theta_y &amp; 0 &amp; \sin \theta_y \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta_y &amp; 0 &amp; \cos \theta_y +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; 0 &amp; 1\\ +0 &amp; 0 &amp; 0\\ -1 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Z$ 轴的主动旋转定义为($\theta_z$ 是<code>yaw</code>角): +$$ +R(Z_A, \theta_z) = +\begin{bmatrix} +\cos \theta_z &amp; - \sin \theta_z &amp; 0 \\ +\sin \theta_z &amp; \cos \theta_z &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; -1 &amp; 0\\ +1 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +将上述三个旋转矩阵结合起来,最终的旋转矩阵(设绕 $X, Y, Z$ 轴旋转的角度分别为 $\alpha, \beta, \gamma$): +$$ +\begin{array}{ll} +M(\alpha, \beta, \gamma) &amp; = R_x(\alpha) R_y(\beta) R_z(\gamma) \\ +&amp; = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \alpha &amp; - \sin \alpha \\ +0 &amp; \sin \alpha &amp; \cos \alpha +\end{bmatrix} +\begin{bmatrix} +\cos \beta &amp; 0 &amp; \sin \beta \\ +0 &amp; 1 &amp; 0 \\ - \sin \beta &amp; 0 &amp; \cos \beta +\end{bmatrix} +\begin{bmatrix} +\cos \gamma &amp; -\sin \gamma &amp; 0 \\ +\sin \gamma &amp; \cos \gamma &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} \\ +&amp; = \begin{bmatrix} +\cos \gamma \cos \beta &amp; - \sin \gamma \cos \alpha + \cos \gamma \sin \beta \sin \alpha &amp; \sin \gamma \sin \alpha + \cos \gamma \sin \beta \cos \alpha \\ +\sin \gamma \cos \beta &amp; \cos \gamma \cos \alpha + \sin \gamma \sin \beta \sin \alpha &amp; - \cos \gamma \sin \alpha + \sin \gamma \sin \beta \cos \alpha \\ - \sin \beta &amp; \cos \beta \sin \alpha &amp; \cos \beta \cos \alpha +\end{bmatrix} +\end{array} +$$</p> +<p>此时我们再加上平移向量 $T$ 便可完成从世界坐标系到相机坐标系的这个刚体变换了:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c +\end{bmatrix} = +\begin{bmatrix} +r_{11} &amp; r_{12} &amp; r_{13} \\ +r_{21} &amp; r_{22} &amp; r_{23} \\ +r_{31} &amp; r_{32} &amp; r_{33} +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + +\begin{bmatrix} +t_x \\ +t_y \\ +t_z +\end{bmatrix} = +\mathbf{R} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + T +$$</p> +<p>可进一步写成如下形式:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0_3^T &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$\mathbf{R}$ 和 $\mathbf{T}$ 便是相机外参。</p> +<h3 id="相机内参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%86%85%e5%8f%82">#</a> + 相机内参 +</h3><p>首先考虑图像坐标系($xy$)和像素坐标系($uv$)之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/8.png" + width="369" + height="245" + srcset="https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="150" + data-flex-basis="361px" + +></p> +<p>$$ +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix}= +\begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix} +$$</p> +<p>$dx$ 表示一个像素点在 $x$ 方向的长度是多少毫米,$dy$ 表示一个像素点在 $y$ 方向的长度是多少毫米;$(u_0, v_0)$ 为图像的中心点。</p> +<p>然后考虑相机坐标系和图像坐标系之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/9.png" + width="353" + height="346" + srcset="https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="102" + data-flex-basis="244px" + +></p> +<p>$$ +\Delta ABO_c \sim \Delta oCO_c, \Delta PBO_c \sim \Delta pCO_c +$$ +$$ +\displaystyle \frac{AB}{oC} = \frac{AO_c}{oO_c} = \frac{PB}{p C} = \frac{X_c}{x} = \frac{Z_c}{f} = \frac{Y_c}{y} +$$ +$$ +x = f \displaystyle \frac{X_c}{Z_c}, y = f \frac{Y_c}{Z_c} +$$</p> +<p>$$ +Z_c \begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix}= +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$f$ 是焦距,结合外参我们最终可以得到世界坐标系和像素坐标系之间的映射关系:</p> +<p>$$ +\begin{array}{l} +\lambda \begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} &amp; = +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix}\\ +&amp; = +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +\end{array} +$$ +其中,相机内参为(不考虑图像传感器的特性): +$$ +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +$$</p> +<p>其中,$f_x, f_y$ 即为焦距的物理距离在像素坐标系中的长度,相机内参标定主要是标定相机的焦距、主点、歪斜等内部参数。</p> +<h2 id="可能存在的影响"> + <a href="#%e5%8f%af%e8%83%bd%e5%ad%98%e5%9c%a8%e7%9a%84%e5%bd%b1%e5%93%8d">#</a> + 可能存在的影响 +</h2><h3 id="主点偏移"> + <a href="#%e4%b8%bb%e7%82%b9%e5%81%8f%e7%a7%bb">#</a> + 主点偏移 +</h3><p>主点是光轴和相机成像平面的交点,在理想情况下,图像坐标系和相机坐标系原点重合,不存在坐标系偏移。但在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动:</p> +<div style="display: flex; justify-content: center;"> + <img src="assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png" alt="img" style="zoom: 67%;" /> +</div> +<p>考虑主点偏移后,图像坐标和3D在相机坐标系的关系为:</p> +<p>$$ +\begin{matrix} +u = f \frac{X}{Z} + O_x \\ +v = f \frac{X}{Z} + O_y +\end{matrix} +$$ +此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; 0 &amp; O_x &amp; 0 \\ +0 &amp; f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>仔细观察就会发现,该关系与上面提到的关系是等价的,只不过上面使用坐标 $(u_0, v_0)$ 来代表偏移量 $(O_x, O_y)$。</p> +<h3 id="图像传感器特征"> + <a href="#%e5%9b%be%e5%83%8f%e4%bc%a0%e6%84%9f%e5%99%a8%e7%89%b9%e5%be%81">#</a> + 图像传感器特征 +</h3><p>图像传感器像原尺寸在制造过程可能不是正方形,同时可能存在歪斜(skewed),因此需要考虑这些影响因素,传感器歪斜和不是正方形主要对相机 $x$ 和 $y$ 方向的焦距产生影响。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/v2-a812da34b739588fa9142c46839ad281_1440w.png" alt="img" style="zoom: 50%;" /> +</div> +<p>此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; s &amp; O_x &amp; 0 \\ +0 &amp; \eta f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = [K, 0_3] P +$$ +其中,$K$ 矩阵即为最终的内参矩阵。</p> +<h3 id="镜头畸变"> + <a href="#%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 镜头畸变 +</h3><p>小孔成像模型虽然充分考虑了相机内部参数对成像的影响,但没有考虑成像系统另一个重要的部分,镜头。镜头常用的有普通镜头、广角镜头、鱼眼镜头等,在无人驾驶和视觉slam领域,鱼眼镜头和广角镜头用的很多,主要是视角很大,可以观测到更多的信息。任何镜头都存在不同程度的畸变,不同类型的镜头用到的畸变模型也不相同。</p> +<p>在几何光学和阴极射线管(CRT)显示中,畸变(distortion)是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括<strong>径向畸变(radial distortion)<strong>和</strong>切向畸变(tangential distortion)</strong>。</p> +<p>径向畸变来自于透镜形状,主要是由于透镜不同部位放大倍率不同造成的。切向畸变来自于整个相机的组装过程,主要是由于透镜安装与成像平面不平行造成的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/10.png" + width="456" + height="300" + srcset="https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="152" + data-flex-basis="364px" + +></p> +<h4 id="径向畸变"> + <a href="#%e5%be%84%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 径向畸变 +</h4><p>透过镜头边缘的光线很容易产生径向畸变,这种现象来源于“筒形”或“鱼眼”的影响。光线离镜头中心越远,畸变越大。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png" + width="765" + height="248" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="308" + data-flex-basis="740px" + +></p> +<p>从图像可以看出,径向畸变以某一个中心往外延伸,且越往外,畸变越大;显然畸变与距离成一种非线性的变换关系,参考众多文献,可以用多项式来近似: +$$ +\begin{matrix} +x_{rcrt} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\\\ +y_{rcrt} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$k_1, k_2, k_3$ 是径向畸变系数,$r^2 = x^2 + y^2$。</p> +<h4 id="切向畸变"> + <a href="#%e5%88%87%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 切向畸变 +</h4><p>切向畸变主要发生在相机sensor和镜头不平行的情况下;因为有夹角,所以光透过镜头传到图像传感器上时,成像位置发生了变化。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/11.png" + width="926" + height="385" + srcset="https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="240" + data-flex-basis="577px" + +> +$$ +\begin{matrix} +x_{tcrt} = x + [2p_1 xy + p_2 (r^2 + 2 x^2)] \\\\ +y_{tcrt} = y + [2p_2 xy + p_1 (r^2 + 2 y^2)] +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$p_1, p_2$ 是切向畸变系数,$r^2 = x^2 + y^2$。</p> +<h3 id="消除镜头畸变"> + <a href="#%e6%b6%88%e9%99%a4%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 消除镜头畸变 +</h3><p>考虑镜头畸变前,我们可以将相机标定简单描述为以下过程:像素坐标 $(u_{ccd}, v_{ccd})$ $\to$ 图像坐标 $(x, y)$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<p>此时我们考虑加入镜头畸变: +$$ +\begin{matrix} +x_{crt} = x_{rcrt} + x_{tcrt} \\\\ +y_{crt} = y_{rcrt} + y_{tcrt} +\end{matrix} +$$ +得到消除镜头畸变的相机标定流程:像素坐标 $(u_{ccd - crt}, v_{ccd - crt})$ $\to$ 图像坐标 $(x_{crt}, y_{crt})$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<h2 id="标定板的作用"> + <a href="#%e6%a0%87%e5%ae%9a%e6%9d%bf%e7%9a%84%e4%bd%9c%e7%94%a8">#</a> + 标定板的作用 +</h2><h3 id="相机标定中的参数"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e4%b8%ad%e7%9a%84%e5%8f%82%e6%95%b0">#</a> + 相机标定中的参数 +</h3><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<p>针孔相机模型中,只要确定这9个参数就可以唯一的确定针孔相机模型:</p> +<p>$$ +f_x,f_y,O_x,O_y,k_1,k_2,k_3,p_1,p_2 +$$</p> +<p>这个过程就称为「相机标定」,其中前4个我们称为内参数,后5个称为畸变参数,畸变参数是为了补充内参的。所以一旦相机结构固定,包括镜头结构固定,对焦距离固定,我们就可以用这9个的参数去近似这个相机。这里说的「镜头结构固定」,按我个人的理解,除了焦距固定之外,也应当包含光圈固定,因为改变光圈的大小,除了景深之外,是有可能改变针孔相机模型中的光心位置,但是影响并不是很大。这意味着标定好的相机如果改变光圈大小,会使得标定误差变大但应该不会大到难以接受的地步。</p> +<p>对于针孔相机本身需要拟合的方程如下:</p> +<p>$$ +\begin{bmatrix} +u_{ccd - crt} * Z\\ +v_{ccd - crt} * Z\\ +Z +\end{bmatrix} = +J(k_1, k_2, k_3, p_1, p_2) +\begin{bmatrix} +f_x &amp; 0 &amp; O_x \\ +0 &amp; f_y &amp; O_y \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X \\ +Y \\ +X +\end{bmatrix} +$$</p> +<p>因此,我们现在的任务就是找出一大堆具有对应关系的像点 ${(u_{ccd - crt}, v_{ccd - crt}) ^T }$ 和物点 ${ (X, Y, Z)^T }$ 的点作为样本,来训练出模型的参数。这里就引发了两个问题:</p> +<ul> +<li>这么多像点和物点如何匹配?</li> +<li>即便现在知道物点的位置,如何用相机坐标系来表达物点的位置 $(X, Y, Z)$?</li> +</ul> +<p>为了解决上述问题,标定板应运而生。标定板的一大作用,确定物点和像点的对应性。这里用到的原理主要是「透视不变性」,打个比方,你近看一个人和远看一个人,虽然他的鼻子大小变了,你看鼻子的视角也变了,但是拓扑结构肯定是不变的,你也不可能把鼻子看成是嘴巴。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png" + width="803" + height="363" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="221" + data-flex-basis="530px" + +></p> +<p>所以在标定板中,印刷了拓扑结构,广泛应用的是棋盘格和圆点格,这两种之所以成为主流,不仅是因为它们的拓扑结构明确且均匀,更重要的是检测其拓扑结构的算法简单且有效。棋盘格检测的是角点,只要对拍摄到的棋盘格图像横纵两个方向计算梯度就可获得;而圆点格的检测只需要对拍摄到的圆点格图样计算质心即可。假如你开发了一套非常完美的检测人脸全部特征的算法,你完全可以用你的照片当作标定板。</p> +<p>按照我的经验,圆点格的效果应该是好于棋盘格,因为圆点质心的「透视不变性」要比棋盘格的角点稳定的多。下图是同样尺寸、同样比例棋盘格和圆点在最大重投影误差处的误差对比,红色十字是提取的角点/质心,绿色圆圈是针孔相机模型计算出来认为的角点/质心位置。</p> +<p>但是圆点格的检测似乎是Halcon的专利(存疑),因此OpenCV和Matlab标定工具箱用的是棋盘格,要用圆点格得要自己写算法。下文中提到的标定板说的都是棋盘格。</p> +<p>标定板的第二大作用是把标定板中的角点变换到相机坐标系下的坐标 $(X, Y, Z)$。对于标定的初学者来说,很容易忽略的一点是标定板是具有标定板坐标系的。换句话说,标定板中的每个角点,在标定板坐标系下的位置是确定并且是已知的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png" + width="447" + height="404" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="110" + data-flex-basis="265px" + +></p> +<p>而标定板坐标系变换到相机坐标系的变换矩阵,我们称它的元素为外参数。</p> +<h3 id="如何使用标定板"> + <a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%a0%87%e5%ae%9a%e6%9d%bf">#</a> + 如何使用标定板 +</h3><p>如果用OpenCV或Matlab标定工具箱进行标定,需要给出棋盘格的物理尺寸,这其实就是在建立标定板坐标系,从测量的角度讲,标定板的精度是相机标定精度的基准,是误差传递链上的第一个环节。所以为了使针孔相机模型更逼近真实相机,对标定板的质量有以下要求(按重要性顺序):</p> +<ul> +<li>标定板的平面度高,棋盘格是直角。</li> +<li>标定板每个格子尺寸的高一致性。</li> +<li>真实尺寸与标称尺寸的差异小。</li> +</ul> + + + + Test + https://3000ye.com/p/test/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/test/ + <img src="https://3000ye.com/p/test/nord.jpg" alt="Featured image of post Test" /><h1 id="test-page"> + <a href="#test-page">#</a> + Test page +</h1><h2 id="picture"> + <a href="#picture">#</a> + picture +</h2><p><img src="https://3000ye.com/p/test/nord.jpg" + width="5472" + height="2976" + srcset="https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg 480w, https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="nord" + + + class="gallery-image" + data-flex-grow="183" + data-flex-basis="441px" + +></p> +<h2 id="math"> + <a href="#math">#</a> + math +</h2><p>$$ +\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } +$$</p> +<h2 id="code"> + <a href="#code">#</a> + code +</h2><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;bits/stdc++.h&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello World!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="test-latex"> + <a href="#test-latex">#</a> + Test $\LaTeX$ +</h2><h2 id="frac23"> + <a href="#frac23">#</a> + $\frac{2}{3}$ +</h2> + + + Archives + https://3000ye.com/archives/ + Sun, 06 Mar 2022 00:00:00 +0000 + + https://3000ye.com/archives/ + + + + About + https://3000ye.com/about/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/about/ + + + + Friends + https://3000ye.com/friends/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/friends/ + + + + Search + https://3000ye.com/search/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/search/ + + + + Stars + https://3000ye.com/stars/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/stars/ + + + + + diff --git a/p/2023-summary/assets/03-16.jpg b/p/2023-summary/assets/03-16.jpg new file mode 100644 index 0000000..934f797 Binary files /dev/null and b/p/2023-summary/assets/03-16.jpg differ diff --git a/p/2023-summary/assets/05-10.jpg b/p/2023-summary/assets/05-10.jpg new file mode 100644 index 0000000..f662d22 Binary files /dev/null and b/p/2023-summary/assets/05-10.jpg differ diff --git a/p/2023-summary/assets/06-18.jpg b/p/2023-summary/assets/06-18.jpg new file mode 100644 index 0000000..4274917 Binary files /dev/null and b/p/2023-summary/assets/06-18.jpg differ diff --git a/p/2023-summary/assets/07-19.jpg b/p/2023-summary/assets/07-19.jpg new file mode 100644 index 0000000..d6a70d7 Binary files /dev/null and b/p/2023-summary/assets/07-19.jpg differ diff --git a/p/2023-summary/assets/07-20.jpg b/p/2023-summary/assets/07-20.jpg new file mode 100644 index 0000000..d42f2f6 Binary files /dev/null and b/p/2023-summary/assets/07-20.jpg differ diff --git a/p/2023-summary/assets/09-06.jpg b/p/2023-summary/assets/09-06.jpg new file mode 100644 index 0000000..fd182f7 Binary files /dev/null and b/p/2023-summary/assets/09-06.jpg differ diff --git a/p/2023-summary/assets/10-19.jpg b/p/2023-summary/assets/10-19.jpg new file mode 100644 index 0000000..d7c4acc Binary files /dev/null and b/p/2023-summary/assets/10-19.jpg differ diff --git a/p/2023-summary/assets/12-31.jpg b/p/2023-summary/assets/12-31.jpg new file mode 100644 index 0000000..fcb40c5 Binary files /dev/null and b/p/2023-summary/assets/12-31.jpg differ diff --git a/p/2023-summary/assets/7-20.jpg b/p/2023-summary/assets/7-20.jpg new file mode 100644 index 0000000..8ad5077 Binary files /dev/null and b/p/2023-summary/assets/7-20.jpg differ diff --git a/p/2023-summary/assets/wakatime.png b/p/2023-summary/assets/wakatime.png new file mode 100644 index 0000000..1b31696 Binary files /dev/null and b/p/2023-summary/assets/wakatime.png differ diff --git a/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_120x120_fill_box_smart1_3.png b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_120x120_fill_box_smart1_3.png new file mode 100644 index 0000000..a20b439 Binary files /dev/null and b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_120x120_fill_box_smart1_3.png differ diff --git a/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_1600x0_resize_box_3.png b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_1600x0_resize_box_3.png new file mode 100644 index 0000000..50a5b82 Binary files /dev/null and b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_1600x0_resize_box_3.png differ diff --git a/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_800x0_resize_box_3.png b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_800x0_resize_box_3.png new file mode 100644 index 0000000..0cf60cc Binary files /dev/null and b/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_800x0_resize_box_3.png differ diff --git a/p/2023-summary/index.html b/p/2023-summary/index.html new file mode 100644 index 0000000..8dc575c --- /dev/null +++ b/p/2023-summary/index.html @@ -0,0 +1,668 @@ + + + + +2023 Summary + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post 2023 Summary + + +
+ + +
+ + +
+

+ 2023 Summary +

+ + +

+ Wrap up 2023 with desktop changes +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 用桌面变化来总结 2023 +

+ # + 3月16日,我有 5 块屏幕 +

这段时间是我同时使用屏幕数量最多的时候,主屏幕写代码,副屏幕看网页;surface 和工控屏做直播推流;iPad 负责预览直播画面:

+
    +
  • 主屏幕(中间):27 寸 2k,副屏幕(最左):15.6 寸 2k
  • +
  • surface(最右),工控屏(最小):7 寸 1080p
  • +
  • iPad(非统一背景)
  • +
+

几乎每天都在宿舍,买了本《明解 C++》一边学语言一边学算法。虽然算法已经忘得差不多了,但坚实的 C++ 基础对我帮助很大,甚至我现在实习的主要内容就是 C++ 开发。

+
+img +
+

+ # + 5月10日,我有 2 块屏幕 +

学完了 C++ 语言基础后,我开始意识到数据结构对编程的重要性。同时也有为考研做准备的打算,我开始每天往返图书馆,刷王道考研的 CS408 课程。

+

为了方便往返图书馆并尽可能满足我多屏幕的需求,买了一台可以 180 度开合的笔记本(ThinkBook 14)和一个立式支架,搭配 ThinkPad 的 GaN 100W 充电器和雷电线,只需 2 根线完成所有的连接。这样的好处是可以上面写代码下面看课程,同时使用外接键盘不会很突兀。

+
+img +
+

+ # + 6月18日,我有 2 块屏幕 +

春季学期结束后,我也放弃了考研的打算,开始往机器学习和 Pytorch 的方向学习。翻出以前买的西瓜书和南瓜书,对着 Github 上的开源笔记边看边学。由于宿舍桌子太窄了,放三块屏幕过于拥挤,于是将 15.6 寸的副屏收了起来。

+

趁着 618 换了罗技的 master 3s 鼠标,手感确实比雷蛇的 click Pro 好很多。同时每天都坚持录视频,之前买的麦克风也派上了用场。

+
+img +
+

+ # + 7月19日,我有 1 块屏幕 +

随着 Pytorch 学习的深入,笔记本的核显已经无法满足需求。而此时矿潮已经开始逐渐褪去,随着 4090 的发布老显卡的价格开始走低,索性在咸鱼上淘了一张服务器版的 2080Ti:

+
    +
  • 300A 核心,三星显存
  • +
  • 纯铜涡轮散热,尾部供电
  • +
+

其 11G 的显存足以满足入门需求,同时后期还可以加焊 22G 显存,是一张性能和成长空间都不错的显卡。

+

同时为了最大限度使用显卡,直接把机器刷成了 Ubuntu server,将之前的监控屏用来监控显卡状态。

+
+img +
+

+ # + 7月20日,我有 3 块屏幕 +

搞深度学习,真的是屏幕越多越好,有太多的资料和数据集看不过来:

+
    +
  • 主显示器用来看资料
  • +
  • 副显示器用来看数据集(竖直摆放)
  • +
  • 笔记本屏幕用来写代码
  • +
+
+img +
+

+ # + 9月6日,我有 1 块屏幕 +

秋季学期重回宿舍后,开始迷上了 Minecraft 和泡茶,一杯茶一个种子就是一天。这段时间是最放松的时间,每天都在搭方块。

+
+img +
+

+ # + 10月19日,我有 3 块屏幕 +

开始实习后,第一次用上了 MacOS。但尴尬的是,工作用的编译和测试环境还是 Windows,所以基本上都是在 Mac 上面通过 SSH 远程 Windows 进行开发(VsCode 大法好):

+
    +
  • 开发环境:MacBookPro(左边)和主屏幕:28 寸 4k
  • +
  • 编译环境:dell Windows 主机(无屏幕)
  • +
  • 测试环境:hp | ThinkPad(右边)
  • +
+

同时,我也负责服务器上的容器部署等相关工作,所以我有幸同时使用 3 大主流操作系统。

+
+img +
+

+ # + 12月31日,我有 2 块屏幕 +

适应实习的节奏后,晚上回宿舍的几个小时是每天最舒服的,因为可以不用管公司的事情专注自己喜欢的项目。

+

趁着放假买了一块树莓派 4B 在宿舍折腾,初步搭了 Seafile 和 Gitlab。

+
+img +
+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch02/assets/c++primer.jpg b/p/c-primer-ch02/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch02/assets/c++primer.jpg differ diff --git a/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch02/index.html b/p/c-primer-ch02/index.html new file mode 100644 index 0000000..9135982 --- /dev/null +++ b/p/c-primer-ch02/index.html @@ -0,0 +1,1166 @@ + + + + +C++ Primer Ch02 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch02 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch02 +

+ + +

+ Variables and basic types +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 变量和基本类型 +

变量提供一个具名的、可供程序操作的存储空间。C++中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。

+

+ # + 变量的声明和定义 +

为了允许把程序拆分成多个逻辑部分来编写,C++语言支持分离式编译(separate complication) 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。

+

为了支持分离式编译,C++语言将声明和定义区分开来。声明(declaration) 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 定义(definition) 负责创建与名字关联的实体。

+

变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。

+
+ +
+
1
+2
+3
+4
+
+
extern int i;  // 声明
+int i;  // 定义
+int i = 3;  // 定义并赋值
+extern int i = 3;  // 定义而非声明,extern失效
+
+
+

注意: 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。

+

+ # + 标识符 +

C++标识符(identifier) 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。

+

+ # + 作用域 +

作用域(scope) 是程序的一部分,在其中名字有特定的含义,C++语言中大多数作用域都以花括号分隔。

+

作用域能彼此包含,被包含(嵌套)的作用域称为 内层作用域(inner scope),包含着别的作用域的作用域称为 外层作用域(outer scope)。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
#include "iostream"
+
+int outer = 42;  // 全局作用域
+
+int main() {
+    int inner = 12;  // 内层作用域
+    // 使用全局变量输出
+    std::cout << outer << " " << inner << std::endl;
+    
+    int outer = 0;  // 局部重新定义,覆盖全局变量
+    // 使用局部变量输出
+    std::cout << outer << " " << inner << std::endl;
+
+    // 显式访问全局变量
+    std::cout << ::outer << " " << inner << std::endl;
+    
+    return 0;
+}
+
+
+

+ # + 复合类型 +

复合类型(compound type) 是指基于其他类型定义的类型,主要介绍引用和指针。

+

+ # + 引用 +

引用(reference) 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 绑定(bind) 在一起,而不是将初始者拷贝给引用。

+
+

引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。

+
+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
#include "iostream"
+
+int main() {
+    int x = 3;  // 定义
+    int &y = x;  // 引用
+    int z = x;  // 定义
+
+    y = 3;  // 修改 y 的值,实际是修改 x 的值
+    std::cout << x << " " << y << std::endl;
+
+    return 0;
+}
+
+
+

+ # + 指针 +

指针(pointer) 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:

+
    +
  • 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
  • +
  • 指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。
  • +
+
+ +
+
1
+2
+
+
int *ip1, *ip2;  // ip1 和 ip2 都是指向 int 类型对象的指针
+double dp, *dp2;  // dp2 是指向 double 类型对象的指针,dp 是 double 类型对象
+
+
+

指针存放某个对象的地址,想获取该地址,需要使用 取地址符(&

+
+ +
+
1
+2
+
+
int val = 42;
+int *p = &val;  // p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针
+
+
+
+

因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。

+
+

指针的值(即地址)应属于下列 4 种状态之一:

+
    +
  • 指向一个对象。
  • +
  • 指向紧邻对象所占空间的下一个位置。
  • +
  • 空指针,意味着指针没有指向任何对象。
  • +
  • 无效指针,也就是上述情况之外的其他值。
  • +
+

如果指针指向了一个对象,则允许使用 解引用符(* 来访问该对象:

+
+ +
+
1
+2
+3
+4
+
+
int val = 42;
+int *p = &val;
+
+std::cout << *p << std::endl;  // 由符号 * 得到指针 p 所指的对象,输出 42
+
+
+
+

解引用操作仅适用于有效指针,无效指针无法解引用。

+
+

空指针(null pointer) 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。

+

生成空指针:

+
+ +
+
1
+2
+
+
int *p1 = nullptr;  // 等价于 int *p1 = 0
+int *p2 = 0;
+
+
+

指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。

+

指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
int i = 42;
+int *pi = 0;  // pi 为空指针
+int *pi2 = &i;  // pi2 存放 i 的地址
+int *pi3;  // pi3 的值无法确定
+
+pi3 = pi2;  // pi3 和 pi2 指向同一个对象 i
+pi2 = 0;  // pi2 变为空指针
+
+
+

+ # + const 限定符 +

有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字const对变量的类型加以限定:

+
+ +
+
1
+
+
const int bufferSize = 512;  // 限定缓冲区大小为 512
+
+
+

默认情况下,const对象被设定为仅在单个文件内有效。当多个文件中出现了同名的const变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上extern

+
+ +
+
1
+2
+3
+4
+5
+
+
// 定义文件 xxx.cpp
+extern const int bufferSize = 512;
+
+// 声明文件 xxx.hpp
+extern const int bufferSize;
+
+
+

+ # + 指针和 const +

与引用一样,也可以令指针指向常量与非常量。指向常量的指针(pointer to const) 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:

+
+ +
+
1
+2
+3
+4
+5
+
+
const int val = 42;  // val 为常量对像,其值不能改变
+
+int *pi = &val;  // 报错,pi 为普通指针,不能存放常量对象地址
+const int *pi2 = &val;  // 使用指向常量的指针存放常量对象地址
+*pi2 = 20;  // 报错,不能给指向常量的指针赋值
+
+
+

+ # + 处理类型 +

随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。

+

+ # + 类型别名 +

类型别名(type alias) 是一个名字,它是某种类型的同义词。

+

使用关键字typedef定义类型别名:

+
+ +
+
1
+
+
typedef long long ll;  // ll 是 long long 的同义词
+
+
+

使用 别名声明(alias declaration) 定义类型别名:

+
+ +
+
1
+
+
using ll = long long;  // ll 是 long long 的同义词
+
+
+

+ # + auto 类型说明符 +

编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此C++11引入了auto类型说明符,它能自动分析表达式结果的类型。

+
+ +
+
1
+
+
auto item = val1 + val2;  // item 初始化为 val1 和 val2 相加的结果
+
+
+

使用auto定义或声明多个变量时,所有变量的类型必须一致:

+
+ +
+
1
+2
+
+
auto x = 3, y = 5;
+auto x = 3, y = 4.2;  // 报错,同一行的变量类型必须一致
+
+
+

+ # + decltype 类型指示符 +

有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。

+

因此C++11引入了decltype类型指示符,它的作用是返回操作数的数据类型:

+
+ +
+
1
+2
+
+
int x = 5, y = 7;
+decltype(x + y) z = 6;  // z 为 int 类型
+
+
+

+ # + 自定义数据结构 +

内置的数据类型并不能满足所有的需求,因此c++提供了自定义数据类型的方式:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
struct student {
+    std::string name;
+    std::string sex;
+    int gender;
+};
+
+// 定义
+student jack{"jack", "m", 18};
+// 先声明后赋值
+student castor;
+castor.name = "castor", castor.sex = "m", castor.gender = 18;
+
+
+

+ # + 自定义数据结构使用别名 +

和内置数据类型一样,自定义数据结构也能使用别名:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
using stu = studeng;
+
+// 或直接在定义时使用别名
+using stu = struct student {
+    std::string name;
+    std::string sex;
+    int gender;
+};
+
+
+

+ # + 编写自己的头文件 +

当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+
+
// studeng.h
+#ifndef STUDENT_H
+#define STUDENT_H
+
+#include "string"
+
+struct student {
+    std::string name;
+    std::string sex;
+    int gender;
+};
+
+#endif
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch03/assets/c++primer.jpg b/p/c-primer-ch03/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer.jpg differ diff --git a/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch03/index.html b/p/c-primer-ch03/index.html new file mode 100644 index 0000000..f7b3704 --- /dev/null +++ b/p/c-primer-ch03/index.html @@ -0,0 +1,1374 @@ + + + + +C++ Primer Ch03 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch03 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch03 +

+ + +

+ Strings, vectors and arrays +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 字符串、向量和数组 +

+ # + 命名空间的 using 声明 +

我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用using进行声明:

+
+ +
+
1
+2
+3
+
+
using std::cout;  // 单独使用某个函数
+
+using namespace std;  // 批量声明 std 中所有函数
+
+
+
+

头文件中不应该包含 using 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。

+
+

+ # + 标准库类型 string +

标准库类型 string 表示可变长的字符序列,使用 string 类型必须首先包含 string 头文件:

+
+ +
+
1
+2
+3
+
+
#include <string>
+
+using std::string
+
+
+

+ # + 定义和初始化 string 对象 +

初始化 string 对象的方式:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
方式解释
string s1默认初始化,s1是个空字符串
string s2(s1)s2s1的副本
string s2 = s1等价于s2(s1)s2s1的副本
string s3("value")s3是字面值“value”的副本,除了字面值最后的那个空字符外
string s3 = "value"等价于s3("value")s3是字面值"value"的副本
string s4(n, 'c')s4初始化为由连续n个字符c组成的串
+

拷贝初始化(copy initialization):使用 = 将一个已有的对象拷贝到正在创建的对象。

+

直接初始化(direct initialization):通过括号给对象赋值。

+

+ # + string 对象的操作 +

string的操作:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
os << ss写到输出流os当中,返回os
is >> sis中读取字符串赋给s,字符串以空白分割,返回is
getline(is, s)is中读取一行赋给s,返回is
s.empty()s为空返回true,否则返回false
s.size()返回s中字符的个数
s[n]返回s中第n个字符的引用,位置n从0计起
s1+s2返回s1s2连接后的结果
s1=s2s2的副本代替s1中原来的字符
s1==s2如果s1s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感
s1!=s2同上
<, <=, >, >=利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)
+

读取 string 对象:

+
    +
  • 使用 IO 操作符 >> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。
  • +
  • 使用 getline() 函数读取:将一整行读取为 string 对象,包括空白。
  • +
+

s.size() 返回 string::size_type 类型,是 无符号 类型的值,不能和 int 混用。

+

s1 + s2 使用时,必须保证至少其中一个为 string 类型。例如:string s = "hello" + "world" 错误,其 + 两边都为字符串字面值。

+

字符串字面值string 是不同的类型。

+

+ # + 处理 string 对象中的字符 +

C++ 修改了 c 的标准库 ctype.hcctype,其中定义了一组标准函数:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
函数解释
isalnum(c)c是字母或数字时为真
isalpha(c)c是字母时为真
iscntrl(c)c是控制字符时为真
isdigit(c)c是数字时为真
isgraph(c)c不是空格但可以打印时为真
islower(c)c是小写字母时为真
isprint(c)c是可打印字符时为真
ispunct(c)c是标点符号时为真
isspace(c)c是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)
isupper(c)c是大写字母时为真
isxdigit(c)c是十六进制数字时为真
tolower(c)c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c)c是小写字母,输出对应的大写字母;否则原样输出c
+

遍历字符串:

+
+ +
+
1
+2
+3
+
+
for (auto c : str) {
+    ...
+}
+
+
+

str[idx] 中的 idxstring::size_type 类型,如果使用 int 会隐式转换为该类型。

+

+ # + 标准库类型 vector +

标准库类型 vector 表示对象的集合,其中给所有对象的类型都相同。因为 vector 容纳着其他对象,所以称其为 容器(container),使用 vector 必须包含其头文件:

+
+ +
+
1
+2
+3
+
+
#include <vector>
+
+using std::vector
+
+
+

vector 同时也是 类模板(class template),模板本身不是类或函数,但可以使用模板创建类,这个过程称为 实例化(instantiation)

+

当使用模板时,需要指出编译器应把类或函数实例化成何种类型:

+
+ +
+
1
+2
+
+
vector<int> ls;  // ls 保存 int 类型的对象
+vector<vector<string>> files;  // 该向量中的元素是 vector 对象
+
+
+
+

vector 是模板,vector<int> 是类型。

+
+

+ # + 定义和初始化 vector 对象 +

初始化vector对象的方法:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
方法解释
vector<T> v1v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector<T> v2(v1)v2中包含有v1所有元素的副本
vector<T> v2 = v1等价于v2(v1)v2中包含v1所有元素的副本
vector<T> v3(n, val)v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n)v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a, b, c...}v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a, b, c...}等价于v5{a, b, c...}
+

+ # + vector 对象的操作: +

vector支持的操作:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
v.emtpy()如果v不含有任何元素,返回真;否则返回假
v.size()返回v中元素的个数
v.push_back(t)v的尾端添加一个值为t的元素
v[n]返回v中第n个位置上元素的引用
v1 = v2v2中的元素拷贝替换v1中的元素
v1 = {a,b,c...}用列表中元素的拷贝替换v1中的元素
v1 == v2v1v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同
v1 != v2同上
<,<=,>, >=以字典顺序进行比较
+

+ # + 迭代器介绍 +

除了下标运算符外,迭代器(iterator) 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。

+

+ # + 使用迭代器 +

拥有迭代器的类型都具有 beginend 成员,其中 begin 成员返回指向第一个元素的迭代器:

+
+ +
+
1
+2
+
+
vector<int> ls{1, 2, 3};
+auto b = v.begin(), e = v.end();  // b 和 e 类型相同
+
+
+

end 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 end 指向容器的 尾后(off the end) 元素。这样的迭代器通常没有意义,只是作为标记,被称为 尾后迭代器(off-the-end iterator)尾迭代器(end iterator)

+
+

若容器为空,则 beginend 都返回尾后迭代器。

+
+

标准容器迭代器的运算符:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
运算符解释
*iter返回迭代器iter所指向的元素的引用
iter->mem等价于(*iter).mem
++iteriter指示容器中的下一个元素
--iteriter指示容器中的上一个元素
iter1 == iter2判断两个迭代器是否相等
+
+

泛型编程:尽量使用 != 来对迭代器进行判断

+
+

迭代器也拥有自己的类型:

+
+ +
+
1
+2
+
+
vector<int>::iterator it;  // it 是 vector<int> 类型的迭代器,可以读写元素
+vector<int>::const_iterator it2;  // it2 只能读,不能写
+
+
+

如果容器中的值为常量,则 beginend 返回 const_iterator,否则返回 iterator

+

解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
vector<string> ls{"str1", "str2"};
+
+auto it = ls.begin();
+string s = *it;  // s 为 "str1"
+bool flag = (*it).empty();  // 解引用访问 string 成员
+bool flag = it->empty();  // 作用同上
+
+
+

+ # + 迭代器运算 +

stringvector 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 迭代器运算(iterator arithmetic)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
运算符解释
iter + n迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。
iter - n迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。
iter1 += n迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1
iter1 -= n迭代器减法的复合赋值语句,将iter2减n的加过赋给iter1
iter1 - iter2两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。
>>=<<=迭代器的关系运算符,如果某迭代器
+

当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 difference_type 类型,是带符号整数型。

+

+ # + 数组 +

数组可以看做 vector 的低配版,其 长度固定

+

+ # + 定义和初始化内置数组 +

数组的声明和定义形如 a[d],其中 a 是数组的名字,d 是数组的维度(大于 0):

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
int cnt = 42;  // 非常量
+const int cnt2 = 42;  // 常量
+
+int arr[10]; // 含有 10 个整数的数组
+int *arr2[10];  // 含有 10 个整型指针的数组
+int arr3[cnt];  // 报错,cnt 非常量
+int arr4[cnt2];  // 含有 42 和整数的数组
+int arr5[] = {1, 2, 3};  // 自动计算长度的数组
+
+
+

字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:

+
+ +
+
1
+2
+
+
char arr[] = "hello";
+char arr2[5] = "hello";  // 报错,长度不够
+
+
+

不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:

+
+ +
+
1
+2
+3
+
+
int a[] = {1, 2, 3};
+int a2[] = a;  // 报错,不能用数组来初始化数组
+a2 = a;  // 报错,不能用数组进行赋值
+
+
+

+ # + 访问数组元素 +

数组的下标为 size_t 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。

+
+

下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。

+
+

+ # + 指针和数组 +

使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:

+
+ +
+
1
+2
+3
+
+
int ls[] = {1, 2, 3}
+int *p = &ls[0];  // 数组的元素的指针
+int *p2 = ls;  // 等价于 *p2 = &ls[0]
+
+
+

+ # + C 风格字符串 +

字符串字面值是一种通用结构的实例,这种结构是 C++C 继承而来的 C 风格字符串(C-style character string) 。 +按此习惯书写的字符串存放在字符数组中并以 空字符结束(null terminated)

+
+

C++ 程序中尽量不要使用 C 风格字符串,容易引起安全漏洞且不方便。

+
+

C标准库String函数,定义在<cstring> 中:

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
函数介绍
strlen(p)返回p的长度,空字符不计算在内
strcmp(p1, p2)比较p1p2的相等性。如果p1==p2,返回0;如果p1>p2,返回一个正值;如果p1<p2,返回一个负值。
strcat(p1, p2)p2附加到p1之后,返回p1
strcpy(p1, p2)p2拷贝给p1,返回p1
+

+ # + 多维数组 +

严格来说,C++ 语言中没有多维数组,所谓的多维数组实际是数组的数组。

+
+ +
+
1
+
+
int arr[10][20] = {0};  // 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0
+
+
+

+ # + 使用范围 for 语句处理多维数组 +

+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
size_t cnt = 0;
+
+for (auto &row: arr) {
+    for (auto &col: row) {
+        col = cnt; cnt ++;
+    }
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch04/assets/c++primer.jpg b/p/c-primer-ch04/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer.jpg differ diff --git a/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch04/index.html b/p/c-primer-ch04/index.html new file mode 100644 index 0000000..cb00274 --- /dev/null +++ b/p/c-primer-ch04/index.html @@ -0,0 +1,771 @@ + + + + +C++ Primer Ch04 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch04 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch04 +

+ + +

+ Expression +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 表达式 +

表达式由一个或多个 运算对象(operand) 组成对表达式求值将得到一个 结果(result)

+

+ # + 基础 +

+ # + 基本概念 +

C++ 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。

+

对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。

+

在表达式求值过程中,小整数类型(boolcharshort)等通常会被 提升(promoted) 成较大的整数类型(int)。

+

当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 重载运算符(overloaded operator)

+

C++ 的表达式要不然是 右值(rvalue),要不然就是 左值(lvalue),这两个名词是从 C 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。

+

+ # + 求值顺序 +

在大多数情况下,表达式求值的顺序是没有明确指定的:

+
+ +
+
1
+
+
int i = f1() * f2()
+
+
+

我们知道 f1f2 一定会在执行乘法之前被调用,但是无法知道是 f1 先被调用还是 f2 先被调用。对于没有指定调用顺序的程序来说,如果 f1f2 同时修改了同一个对象,将会引发错误并产生未定义的行为。

+

+ # + 算术运算符 +

溢出:当计算的结果超出该类型所能表示的范围时就会产生溢出。

+

bool 类型不应该参与计算

+

取余运算m % n 的结果的符号与 m 相同。

+

+ # + 逻辑运算符 +

短路求值:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。

+

+ # + 赋值运算符 +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch06/assets/c++primer.jpg b/p/c-primer-ch06/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer.jpg differ diff --git a/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch06/index.html b/p/c-primer-ch06/index.html new file mode 100644 index 0000000..09c7137 --- /dev/null +++ b/p/c-primer-ch06/index.html @@ -0,0 +1,792 @@ + + + + +C++ Primer Ch06 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch06 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch06 +

+ + +

+ Function +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 函数 +

函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。

+

+ # + 函数基础 +

一个典型的 函数(function) 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 形参(parameter) 组成的列表以及函数体。

+

我们通过 调用运算符(call operator) 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 实参(argument) 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。

+

编写函数:例如编写一个求 n 的阶乘的函数:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
int fact(int n) {
+    int res = 1;
+    while (n > 1) {
+        res *= n --;
+    }
+
+    return res;
+}
+
+
+

调用函数:要调用 fact 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
int main() {
+    int x = fact(5);
+    cout << "5! = " << x << endl;
+
+    return 0;
+}
+
+
+

函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,主调函数(calling funciton) 的执行暂停,被调函数(called funciton) 开始执行。

+

+ # + 局部对象 +

C++ 语言中,名字有作用域,对象有 生命周期(lifetime)

+
    +
  • 名字的作用域是程序文本的一部分,名字在其中可见。
  • +
  • 对象的生命周期是程序执行过程中该对象存在的一段时间。
  • +
+

在函数体内,形参和内部定义的变量统称为 局部变量(local variable),它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。

+

局部静态对象:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 static 类型从而获得这样的对象,局部静态对象(local static object) 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

+

+ # + 参数传递 +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch07/assets/c++primer.jpg b/p/c-primer-ch07/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer.jpg differ diff --git a/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch07/index.html b/p/c-primer-ch07/index.html new file mode 100644 index 0000000..f5b882d --- /dev/null +++ b/p/c-primer-ch07/index.html @@ -0,0 +1,779 @@ + + + + +C++ Primer Ch07 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch07 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch07 +

+ + +

+ Class +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 类 +

类的基本思想是 数据抽象(data abstraction)封装(encapsulation)。数据抽象是一种依赖于 接口(interface)实现(implementation) 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。

+

封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。

+

类要想实现数据抽象和封装,首先需要定义一个 抽象数据类型(abstract data type)

+

+ # + 定义抽象数据类型 +

+ # + 访问控制与封装 +

我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 C++ 中使用 访问说明符(access specifiers) 加强类的封装:

+
    +
  • 定义在 public 说明符之后的成员,在整个程序内都可被访问。
  • +
  • 定义在 private 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。
  • +
+

定义新的 Sales_data 类:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+
+
class Sales_data {
+  private:
+    std::string bookNo;
+    unsigned units_sold = 0;
+    double revenue = 0.0;
+
+    double avg_price() const { return units_sold ? revenue / units_sold : 0; }
+
+  public:
+    Sales_data() = default;
+    Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p * n) {}
+    Sales_data(const std::string &s): bookNo(s) {}
+    Sales_data(std::istream&);
+    std::string isbn() const { return bookNo; };
+    Sales_data &combine(const Sales_data&);
+};
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch08/assets/c++primer.jpg b/p/c-primer-ch08/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer.jpg differ diff --git a/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch08/index.html b/p/c-primer-ch08/index.html new file mode 100644 index 0000000..befb774 --- /dev/null +++ b/p/c-primer-ch08/index.html @@ -0,0 +1,714 @@ + + + + +C++ Primer Ch08 + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + Featured image of post C++ Primer Ch08 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch08 +

+ + +

+ IO library +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + IO 库 +

我们的程序已经使用了很多 IO 库设施:

+
    +
  • istream (输入流)类型,提供输入操作。
  • +
  • ostream (输出流)类型,提供输出操作。
  • +
  • cin,一个 istream 对象,从标准输入读取数据。
  • +
  • cout,一个 ostream 对象,想标准输出写入数据。
  • +
  • cerr,一个 ostream 对象,通常用于输出程序错误信息,写入到标准错误。
  • +
  • >> 运算符,用来从一个 istream 对象中读取输入数据。
  • +
  • << 运算符,用来向一个 ostream 对象中写入输出数据。
  • +
  • getline 函数,从一个给定的 istream 对象中读取一行数据,存入到一个给定的 string 对象中。
  • +
+

+ # + IO 库 +

IO 类型和对象一般都是操纵 char 数据的,但有些使用需要对文件、string 进行操作,因此分别定义了三个头文件:

+
    +
  • iostream 头文件:从标准流中读写数据,istreamostream 等。
  • +
  • fstream 头文件:从文件中读写数据,ifstreamofstream 等。
  • +
  • sstream 头文件:从字符串中读写数据,istringstreamostringstream 等。
  • +
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg b/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d.jpg differ diff --git a/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg b/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..7a0880f Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer.7630d75c87dffb5ed3e54c23dd076a9d_hu7c71e19f9023f7a97b706cc8104fd581_19508_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch09/assets/c++primer.jpg b/p/c-primer-ch09/assets/c++primer.jpg new file mode 100644 index 0000000..6c1a9c0 Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer.jpg differ diff --git a/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4818583 Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..8ed7c5f Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_1600x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..2d0a3e3 Binary files /dev/null and b/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_800x0_resize_q75_box.jpg differ diff --git a/p/c-primer-ch09/index.html b/p/c-primer-ch09/index.html new file mode 100644 index 0000000..5452d52 --- /dev/null +++ b/p/c-primer-ch09/index.html @@ -0,0 +1,1216 @@ + + + + +C++ Primer Ch09 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post C++ Primer Ch09 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch09 +

+ + +

+ Sequence Container +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 顺序容器 +

一个容器就是一些特定类型对象的集合,顺序容器(sequence container) 为程序员提供了控制元素存储和访问顺序的能力。

+

+ # + 顺序容器概述 +

下面列出了标准库中的顺序容器,不同容器有不同的性能折中:

+
    +
  • 想容器添加或从容器删除元素的代价。
  • +
  • 非顺序访问容器中元素的代价。
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
容器类型介绍
vector可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。
deque双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。
list双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。
forward_list单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。
array固定大小数组。支持快速随机访问。不能添加或者删除元素。
stringvector相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。
+
    +
  • 除了固定大小的 array 外,其他容器都提供高效、灵活的内存管理。
  • +
  • 通常使用 vector 是最好的选择,除非你有很好的理由选择其他容器。
  • +
  • 如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 listforward_list
  • +
  • 如果程序要求随记访问元素,应使用 vectordeque
  • +
  • 如果程序要求在容器的中间插入或删除元素,应使用 listforward_list
  • +
  • 如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 deque
  • +
+

+ # + 容器库概念 +

容器类型操作上形成了一种层次:

+
    +
  • 某些操作是所有容器类型都提供的。
  • +
  • 另一些操作仅针对顺序容器、关联容器或无序容器。
  • +
+

+ # + 类型 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
iterator此容器类型的迭代器类型
const_iterator可以读取元素但不能修改元素的迭代器类型
size_type无符号整数类型,足够保存此种容器类型最大可能的大小
difference_type带符号整数类型,足够保存两个迭代器之间的距离
value_type元素类型
reference元素的左值类型;和value_type &含义相同
const_reference元素的const左值类型,即const value_type &
+

+ # + 构造函数 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
C c;默认构造函数,构造空容器
C c1(c2);C c1 = c2;构造c2的拷贝c1
C c(b, e)构造c,将迭代器be指定范围内的所有元素拷贝到c
C c(a, b, c...)列表初始化c
C c(n)只支持顺序容器,且不包括array,包含n个元素,这些元素进行了值初始化
C c(n, t)包含n个初始值为t的元素
+
    +
  • 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
  • +
  • array具有固定大小。
  • +
  • 和其他容器不同,默认构造的array是非空的。
  • +
  • 直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。
  • +
  • 使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。
  • +
+

+ # + 赋值和swap +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
c1 = c2;c1中的元素替换成c2中的元素
c1 = {a, b, c...}c1中的元素替换成列表中的元素(不适用于array
c1.swap(c2)交换c1c2的元素
swap(c1, c2)等价于c1.swap(c2)
c.assign(b, e)c中的元素替换成迭代器be表示范围中的元素,be不能指向c中的元素
c.assign(il)c中的元素替换成初始化列表il中的元素
c.assign(n, r)c中的元素替换为n个值是t的元素
+
    +
  • 使用非成员版本的swap是一个好习惯。
  • +
  • assign操作不适用于关联容器和array
  • +
+

+ # + 大小 +

+ + + + + + + + + + + + + + + + + + + + +
操作解释
c.size()c中元素的数目(不支持forward_list
c.max_size()c中可保存的最大元素数目
c.empty()c中存储了元素,返回false,否则返回true
+

+ # + 添加元素 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
c.push_back(t)c尾部创建一个值为t的元素,返回void
c.emplace_back(args)同上
c.push_front(t)c头部创建一个值为t的元素,返回void
c.emplace_front(args)同上
c.insert(p, t)在迭代器p指向的元素之前创建一个值是t的元素,返回指向新元素的迭代器
c.emplace(p, args)同上
c.insert(p, n, t)在迭代器p指向的元素之前插入n个值为t的元素,返回指向第一个新元素的迭代器;如果n是0,则返回p
c.insert(p, b, e)将迭代器be范围内的元素,插入到p指向的元素之前;如果范围为空,则返回p
c.insert(p, il)il是一个花括号包围中的元素值列表,将其插入到p指向的元素之前;如果il是空,则返回p
+
    +
  • 因为这些操作会改变大小,因此不适用于array
  • +
  • forward_list有自己专有版本的insertemplace
  • +
  • forward_list不支持push_backemplace_back
  • +
  • 当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。
  • +
  • emplace开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。
  • +
  • 传递给emplace的参数必须和元素类型的构造函数相匹配。
  • +
+

+ # + 访问元素 +

+ + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
c.back()返回c中尾元素的引用。若c为空,函数行为未定义
c.front()返回c中头元素的引用。若c为空,函数行为未定义
c[n]返回c中下标是n的元素的引用,n时候一个无符号证书。若n>=c.size(),则函数行为未定义
c.at(n)返回下标为n的元素引用。如果下标越界,则抛出out_of_range异常
+
    +
  • 访问成员函数返回的是引用。
  • +
  • at和下标操作只适用于stringvectordequearray
  • +
  • back不适用于forward_list
  • +
  • 如果希望下标是合法的,可以使用at函数。
  • +
+

+ # + 删除元素 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
c.pop_back()删除c中尾元素,若c为空,则函数行为未定义。函数返回void
c.pop_front()删除c中首元素,若c为空,则函数行为未定义。函数返回void
c.erase(p)删除迭代器p指向的元素,返回一个指向被删除元素之后的元素的迭代器,若p本身是尾后迭代器,则函数行为未定义
c.erase(b, e)删除迭代器be范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则返回尾后迭代器
c.clear()删除c中所有元素,返回void
+
    +
  • 会改变容器大小,不适用于array
  • +
  • forward_list有特殊版本的erase
  • +
  • forward_list不支持pop_back
  • +
  • vectorstring不支持pop_front
  • +
+

+ # + 特殊的 forwad_list操作 +

    +
  • 链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。
  • +
  • forward_list定义了before_begin,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
lst.before_begin()返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。
lst.cbefore_begin()同上,但是返回的是常量迭代器。
lst.insert_after(p, t)在迭代器p之后插入元素。t是一个对象
lst.insert_after(p, n, t)在迭代器p之后插入元素。t是一个对象,n是数量。若n是0则函数行为未定义
lst.insert_after(p, b, e)在迭代器p之后插入元素。由迭代器be指定范围。
lst.insert_after(p, il)在迭代器p之后插入元素。由il指定初始化列表。
emplace_after(p, args)使用argsp之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义。
lst.erase_after(p)删除p指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义。
lst.erase_after(b, e)类似上面,删除对象换成从be指定的范围。
+

+ # + 改变容器大小 +

+ + + + + + + + + + + + + + + + +
操作解释
c.resize(n)调整c的大小为n个元素,若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化
c.resize(n, t)调整c的大小为n个元素,任何新添加的元素都初始化为值t
+

+ # + 获取迭代器 +

+ + + + + + + + + + + + + + + + +
操作解释
c.begin(), c.end()返回指向c的首元素和尾元素之后位置的迭代器
c.cbegin(), c.cend()返回const_iterator
+
    +
  • c开头的版本是C++11新标准引入的
  • +
  • 当不需要写访问时,应该使用cbegincend
  • +
+

+ # + 反向容器的额外成员 +

+ + + + + + + + + + + + + + + + + + + + + + + + +
操作解释
reverse_iterator按逆序寻址元素的迭代器
const_reverse_iterator不能修改元素的逆序迭代器
c.rbegin(), c.rend()返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(), c.crend()返回const_reverse_iterator
+
    +
  • 不支持forward_list
  • +
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/camera-calibration/assets/1.png b/p/camera-calibration/assets/1.png new file mode 100644 index 0000000..69283b9 Binary files /dev/null and b/p/camera-calibration/assets/1.png differ diff --git a/p/camera-calibration/assets/10.png b/p/camera-calibration/assets/10.png new file mode 100644 index 0000000..5096603 Binary files /dev/null and b/p/camera-calibration/assets/10.png differ diff --git a/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png b/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png new file mode 100644 index 0000000..3815478 Binary files /dev/null and b/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png b/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png new file mode 100644 index 0000000..c25b08e Binary files /dev/null and b/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/11.png b/p/camera-calibration/assets/11.png new file mode 100644 index 0000000..8e3d986 Binary files /dev/null and b/p/camera-calibration/assets/11.png differ diff --git a/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png b/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png new file mode 100644 index 0000000..746b7b9 Binary files /dev/null and b/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png b/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png new file mode 100644 index 0000000..054f0ef Binary files /dev/null and b/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/14.png b/p/camera-calibration/assets/14.png new file mode 100644 index 0000000..635dcb0 Binary files /dev/null and b/p/camera-calibration/assets/14.png differ diff --git a/p/camera-calibration/assets/15.png b/p/camera-calibration/assets/15.png new file mode 100644 index 0000000..1a65918 Binary files /dev/null and b/p/camera-calibration/assets/15.png differ diff --git a/p/camera-calibration/assets/2.png b/p/camera-calibration/assets/2.png new file mode 100644 index 0000000..63fb622 Binary files /dev/null and b/p/camera-calibration/assets/2.png differ diff --git a/p/camera-calibration/assets/3.png b/p/camera-calibration/assets/3.png new file mode 100644 index 0000000..75fad66 Binary files /dev/null and b/p/camera-calibration/assets/3.png differ diff --git a/p/camera-calibration/assets/4.png b/p/camera-calibration/assets/4.png new file mode 100644 index 0000000..a87a1b5 Binary files /dev/null and b/p/camera-calibration/assets/4.png differ diff --git a/p/camera-calibration/assets/5.png b/p/camera-calibration/assets/5.png new file mode 100644 index 0000000..1495084 Binary files /dev/null and b/p/camera-calibration/assets/5.png differ diff --git a/p/camera-calibration/assets/6.png b/p/camera-calibration/assets/6.png new file mode 100644 index 0000000..aa08439 Binary files /dev/null and b/p/camera-calibration/assets/6.png differ diff --git a/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png b/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png new file mode 100644 index 0000000..e7667c4 Binary files /dev/null and b/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png b/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png new file mode 100644 index 0000000..96b246d Binary files /dev/null and b/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/7.png b/p/camera-calibration/assets/7.png new file mode 100644 index 0000000..e58323f Binary files /dev/null and b/p/camera-calibration/assets/7.png differ diff --git a/p/camera-calibration/assets/8.png b/p/camera-calibration/assets/8.png new file mode 100644 index 0000000..7f8b129 Binary files /dev/null and b/p/camera-calibration/assets/8.png differ diff --git a/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png b/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png new file mode 100644 index 0000000..0501146 Binary files /dev/null and b/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png b/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png new file mode 100644 index 0000000..d271850 Binary files /dev/null and b/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/9.png b/p/camera-calibration/assets/9.png new file mode 100644 index 0000000..eb8a423 Binary files /dev/null and b/p/camera-calibration/assets/9.png differ diff --git a/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png b/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png new file mode 100644 index 0000000..0594692 Binary files /dev/null and b/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png b/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png new file mode 100644 index 0000000..6a3905d Binary files /dev/null and b/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/image-20230922105257485.png b/p/camera-calibration/assets/image-20230922105257485.png new file mode 100644 index 0000000..e368198 Binary files /dev/null and b/p/camera-calibration/assets/image-20230922105257485.png differ diff --git a/p/camera-calibration/assets/image-20230922105324203.png b/p/camera-calibration/assets/image-20230922105324203.png new file mode 100644 index 0000000..e368198 Binary files /dev/null and b/p/camera-calibration/assets/image-20230922105324203.png differ diff --git a/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png new file mode 100644 index 0000000..c9f91f7 Binary files /dev/null and b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png differ diff --git a/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png new file mode 100644 index 0000000..0e1dfdb Binary files /dev/null and b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png new file mode 100644 index 0000000..74a370f Binary files /dev/null and b/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-a812da34b739588fa9142c46839ad281_1440w.png b/p/camera-calibration/assets/v2-a812da34b739588fa9142c46839ad281_1440w.png new file mode 100644 index 0000000..80800c1 Binary files /dev/null and b/p/camera-calibration/assets/v2-a812da34b739588fa9142c46839ad281_1440w.png differ diff --git a/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp new file mode 100644 index 0000000..0bc20ee Binary files /dev/null and b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp differ diff --git a/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp new file mode 100644 index 0000000..f9e1a96 Binary files /dev/null and b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp differ diff --git a/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp new file mode 100644 index 0000000..d6fc360 Binary files /dev/null and b/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp differ diff --git a/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png new file mode 100644 index 0000000..d59828d Binary files /dev/null and b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png differ diff --git a/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png new file mode 100644 index 0000000..fe5c143 Binary files /dev/null and b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png new file mode 100644 index 0000000..4703423 Binary files /dev/null and b/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png b/p/camera-calibration/assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png new file mode 100644 index 0000000..4bb97ac Binary files /dev/null and b/p/camera-calibration/assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png differ diff --git a/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png new file mode 100644 index 0000000..df6c36c Binary files /dev/null and b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png differ diff --git a/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png new file mode 100644 index 0000000..8636270 Binary files /dev/null and b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png new file mode 100644 index 0000000..8baf592 Binary files /dev/null and b/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png differ diff --git a/p/camera-calibration/assets/v2-f06178480b426e54a55cc8ee606116b7_720w.png b/p/camera-calibration/assets/v2-f06178480b426e54a55cc8ee606116b7_720w.png new file mode 100644 index 0000000..2544644 Binary files /dev/null and b/p/camera-calibration/assets/v2-f06178480b426e54a55cc8ee606116b7_720w.png differ diff --git "a/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio.png" "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio.png" new file mode 100644 index 0000000..9359a37 Binary files /dev/null and "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio.png" differ diff --git "a/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png" "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png" new file mode 100644 index 0000000..1372bad Binary files /dev/null and "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png" differ diff --git "a/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png" "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png" new file mode 100644 index 0000000..de308a8 Binary files /dev/null and "b/p/camera-calibration/assets/\347\233\270\346\234\272\346\240\207\345\256\232.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png" differ diff --git a/p/camera-calibration/board.jpg b/p/camera-calibration/board.jpg new file mode 100644 index 0000000..d59828d Binary files /dev/null and b/p/camera-calibration/board.jpg differ diff --git a/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_120x120_fill_q75_box_smart1.jpg b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..30b8892 Binary files /dev/null and b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_1600x0_resize_q75_box.jpg b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..715e6a8 Binary files /dev/null and b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_1600x0_resize_q75_box.jpg differ diff --git a/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_800x0_resize_q75_box.jpg b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..7c39e08 Binary files /dev/null and b/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_800x0_resize_q75_box.jpg differ diff --git a/p/camera-calibration/index.html b/p/camera-calibration/index.html new file mode 100644 index 0000000..23fc057 --- /dev/null +++ b/p/camera-calibration/index.html @@ -0,0 +1,1309 @@ + + + + +Camera Calibration + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Camera Calibration + + +
+ + +
+ + +
+

+ Camera Calibration +

+ + +

+ The principle and specific implementation of camera calibration +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 由浅到深理解相机标定 +

+ # + 何为相机标定 +

在图像测量过程以及机器视觉应用中,为确定空间无哦表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。

+

在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像头标定)

+

相机标定涉及的知识面很广:成像几何、镜头畸变、单应矩阵、非线性优化等。

+

相机标定有自标定(找图像中特征点)、标定板标定(特征点易求,稳定性好),一般采用标定板标定。

+

相机标定按照相机是否静止,可分为静态相机标定(标定板动,相机静止),动态相机标定(标定板静止,相机运动)。

+

+ # + 为什么需要标定 +

任何理论物理模型都是在特定假设上对真实事物的近似,然而在实际应用中存在误差,普通相机的成像模型也不例外(透视投影)。

+

实际中,普通相机成像误差的主要来源有两部分:

+
    +
  • 第一是sensor(传感器)制造产生的误差,比如sensor成像单元不是正方形,sensor歪斜。
  • +
  • 第二是镜头制造和安装产生的误差,镜头一般存在非线性的径向畸变;镜头与相机sensor安装不平行,还会产生切向畸变。
  • +
+

+ # + 相机标定的目的和意义 +

我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。

+

相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。

+

img

+

这个逼近的过程就是「相机标定」,我们用简单的数学模型来表达复杂的成像过程,并且求出成像的反过程。标定之后的相机,可以进行三维场景的重建,即深度的感知。

+

+ # + 相关术语 +

焦点:在几何光学中有时也称为像点,是源头的光线经过物镜后汇聚的点。

+

焦距:也称为焦长,是光学系统中衡量光的聚集或发散的度量方式,指从透镜中心到光聚集之焦点的距离。亦是照相机中,从镜片光学中心到底片、CCD或CMOS等成像平面的距离。

+

正透镜、负透镜、凹面镜和凸面镜的焦点F和焦距f

+
+img +
+

镜头(Lenses):是将拍摄景物在传感器上成像的器件,它通常由几片透镜、光圈叶片、对焦马达等光学元件组成。

+

传感器(Sensor):是摄像头组成的核心,其作用是作为相机的感光元件。摄像头传感器主要有两种,一种是CCD传感器,一种是CMOS传感器,两者区别在于:CCD的优势在于成像质量好,但是由于制造工艺复杂,成本居高不下,特别是大型CCD,价格非常高昂。在相同分辨率下,CMOS价格比CCD便宜,但是CMOS器件产生的图像质量相比CCD来说要低一些。

+

光心:凸透镜近轴光线中,入射线和与其对应且相平行的出射线构成共轭光线,其入射点跟出射点的连线与主光轴的交点,称为凸透镜的焦点,位于透镜中央的点叫光心。

+
+img +
+

从图中可知,O为光心,F为焦点。每个透镜主轴上都有一个特殊点,凡是通过该点的光,其传播方向不变,这个点叫光心。经过光心的光线的传播方向不会发生改变。

+

+ # + 相机标定原理模型 +

相机标定.drawio

+

+ # + 针孔相机模型 +

我们通常将相机看成如下所示的透镜模型:

+
+img +
+

在实际分析时,通常将其简化为针孔模型(小孔成像):

+
+img +
+

一般为了分析简单,将成像平面画在对称位置,这样图像不再颠倒:

+
+img +
+

+ # + 四个坐标系 +

世界坐标系:用户定义的三维世界的坐标系,用于描述目标物体在真实世界里的位置。单位通常为米(m)。该坐标系作用于三维空间。

+

相机坐标系:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位通常为米(m)。相机坐标系的原点在光心,其 $X_c、Y_c$ 轴分别与像面的两边平行,其 $Z_c$ 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点(实际情况中可能存在主点偏移),相机坐标系与图像坐标系之间的距离为焦距 $f$。该坐标系作用于三维空间。

+

图像坐标系:为了描述成像过程中物体从相机坐标系到图像坐标系的投影投射关系而引入,方便进一步得到像素坐标系下的坐标。其原点是相机光轴与像面的交点(称为主点),即图像的中心点。其 $x, y$ 轴和像素坐标系的 $u, v$ 轴平行,故图像坐标系和像素坐标系实际是平移关系。单位通常为毫米(mm)。该坐标系作用于二维空间。

+

像素坐标系:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为像素。像素坐标平面和图像坐标系平面重合,但像素坐标系原点位于图像左上角。该坐标系作用于二维空间。

+

img

+

+ # + 相机外参 +

将世界坐标系中的点映射到相机坐标系:相机坐标系是世界坐标系通过刚体变换得到的。

+
+

刚体变换能够保持物体中各点的距离和角度,常见的刚体变换有:平移、旋转和镜像。

+
+

我们先只考虑旋转,假设将坐标系以 $X$ 轴为中心进行旋转,即 $X$ 不变,旋转 $Y - Z$ 平面。

+
+img +
+

假设旋转角度为 $\theta$,即 $\angle Y’ O Y = \angle Z’ O Z = \theta$。旋转前的坐标系为 $X - Y - Z$,旋转后的坐标系为 $X’ - Y’ - Z’$。假设点 $P$ 在 $X - Y - Z$ 中的坐标为($X_w, Y_w, Z_w$),旋转后,其在 $X’ - Y’ - Z’$ 中的坐标为($X_c, Y_c, Z_c$): +$$ +X_C = X_w +$$

+

$$ +\begin{array}{l} +Y_c & = OC + CD = OA \cdot \sin \theta + BP \\ +& = Z_w \cdot \sin \theta + AP \cdot \cos \theta \\ +& = Z_w \sin \theta + Y_w \cos \theta +\end{array} +$$

+

$$ +\begin{array}{l} +Z_c & = PD = AC - AB \\ +& = AO \cdot \cos \theta - AP \cdot \cos \theta \\ +& = Z_w \cos \theta + Y_w \cos \theta +\end{array} +$$

+

写成矩阵形式: +$$\displaystyle \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} = \mathbf{R_{cw}} \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} or \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} = \mathbf{R_{wc}} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} $$ +推广到每个方向,可得到 $\mathbf{R_{cw}}, \mathbf{R_{wc}}$ 为: +$$ +\mathbf{R_{cw}} (X_A, \theta) = +\begin{bmatrix} +1 & 0 & 0 \\ +0 & \cos \theta & \sin \theta \\ +0 & - \sin \theta & \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (X_A, \theta) = +\begin{bmatrix} +1 & 0 & 0 \\ +0 & \cos \theta & - \sin \theta \\ +0 & \sin \theta & \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta & 0 & \sin \theta \\ +0 & 1 & 0 \\ - \sin \theta & 0 & \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta & 0 & - \sin \theta \\ +0 & 1 & 0 \\ +\sin \theta & 0 & \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta & \sin \theta & 0 \\ - \sin \theta & \cos \theta & 0 \\ +0 & 0 & 1 +\end{bmatrix} +, +\mathbf{R_{wc}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta & - \sin \theta & 0 \\ +\sin \theta & \cos \theta & 0 \\ +0 & 0 & 1 +\end{bmatrix} +$$

+

这里我们使用右手笛卡尔三维坐标系:

+
+img +
+

旋转可分为主动旋转被动旋转主动旋转是指将向量逆时针围绕旋转轴所做出的旋转。被动旋转是对坐标轴本身进行的逆时针旋转,它相当于主动旋转的逆操作。关于右手笛卡尔坐标系的 $X, Y, Z$ 轴的旋转分别叫做rollpitchyaw旋转:

+
+img +
+

因为逆时针和顺时针旋转会得到不一样的旋转矩阵,所以我们统一如下:

+

绕 $X$ 轴的主动旋转定义为($\theta_x$ 是roll角 ): +$$ +R(X_A, \theta_x) = +\begin{bmatrix} +1 & 0 & 0 \\ +0 & \cos \theta_x & - \sin \theta_x \\ +0 & \sin \theta_x & \cos \theta_x +\end{bmatrix} = +\exp \left ( \theta_x +\begin{bmatrix} +0 & 0 & 0\\ +0 & 0 & -1\\ +0 & 1 & 0 +\end{bmatrix} +\right ) +$$ +绕 $Y$ 轴的主动旋转定义为($\theta_y$ 是pitch角): +$$ +R(Y_A, \theta_y) = +\begin{bmatrix} +\cos \theta_y & 0 & \sin \theta_y \\ +0 & 1 & 0 \\ - \sin \theta_y & 0 & \cos \theta_y +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 & 0 & 1\\ +0 & 0 & 0\\ -1 & 0 & 0 +\end{bmatrix} +\right ) +$$ +绕 $Z$ 轴的主动旋转定义为($\theta_z$ 是yaw角): +$$ +R(Z_A, \theta_z) = +\begin{bmatrix} +\cos \theta_z & - \sin \theta_z & 0 \\ +\sin \theta_z & \cos \theta_z & 0 \\ +0 & 0 & 1 +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 & -1 & 0\\ +1 & 0 & 0\\ +0 & 0 & 0 +\end{bmatrix} +\right ) +$$ +将上述三个旋转矩阵结合起来,最终的旋转矩阵(设绕 $X, Y, Z$ 轴旋转的角度分别为 $\alpha, \beta, \gamma$): +$$ +\begin{array}{ll} +M(\alpha, \beta, \gamma) & = R_x(\alpha) R_y(\beta) R_z(\gamma) \\ +& = +\begin{bmatrix} +1 & 0 & 0 \\ +0 & \cos \alpha & - \sin \alpha \\ +0 & \sin \alpha & \cos \alpha +\end{bmatrix} +\begin{bmatrix} +\cos \beta & 0 & \sin \beta \\ +0 & 1 & 0 \\ - \sin \beta & 0 & \cos \beta +\end{bmatrix} +\begin{bmatrix} +\cos \gamma & -\sin \gamma & 0 \\ +\sin \gamma & \cos \gamma & 0 \\ +0 & 0 & 1 +\end{bmatrix} \\ +& = \begin{bmatrix} +\cos \gamma \cos \beta & - \sin \gamma \cos \alpha + \cos \gamma \sin \beta \sin \alpha & \sin \gamma \sin \alpha + \cos \gamma \sin \beta \cos \alpha \\ +\sin \gamma \cos \beta & \cos \gamma \cos \alpha + \sin \gamma \sin \beta \sin \alpha & - \cos \gamma \sin \alpha + \sin \gamma \sin \beta \cos \alpha \\ - \sin \beta & \cos \beta \sin \alpha & \cos \beta \cos \alpha +\end{bmatrix} +\end{array} +$$

+

此时我们再加上平移向量 $T$ 便可完成从世界坐标系到相机坐标系的这个刚体变换了:

+

$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c +\end{bmatrix} = +\begin{bmatrix} +r_{11} & r_{12} & r_{13} \\ +r_{21} & r_{22} & r_{23} \\ +r_{31} & r_{32} & r_{33} +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + +\begin{bmatrix} +t_x \\ +t_y \\ +t_z +\end{bmatrix} = +\mathbf{R} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + T +$$

+

可进一步写成如下形式:

+

$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = +\begin{bmatrix} +\mathbf{R} & \mathbf{T} \\ +0_3^T & 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +$$

+

其中,$\mathbf{R}$ 和 $\mathbf{T}$ 便是相机外参。

+

+ # + 相机内参 +

首先考虑图像坐标系($xy$)和像素坐标系($uv$)之间的转换:

+

img

+

$$ +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +\displaystyle \frac{1}{dx} & 0 & u_0 \\ +0 & \displaystyle \frac{1}{dy} & v_0 \\ +0 & 0 & 1 +\end{bmatrix}= +\begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix} +$$

+

$dx$ 表示一个像素点在 $x$ 方向的长度是多少毫米,$dy$ 表示一个像素点在 $y$ 方向的长度是多少毫米;$(u_0, v_0)$ 为图像的中心点。

+

然后考虑相机坐标系和图像坐标系之间的转换:

+

img

+

$$ +\Delta ABO_c \sim \Delta oCO_c, \Delta PBO_c \sim \Delta pCO_c +$$ +$$ +\displaystyle \frac{AB}{oC} = \frac{AO_c}{oO_c} = \frac{PB}{p C} = \frac{X_c}{x} = \frac{Z_c}{f} = \frac{Y_c}{y} +$$ +$$ +x = f \displaystyle \frac{X_c}{Z_c}, y = f \frac{Y_c}{Z_c} +$$

+

$$ +Z_c \begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix}= +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +f & 0 & 0 & 0 \\ +0 & f & 0 & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$

+

其中,$f$ 是焦距,结合外参我们最终可以得到世界坐标系和像素坐标系之间的映射关系:

+

$$ +\begin{array}{l} +\lambda \begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} & = +\begin{bmatrix} +\displaystyle \frac{1}{dx} & 0 & u_0 \\ +0 & \displaystyle \frac{1}{dy} & v_0 \\ +0 & 0 & 1 +\end{bmatrix} +\begin{bmatrix} +f & 0 & 0 & 0 \\ +0 & f & 0 & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} & \mathbf{T} \\ +0 & 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix}\\ +& = +\begin{bmatrix} +fx & 0 & u_0 & 0 \\ +0 & fy & v_0 & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} & \mathbf{T} \\ +0 & 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +\end{array} +$$ +其中,相机内参为(不考虑图像传感器的特性): +$$ +\begin{bmatrix} +fx & 0 & u_0 & 0 \\ +0 & fy & v_0 & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +$$

+

其中,$f_x, f_y$ 即为焦距的物理距离在像素坐标系中的长度,相机内参标定主要是标定相机的焦距、主点、歪斜等内部参数。

+

+ # + 可能存在的影响 +

+ # + 主点偏移 +

主点是光轴和相机成像平面的交点,在理想情况下,图像坐标系和相机坐标系原点重合,不存在坐标系偏移。但在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动:

+
+ img +
+

考虑主点偏移后,图像坐标和3D在相机坐标系的关系为:

+

$$ +\begin{matrix} +u = f \frac{X}{Z} + O_x \\ +v = f \frac{X}{Z} + O_y +\end{matrix} +$$ +此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f & 0 & O_x & 0 \\ +0 & f & O_x & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$

+

仔细观察就会发现,该关系与上面提到的关系是等价的,只不过上面使用坐标 $(u_0, v_0)$ 来代表偏移量 $(O_x, O_y)$。

+

+ # + 图像传感器特征 +

图像传感器像原尺寸在制造过程可能不是正方形,同时可能存在歪斜(skewed),因此需要考虑这些影响因素,传感器歪斜和不是正方形主要对相机 $x$ 和 $y$ 方向的焦距产生影响。

+
+img +
+

此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f & s & O_x & 0 \\ +0 & \eta f & O_x & 0 \\ +0 & 0 & 1 & 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = [K, 0_3] P +$$ +其中,$K$ 矩阵即为最终的内参矩阵。

+

+ # + 镜头畸变 +

小孔成像模型虽然充分考虑了相机内部参数对成像的影响,但没有考虑成像系统另一个重要的部分,镜头。镜头常用的有普通镜头、广角镜头、鱼眼镜头等,在无人驾驶和视觉slam领域,鱼眼镜头和广角镜头用的很多,主要是视角很大,可以观测到更多的信息。任何镜头都存在不同程度的畸变,不同类型的镜头用到的畸变模型也不相同。

+

在几何光学和阴极射线管(CRT)显示中,畸变(distortion)是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括径向畸变(radial distortion)切向畸变(tangential distortion)

+

径向畸变来自于透镜形状,主要是由于透镜不同部位放大倍率不同造成的。切向畸变来自于整个相机的组装过程,主要是由于透镜安装与成像平面不平行造成的。

+

img

+

+ # + 径向畸变 +

透过镜头边缘的光线很容易产生径向畸变,这种现象来源于“筒形”或“鱼眼”的影响。光线离镜头中心越远,畸变越大。

+

img

+

从图像可以看出,径向畸变以某一个中心往外延伸,且越往外,畸变越大;显然畸变与距离成一种非线性的变换关系,参考众多文献,可以用多项式来近似: +$$ +\begin{matrix} +x_{rcrt} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\\\ +y_{rcrt} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$k_1, k_2, k_3$ 是径向畸变系数,$r^2 = x^2 + y^2$。

+

+ # + 切向畸变 +

切向畸变主要发生在相机sensor和镜头不平行的情况下;因为有夹角,所以光透过镜头传到图像传感器上时,成像位置发生了变化。

+

img +$$ +\begin{matrix} +x_{tcrt} = x + [2p_1 xy + p_2 (r^2 + 2 x^2)] \\\\ +y_{tcrt} = y + [2p_2 xy + p_1 (r^2 + 2 y^2)] +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$p_1, p_2$ 是切向畸变系数,$r^2 = x^2 + y^2$。

+

+ # + 消除镜头畸变 +

考虑镜头畸变前,我们可以将相机标定简单描述为以下过程:像素坐标 $(u_{ccd}, v_{ccd})$ $\to$ 图像坐标 $(x, y)$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。

+

此时我们考虑加入镜头畸变: +$$ +\begin{matrix} +x_{crt} = x_{rcrt} + x_{tcrt} \\\\ +y_{crt} = y_{rcrt} + y_{tcrt} +\end{matrix} +$$ +得到消除镜头畸变的相机标定流程:像素坐标 $(u_{ccd - crt}, v_{ccd - crt})$ $\to$ 图像坐标 $(x_{crt}, y_{crt})$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。

+

+ # + 标定板的作用 +

+ # + 相机标定中的参数 +

相机标定

+

针孔相机模型中,只要确定这9个参数就可以唯一的确定针孔相机模型:

+

$$ +f_x,f_y,O_x,O_y,k_1,k_2,k_3,p_1,p_2 +$$

+

这个过程就称为「相机标定」,其中前4个我们称为内参数,后5个称为畸变参数,畸变参数是为了补充内参的。所以一旦相机结构固定,包括镜头结构固定,对焦距离固定,我们就可以用这9个的参数去近似这个相机。这里说的「镜头结构固定」,按我个人的理解,除了焦距固定之外,也应当包含光圈固定,因为改变光圈的大小,除了景深之外,是有可能改变针孔相机模型中的光心位置,但是影响并不是很大。这意味着标定好的相机如果改变光圈大小,会使得标定误差变大但应该不会大到难以接受的地步。

+

对于针孔相机本身需要拟合的方程如下:

+

$$ +\begin{bmatrix} +u_{ccd - crt} * Z\\ +v_{ccd - crt} * Z\\ +Z +\end{bmatrix} = +J(k_1, k_2, k_3, p_1, p_2) +\begin{bmatrix} +f_x & 0 & O_x \\ +0 & f_y & O_y \\ +0 & 0 & 1 +\end{bmatrix} +\begin{bmatrix} +X \\ +Y \\ +X +\end{bmatrix} +$$

+

因此,我们现在的任务就是找出一大堆具有对应关系的像点 ${(u_{ccd - crt}, v_{ccd - crt}) ^T }$ 和物点 ${ (X, Y, Z)^T }$ 的点作为样本,来训练出模型的参数。这里就引发了两个问题:

+
    +
  • 这么多像点和物点如何匹配?
  • +
  • 即便现在知道物点的位置,如何用相机坐标系来表达物点的位置 $(X, Y, Z)$?
  • +
+

为了解决上述问题,标定板应运而生。标定板的一大作用,确定物点和像点的对应性。这里用到的原理主要是「透视不变性」,打个比方,你近看一个人和远看一个人,虽然他的鼻子大小变了,你看鼻子的视角也变了,但是拓扑结构肯定是不变的,你也不可能把鼻子看成是嘴巴。

+

img

+

所以在标定板中,印刷了拓扑结构,广泛应用的是棋盘格和圆点格,这两种之所以成为主流,不仅是因为它们的拓扑结构明确且均匀,更重要的是检测其拓扑结构的算法简单且有效。棋盘格检测的是角点,只要对拍摄到的棋盘格图像横纵两个方向计算梯度就可获得;而圆点格的检测只需要对拍摄到的圆点格图样计算质心即可。假如你开发了一套非常完美的检测人脸全部特征的算法,你完全可以用你的照片当作标定板。

+

按照我的经验,圆点格的效果应该是好于棋盘格,因为圆点质心的「透视不变性」要比棋盘格的角点稳定的多。下图是同样尺寸、同样比例棋盘格和圆点在最大重投影误差处的误差对比,红色十字是提取的角点/质心,绿色圆圈是针孔相机模型计算出来认为的角点/质心位置。

+

但是圆点格的检测似乎是Halcon的专利(存疑),因此OpenCV和Matlab标定工具箱用的是棋盘格,要用圆点格得要自己写算法。下文中提到的标定板说的都是棋盘格。

+

标定板的第二大作用是把标定板中的角点变换到相机坐标系下的坐标 $(X, Y, Z)$。对于标定的初学者来说,很容易忽略的一点是标定板是具有标定板坐标系的。换句话说,标定板中的每个角点,在标定板坐标系下的位置是确定并且是已知的。

+

img

+

而标定板坐标系变换到相机坐标系的变换矩阵,我们称它的元素为外参数。

+

+ # + 如何使用标定板 +

如果用OpenCV或Matlab标定工具箱进行标定,需要给出棋盘格的物理尺寸,这其实就是在建立标定板坐标系,从测量的角度讲,标定板的精度是相机标定精度的基准,是误差传递链上的第一个环节。所以为了使针孔相机模型更逼近真实相机,对标定板的质量有以下要求(按重要性顺序):

+
    +
  • 标定板的平面度高,棋盘格是直角。
  • +
  • 标定板每个格子尺寸的高一致性。
  • +
  • 真实尺寸与标称尺寸的差异小。
  • +
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/clear-c-ch09-function-application/9-2.cpp b/p/clear-c-ch09-function-application/9-2.cpp new file mode 100644 index 0000000..d02d744 --- /dev/null +++ b/p/clear-c-ch09-function-application/9-2.cpp @@ -0,0 +1,16 @@ +#include + + +template +type reMax(const type x, const type y) { + return x > y ? x : y; +} + + +int main() { + const int x1 = 2, y1 = 3; + std::cout << reMax(x1, y1) << std::endl; + + const double x2 = 4.5, y2 = 3.8; + std::cout << reMax(x2, y2) << std::endl; +} \ No newline at end of file diff --git a/p/clear-c-ch09-function-application/assets/clearcpp.jpg b/p/clear-c-ch09-function-application/assets/clearcpp.jpg new file mode 100644 index 0000000..23274ad Binary files /dev/null and b/p/clear-c-ch09-function-application/assets/clearcpp.jpg differ diff --git a/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_120x120_fill_q75_box_smart1.jpg b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..84dc829 Binary files /dev/null and b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_1600x0_resize_q75_box.jpg b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..e71de7f Binary files /dev/null and b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_1600x0_resize_q75_box.jpg differ diff --git a/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_800x0_resize_q75_box.jpg b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..b453e8b Binary files /dev/null and b/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_800x0_resize_q75_box.jpg differ diff --git a/p/clear-c-ch09-function-application/index.html b/p/clear-c-ch09-function-application/index.html new file mode 100644 index 0000000..9504a41 --- /dev/null +++ b/p/clear-c-ch09-function-application/index.html @@ -0,0 +1,583 @@ + + + + +Clear C++ Ch09: Function application + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Clear C++ Ch09: Function application + + +
+ + +
+ + +
+

+ Clear C++ Ch09: Function application +

+ + +

+ Clear C++ Ch09: Function application +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 函数的应用 +

+ # + 函数模板 +

+
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg b/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg differ diff --git a/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg b/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2a47b2d Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/cppjson-ch01/assets/JSON-Tutorial.jpg b/p/cppjson-ch01/assets/JSON-Tutorial.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial.jpg differ diff --git a/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..29d92e5 Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..793217d Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg differ diff --git a/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..f0f20db Binary files /dev/null and b/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg differ diff --git a/p/cppjson-ch01/assets/requirement.png b/p/cppjson-ch01/assets/requirement.png new file mode 100644 index 0000000..3f9d035 Binary files /dev/null and b/p/cppjson-ch01/assets/requirement.png differ diff --git a/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png b/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png new file mode 100644 index 0000000..c96974d Binary files /dev/null and b/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png differ diff --git a/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png b/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png new file mode 100644 index 0000000..fba7e16 Binary files /dev/null and b/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png differ diff --git a/p/cppjson-ch01/index.html b/p/cppjson-ch01/index.html new file mode 100644 index 0000000..6cf162d --- /dev/null +++ b/p/cppjson-ch01/index.html @@ -0,0 +1,1238 @@ + + + + +CppJson Ch01 + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post CppJson Ch01 + + +
+ + +
+ + + + +
+

+ CppJson Ch01 +

+ + +

+ My CppJson tutorial ch01: parse null and bool +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + CppJson 第一章节:自动测试,NULLbool 值解析 +

+ # + JSON 是什么 +

JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为ECMA-404

+

虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。

+

例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
+
{
+    "title": "Design Patterns",
+    "subtitle": "Elements of Reusable Object-Oriented Software",
+    "author": [
+        "Erich Gamma",
+        "Richard Helm",
+        "Ralph Johnson",
+        "John Vlissides"
+    ],
+    "year": 2009,
+    "weight": 1.8,
+    "hardcover": true,
+    "publisher": {
+        "Company": "Pearson Education",
+        "Country": "India"
+    },
+    "website": null
+}
+
+
+

网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。

+

从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:

+
    +
  • null: 表示为 null
  • +
  • boolean: 表示为 true 或 false
  • +
  • number: 一般的浮点数表示方式,在下一单元详细说明
  • +
  • string: 表示为 “…”
  • +
  • array: 表示为 [ … ]
  • +
  • object: 表示为 { … }
  • +
+

我们要实现的 JSON 库,主要是完成 3 个需求:

+
    +
  1. 把 JSON 文本解析为一个树状数据结构(parse)。
  2. +
  3. 提供接口访问该数据结构(access)。
  4. +
  5. 把数据结构转换成 JSON 文本(stringify)。
  6. +
+

+

我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。

+

+ # + 搭建编译环境 +

我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。

+

我们的 JSON 库名为 CppJson,代码文件只有 3 个:

+
    +
  1. include/cppjson.hpp:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。
  2. +
  3. cppjson.cpp:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。
  4. +
  5. cppjsonTest.cpp:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。
  6. +
+

为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 CMake

+

在 OS X 平台中,在命令行通过命令:

+
+ +
+
1
+2
+3
+4
+
+
mkdir build
+cd build
+cmake -DCMAKE_BUILD_TYPE=Debug ..
+make
+
+
+

将 Debug 改成 Release 就会生成 Release 配置的 makefile。

+

在 Vscode 中,可以通过配置 tasks.json 文件来进行自动 build:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+
+
//.vscode/tasks.json
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "type": "shell",
+            "label": "mkdirbuild",
+            "command": "mkdir",
+            "options": {
+                "cwd": "${fileDirname}"
+            },
+            "args": ["-p", "build"]
+        },
+        {
+            "type": "shell",
+            "label": "cmake",
+            "command": "cmake",
+            "args": [
+                "-DCMAKE_BUILD_TYPE=Debug",
+                //在此处添加其它CMAKE选项
+                ".."
+            ],
+            "options": {
+                "cwd": "${fileDirname}/build"
+            },
+        },
+        {
+            "label": "make",
+            "command": "make",
+            "args": ["-j16",], //根据机器cpu核心数量自行调整
+            "options": {
+                "cwd": "${fileDirname}/build"
+            },
+        },
+        {
+            "label": "build",
+            "dependsOrder": "sequence",
+            "dependsOn": ["mkdirbuild", "cmake", "make"],
+        },
+    ],
+}
+
+
+

然后执行 build 生成的文件:

+
+ +
+
1
+2
+3
+
+
$ ./build/cppjson_test_ch01
+
+16/16 (100.00%) passed
+
+
+

若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。

+

+ # + 头文件与 API 设计 +

Cpp 语言有头文件的概念,需要使用 #include去引入头文件中的类型声明和函数声明。但由于头文件也可以 #include 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):

+
+ +
+
1
+
+
#pragma once
+
+
+

如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
enum class cppjsonType {
+    CPPJSON_NULL,
+    CPPJSON_TRUE,
+    CPPJSON_FALSE,
+    CPPJSON_NUMBER,
+    CPPJSON_STRING,
+    CPPJSON_ARRAY,
+    CPPJSON_OBJECT
+};
+
+
+

接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 cppjson_value 结构体表示,我们会称它为一个 JSON 值(JSON value)。

+

在此单元中,我们只需要实现 null, truefalse 的解析,因此该结构体只需要存储一个 cppjsonType,之后的单元会逐步加入其他数据。

+
+ +
+
1
+2
+3
+
+
typedef struct {
+    cppjsonType type;
+} cppjson_value;
+
+
+

然后,我们现在只需要两个 API 函数,一个是解析 JSON:

+
+ +
+
1
+
+
cppjsonParseCode cppjson_parse(cppjson_value* v, const std::string json);
+
+
+

传入的 JSON 文本是一个 string 字符串,由于我们不应该改动这个输入字符串,所以使用 const std::string 类型。

+

返回值是以下这些枚举类中的值,无错误会返回 cppjsonParseCode::OK,其他值在下节解释。

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
enum class cppjsonParseCode {
+    OK,
+    EXPECT_VALUE,
+    INVALID_VALUE,
+    ROOT_NOT_SINGULAR
+};
+
+
+

现时我们只需要一个访问结果的函数,就是获取其类型:

+
+ +
+
1
+
+
cppjsonType cppjson_get_type(const cppjson_value* v);
+
+
+

+ # + JSON 语法子集 +

下面是此单元的 JSON 语法子集,使用 RFC7159 中的 ABNF 表示:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
JSON-text = ws value ws
+ws = *(%x20 / %x09 / %x0A / %x0D)
+value = null / false / true 
+null  = "null"
+false = "false"
+true  = "true"
+
+
+

当中 %xhh 表示以 16 进制表示的字符,/ 是多选一,* 是零或多个,() 用于分组。

+

那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。

+

第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。

+

第三行是说,我们现时的值只可以是 nullfalsetrue,它们分别有对应的字面值(literal)。

+

我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。

+

在这个 JSON 语法子集下,我们定义 3 种错误码:

+
    +
  • 若一个 JSON 只含有空白,传回 LEPT_PARSE_EXPECT_VALUE
  • +
  • 若一个值之后,在空白之后还有其他字符,传回 LEPT_PARSE_ROOT_NOT_SINGULAR
  • +
  • 若值不是那三种字面值,传回 LEPT_PARSE_INVALID_VALUE
  • +
+

+ # + 单元测试 +

许多同学在做练习题时,都是以 printfcout 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。

+

常用的单元测试框架有 xUnit 系列,如 C++ 的 Google Test、C# 的 NUnit。我们为了简单起见,会编写一个极简单的单元测试方式。

+

一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:

+
    +
  1. 加入一个测试。
  2. +
  3. 运行所有测试,新的测试应该会失败。
  4. +
  5. 编写实现代码。
  6. +
  7. 运行所有测试,若有测试失败回到3。
  8. +
  9. 重构代码。
  10. +
  11. 回到 1。
  12. +
+

TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。

+

但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。

+

回到 CppJson 项目,cppjsonTest.cpp 包含了一个极简的单元测试框架:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
+
#include "include/cppjson.hpp"
+#include <cstdio>
+#include <stdio.h>
+#include <typeinfo>
+
+static int main_ret = 0;
+static int test_count = 0;
+static int test_pass = 0;
+
+// 测试宏接口
+// flag 表示测试点是否通过,如果未通过则打印异常信息
+#define EXPECT_BASE(flag, expect, actual) \
+    do {\
+        test_count ++;\
+        if (flag) test_pass ++;\
+        else {\
+            fprintf(stderr, "%s:%d, expect = %s(%d), actual = %s(%d)\n", __FILE__, __LINE__, typeid(expect).name(), static_cast<int>(expect), typeid(actual).name(), static_cast<int>(actual));\
+            main_ret = 1;\
+        }\
+    } while(0)
+
+#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual)
+
+
+//
+static void test_parse_null() {
+    cppjson_value v;
+    v.type = cppjsonType::CPPJSON_FALSE;
+
+    EXPECT_TYPE(cppjsonParseCode::OK, cppjson_parse(&v, "null"));
+    EXPECT_TYPE(cppjsonType::CPPJSON_NULL, cppjson_get_type(&v));
+}
+
+static void test_parse() {
+    test_parse_null();
+    // ...
+}
+
+int main() {
+    test_parse();
+    printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
+    return main_ret;
+}
+
+
+

现时只提供了一个 EXPECT_TYPE(expect, actual) 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。

+

若按照 TDD 的步骤,我们先写一个测试,如上面的 test_parse_null(),而 cppjson_parse() 只返回 cppjsonParseCode::OK

+
+ +
+
1
+2
+
+
/CppJson/ch01/cppjsonTest.cpp:30, expect = 16cppjsonParseCode(0), actual = 16cppjsonParseCode(2)
+15/16 (93.75%) passed
+
+
+

为通过的测试是因为 cppjson_parse() 没有把 v.type 改成 cppjsonType::CPPJSON_NULL,造成失败。我们再实现 lept_parse() 令到它能通过测试。

+

然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。

+

+ # + 实现解析器 +

有了 API 的设计、单元测试,终于要实现解析器了。

+

首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 cppjson_context 结构体:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
typedef struct {
+    std::string json;
+} cppjson_context;
+
+// json 解析函数:ws1 value ws2
+cppjsonParseCode cppjson_parse(cppjson_value *v, const std::string json) {
+    cppjson_context s; s.json = json;
+    assert(v != NULL); v->type = cppjsonType::CPPJSON_NULL;
+
+    // 分别解析 ws1 value ws2
+    cppjson_parse_whitespace(&s);
+    auto ret = cppjson_parse_value(&s, v);
+    return ret == cppjsonParseCode::OK ? cppjson_parse_root_not_singular(&s) : ret;
+}
+
+
+

CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:

+
    +
  • n ➔ null
  • +
  • t ➔ true
  • +
  • f ➔ false
  • +
  • " ➔ string
  • +
  • 0-9/- ➔ number
  • +
  • [ ➔ array
  • +
  • { ➔ object
  • +
+

所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
+
// 删除空白符
+static void cppjson_parse_whitespace(cppjson_context* s) {
+    const std::string str = s->json;
+
+    int i = 0;
+    while (str[i] == ' ' or str[i] == '\t' or str[i] == '\n' or str[i] == '\r') { i ++; }
+
+    s->json = str.substr(i, str.size());
+}
+
+// 解析 ws2 之后是否还有非空值
+static cppjsonParseCode cppjson_parse_root_not_singular(cppjson_context* s) {
+    // 删除空白符
+    cppjson_parse_whitespace(s);
+
+    // 判断是否还有非空值
+    if (s->json[0] != '\0') return cppjsonParseCode::ROOT_NOT_SINGULAR;
+    else return cppjsonParseCode::OK;
+}
+
+// 检测 null 值
+static cppjsonParseCode cppjson_parse_null(cppjson_context* s, cppjson_value* v) {
+    const std::string str = s->json;
+
+    auto head = str.substr(0, 4);
+    if (head != "null") return cppjsonParseCode::INVALID_VALUE;
+    else {
+        s->json = str.substr(4, str.size());
+        v->type = cppjsonType::CPPJSON_NULL;
+        return cppjsonParseCode::OK;
+    }
+}
+
+// 解析 value
+static cppjsonParseCode cppjson_parse_value(cppjson_context* s, cppjson_value* v) {
+    const std::string str = s->json;
+
+    switch (str[0]) {
+        case 'n': return cppjson_parse_null(s, v);
+        case 't': return cppjson_parse_true(s, v);
+        case 'f': return cppjson_parse_false(s, v);
+        case '\0': return cppjsonParseCode::EXPECT_VALUE;
+        default: return cppjsonParseCode::INVALID_VALUE;
+    }
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg b/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64.jpg differ diff --git a/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg b/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2a47b2d Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial.86d60edcd464b4a1e408e59f91018d64_hu2efd45d35d025ca12881d6951e1b4111_45457_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/cppjson-ch02/assets/JSON-Tutorial.jpg b/p/cppjson-ch02/assets/JSON-Tutorial.jpg new file mode 100644 index 0000000..c3f7a06 Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial.jpg differ diff --git a/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..29d92e5 Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..793217d Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_1600x0_resize_q75_box.jpg differ diff --git a/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..f0f20db Binary files /dev/null and b/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_800x0_resize_q75_box.jpg differ diff --git a/p/cppjson-ch02/index.html b/p/cppjson-ch02/index.html new file mode 100644 index 0000000..0423989 --- /dev/null +++ b/p/cppjson-ch02/index.html @@ -0,0 +1,598 @@ + + + + +CppJson Ch02 + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + Featured image of post CppJson Ch02 + + +
+ + +
+ + + + +
+

+ CppJson Ch02 +

+ + +

+ My CppJson tutorial ch02: parse number +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + CppJson 第二章节:number 值解析 +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg b/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg differ diff --git a/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg b/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..d417298 Binary files /dev/null and b/p/dhubachelor/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/dhubachelor/assets/latex.jpg b/p/dhubachelor/assets/latex.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/dhubachelor/assets/latex.jpg differ diff --git a/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..cf0ba5a Binary files /dev/null and b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..5a359f0 Binary files /dev/null and b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg differ diff --git a/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..7783dc9 Binary files /dev/null and b/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg differ diff --git a/p/dhubachelor/assets/weixin.jpg b/p/dhubachelor/assets/weixin.jpg new file mode 100644 index 0000000..d9d2eab Binary files /dev/null and b/p/dhubachelor/assets/weixin.jpg differ diff --git a/p/dhubachelor/assets/zip.png b/p/dhubachelor/assets/zip.png new file mode 100644 index 0000000..e2102ea Binary files /dev/null and b/p/dhubachelor/assets/zip.png differ diff --git a/p/dhubachelor/index.html b/p/dhubachelor/index.html new file mode 100644 index 0000000..ab6c607 --- /dev/null +++ b/p/dhubachelor/index.html @@ -0,0 +1,1172 @@ + + + + +dhuBachelor + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post dhuBachelor + + +
+ + +
+ + + + +
+

+ dhuBachelor +

+ + +

+ Donghua University Bachelor's Thesis LaTeX Template User Manual +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 东华大学学士毕业论文 LaTeX 模板使用手册 +

+ # + 导入模板 +

+ # + 使用 Git 克隆仓库 +

将仓库克隆到你需要的位置:

+
+ +
+
1
+2
+3
+
+
git clone git@github.com:3000ye/dhuBachelor.git
+# 或
+git clone https://github.com/3000ye/dhuBachelor.git
+
+
+

+ # + 下载 Zip 文件 +

如果你不会使用Git,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载Zip文件:

+
+img +
+

+ # + 微信公众号获取 +

如果你没有科学代理,无法进入Github,请扫码关注公众号:3000ye Blog然后回复latex模板获取百度网盘分享链接。

+
+img +
+

+ # + 开始使用 +

+ # + 安装字体 +

在使用模板之前,请先找到并打开fonts文件夹,安装里面的所有字体。

+

+ # + 配置 TeX 环境 +

如果你是 $\LaTeX$ 小白,请先行阅读:使用 LaTeX 优雅地完成创作,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。

+

+ # + 尝试编译 +

使用你喜欢的编辑器,打开dhuBachelor.tex文件,选择xelatex命令编译文件。如果没有出现报错,同目录下会生成一个dhuBachelor.pdf文件,这就是我们的论文。

+

+ # + 各个组件说明 +

看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。

+

本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。

+

+ # + 论文题目 +

根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:

+
+ +
+
1
+2
+
+
\reTitle{中文题目}
+\reTitleEN{英文题目}
+
+
+

+ # + 摘要 +

根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:

+
+ +
+
1
+2
+
+
\reAbstract % 中文摘要
+\reAbstractEN % 英文摘要
+
+
+

摘要内容直接写在摘要下方,首行缩进两字符。

+

+ # + 关键词 +

中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。

+

英文关键词:Times New Roman(标题加黑)。

+
+ +
+
1
+2
+
+
\reKeyword{关键词1,关键词2,关键词3,关键词4} % 中文关键词
+\reKeywordEN{Keyword1, Keyword2, Keyword3} % 英文关键词
+
+
+

+ # + 目录 +

目录标题需居中显示,添加代码:

+
+ +
+
1
+2
+3
+
+
\begin{center}
+    \tableofcontents
+\end{center}
+
+
+

+ # + 各级标题 +

规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。

+
+ +
+
1
+2
+3
+
+
\reSection{一级标题}
+\subsection{二级标题}
+\subsubsection{三级标题}
+
+
+

+ # + 有序列表 +

规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
\orderedList{ % 使用 (i) 排序,缩进 2 字符
+    \item 有序列表标题 \par % \par的作用是将内容换行
+    这是有序列表的内容
+
+    \item 有序列表标题 \par
+    这是有序列表的内容
+}
+
+
+

+ # + 数学公式 +

行内公式:$f(x) = x + 1$

+

跨行公式:跨行公式请使用equation环境,默认按照章节自动编号。

+
+ +
+
1
+2
+3
+4
+5
+
+
\begin{equation}
+    x = a_0 + \cfrac{1}{a_1 
+            + \cfrac{1}{a_2 
+            + \cfrac{1}{a_3 + \cfrac{1}{a_4} } } }
+\end{equation}
+
+
+

+ # + 插入图片 +

图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改width大小和图片路径,并根据实际更改图例和索引。

+

注意:图片需要保存在assets/目录中才能被正确插入,读者可以自己新建其他目录实现插入。

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+
+
\begin{figure}[H] % 图片位于文字下方
+    \centering % 居中
+    % 设置图片占页面宽度的比例(默认0.8)
+    \includegraphics[width=0.8\textwidth]{assets/dataStructures.jpg}
+    \caption{图片标题} % 图例,按章节编号
+    \label{fig: 数据结构2} % 图片索引
+\end{figure}
+
+
+

有时可能需要多图并排,模板使用minipage来实现并排展示,使用时可以修改子图所占比例:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+
+
\begin{figure}[H]
+    \centering
+    \begin{minipage}[c]{0.40\textwidth} %minipage使之保持同一行
+    \centering
+    \includegraphics[width=0.8\textwidth]{assets/dataStructures.jpg}\\
+    \caption{图片1标题}
+    \end{minipage}
+    \hspace{1em}
+    \begin{minipage}[c]{0.40\textwidth} %minipage使之保持同一行
+    \centering
+    \includegraphics[width=0.8\textwidth]{assets/dataStructures.jpg}\\
+    \caption{图片2标题}
+    \end{minipage}
+\end{figure}
+
+
+

+ # + 插入表格 +

使用 excel2latex 工具生成表格代码后,需要手动添加分割线(\toprule, \midrule, \bottomrule),以达到三线表的格式要求。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+
+
\begin{table}[H]
+    \centering
+    \caption{表格标题}
+        \begin{tabular}{c||l}
+        \toprule
+        parameter  & Description \\
+        \midrule
+        $I$ & Land area collection \\
+        $J$ & Flower pollination demand set \\
+        $D_j$ & Number of pollinating bees required for flower pollination \\
+        $T_k$ & Honeycomb size grade, $k = 1, 2, \cdots$ \\
+        $B$ & Maximum number of hive \\
+        $R_{ik}$ & Maximum influence radius of a single honeycomb \\
+        \bottomrule
+        \end{tabular}%
+    \label{tab: 一个表}%
+\end{table}%
+
+
+

对于需要多表并排的情况,和图片的方式类似,使用minipage来实现:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
\begin{minipage}[c]{0.45\textwidth}
+    \centering
+    \begin{table}[H]
+        \centering
+        \caption{表格1标题}
+            \begin{tabular}{c||lc}
+            \toprule
+            Symbol  & Description & Unit \\
+            \midrule
+            $t$ & $t_{th}$ year & $\sim$ \\
+            $e_k$ & the error term & $\sim$ \\
+            $X_{ij}$ & Raw data matrix & $\sim$ \\
+            $Y_{ij}$ & Positive matrix & $\sim$ \\
+            \bottomrule
+            \end{tabular}%
+        \label{tab: 表格1标题}%
+    \end{table}%
+\end{minipage}
+\begin{minipage}[c]{0.45\textwidth}
+    \centering
+    \begin{table}[H]
+        \centering
+        \caption{表格2标题}
+            \begin{tabular}{c||lc}
+            \toprule
+            Symbol  & Description & Unit \\
+            \midrule
+            $t$ & $t_{th}$ year & $\sim$ \\
+            $e_k$ & the error term & $\sim$ \\
+            $X_{ij}$ & Raw data matrix & $\sim$ \\
+            $Y_{ij}$ & Positive matrix & $\sim$ \\
+            \bottomrule
+            \end{tabular}%
+        \label{tab: 表格2标题}%
+    \end{table}%
+\end{minipage}
+
+
+

+ # + 插入代码 +

可以直接在.tex文件中编写代码,并指定语言和标题:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
\begin{lstlisting}[language=c++,title={code.cpp}]
+#include "bits/stdc++.h"
+
+using namespace std;
+
+int main() {
+    cout << "3000ye 的 LaTeX 模板!" << endl;
+
+    return 0;
+}
+\end{lstlisting}
+
+
+

另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在assets/目录下:

+
+ +
+
1
+
+
\lstinputlisting[language=c++, title=code.cpp]{code/code.cpp}
+
+
+

+ # + 插入伪代码 +

使用宏包algorithm, algorithmic来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+
+
\begin{algorithm}
+    \caption{Example Pseudocode}
+    \begin{algorithmic}
+        \STATE $x\gets0$
+        \IF {$x\leq 0$}
+        \STATE $x\gets x+1$
+        \ELSE
+        \STATE $x\gets x-1$
+        \ENDIF
+    \end{algorithmic}
+\end{algorithm}
+
+
+

+ # + 参考文献 +

参考文献使用\bibitem来添加,添加时需要手动更改{RNi}索引(i是你文献的序号)。

+
+ +
+
1
+2
+3
+4
+
+
\reference{
+    \bibitem{RN1} 参考文献1
+    \bibitem{RN2} 参考文献2
+}
+
+
+

+ # + 致谢 +

+ +
+
1
+2
+3
+
+
\reThanks{
+    致谢,3000ye 的 \LaTeX 模板!
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.png" "b/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.png" new file mode 100644 index 0000000..63d2f3e Binary files /dev/null and "b/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.png" differ diff --git "a/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.xmind" "b/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.xmind" new file mode 100644 index 0000000..9553585 Binary files /dev/null and "b/p/dhubeamer/assets/Beamer\345\205\203\347\264\240.xmind" differ diff --git a/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg b/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg differ diff --git a/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg b/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..d417298 Binary files /dev/null and b/p/dhubeamer/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/dhubeamer/assets/latex.jpg b/p/dhubeamer/assets/latex.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/dhubeamer/assets/latex.jpg differ diff --git a/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..cf0ba5a Binary files /dev/null and b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..5a359f0 Binary files /dev/null and b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg differ diff --git a/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..7783dc9 Binary files /dev/null and b/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg differ diff --git a/p/dhubeamer/index.html b/p/dhubeamer/index.html new file mode 100644 index 0000000..6181f68 --- /dev/null +++ b/p/dhubeamer/index.html @@ -0,0 +1,706 @@ + + + + +dhuBeamer + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post dhuBeamer + + +
+ + +
+ + + + +
+

+ dhuBeamer +

+ + +

+ Donghua University LaTeX Beamer Template +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 东华大学学术 Beamer 模板 +

本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:

+
    +
  • 设计元素全部来源于东华大学 标识系统,浓浓的东华风。
  • +
  • Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,模板连接)。
  • +
  • 简洁清晰的底部导航区让 Slides 更适合学术汇报使用。
  • +
  • 前后端分离式设计,.tex 文件中只需聚焦内容,.sty 配置文件隔离存放。
  • +
+

+ # + 使用 dhuBeamer 模板 +

+ # + 认识 Beamer 中的元素 +

Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:

+
+img +
+

+ # + 修改 dhuBeamer 模板 +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg b/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg differ diff --git a/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg b/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..d417298 Binary files /dev/null and b/p/elegant-latex/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/elegant-latex/assets/latex.jpg b/p/elegant-latex/assets/latex.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/elegant-latex/assets/latex.jpg differ diff --git a/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..cf0ba5a Binary files /dev/null and b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..5a359f0 Binary files /dev/null and b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg differ diff --git a/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..7783dc9 Binary files /dev/null and b/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg differ diff --git a/p/elegant-latex/assets/snippets.jpg b/p/elegant-latex/assets/snippets.jpg new file mode 100644 index 0000000..3cf0940 Binary files /dev/null and b/p/elegant-latex/assets/snippets.jpg differ diff --git a/p/elegant-latex/assets/windowsInstall.png b/p/elegant-latex/assets/windowsInstall.png new file mode 100644 index 0000000..40725ba Binary files /dev/null and b/p/elegant-latex/assets/windowsInstall.png differ diff --git a/p/elegant-latex/assets/xeCJK.png b/p/elegant-latex/assets/xeCJK.png new file mode 100644 index 0000000..64e488b Binary files /dev/null and b/p/elegant-latex/assets/xeCJK.png differ diff --git a/p/elegant-latex/index.html b/p/elegant-latex/index.html new file mode 100644 index 0000000..ba3811d --- /dev/null +++ b/p/elegant-latex/index.html @@ -0,0 +1,993 @@ + + + + +Elegant LaTeX + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Elegant LaTeX + + +
+ + +
+ + + + +
+

+ Elegant LaTeX +

+ + +

+ How to elegantly use LaTeX to create +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 使用 $\LaTeX$ 优雅地完成创作 +

$\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\LaTeX$ 使用 $\TeX$ 作为它的排版引擎,学习 $\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。

+

+ # + 从安装 $\TeX$ 引擎开始 +

$\TeX$ 引擎类似于 gcc/g++Python,用于编译 $\LaTeX$ 文档。

+

不同平台中 $\TeX$ 的安装方法不尽相同,本文提供:Windows11Linux(Ubuntu 22.04)MacOs(12.7)Windows11-wsl2(Ubuntu22.04)的安装方法。

+

如果你只是想简单体验 $\LaTeX$,可以使用 overleaf 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。

+

+ # + Windows11 +

进入网站tug.org for windows,点击install-tl-windows.exe下载 $\TeX$ 安装器,然后运行安装即可。

+
+img +
+

不过,这种方法需要一直联网安装,网速不好的环境可以直接下载iso镜像进行本地安装。本文给出清华源镜像地址:mirrors.tuna,下载后缀为.iso的文件(只用下载一个)。

+

下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击install-tl-windows.bat文件,使用管理员打开,然后按照指引安装即可。

+

最新版本的安装器会自动添加环境变量,安装完成后打开cmd然后输入:

+
+ +
+
1
+
+
tex --version
+
+
+

若能输出 $\TeX$ 版本信息则安装成功:

+
+ +
+
1
+2
+3
+
+
TeX 3.141592653 (TeX Live 2023/W32TeX)
+kpathsea version 6.3.5
+Copyright 2023 D.E. Knuth.
+
+
+

+ # + Linux(Ubuntu22.04) +

打开终端,然后执行安装命令:

+
+ +
+
1
+2
+3
+
+
sudo apt update
+sudo apt upgrade
+sudo apt install texlive-full
+
+
+

等待安装完成即可,安装完成后执行命令:

+
+ +
+
1
+
+
tex --version
+
+
+

若能输出 $\TeX$ 版本信息则安装成功:

+
+ +
+
1
+2
+3
+
+
TeX 3.141592653 (TeX Live 2023/Debian)
+kpathsea version 6.3.5
+Copyright 2023 D.E. Knuth.
+
+
+

+ # + MacOs(12.7) +

打开终端,然后执行安装命令(推荐安装无窗体版本):

+
+ +
+
1
+
+
brew install mactex-no-gui
+
+
+

等待安装完成即可,安装完成后执行命令:

+
+ +
+
1
+
+
tex --version
+
+
+

若能输出 $\TeX$ 版本信息则安装成功:

+
+ +
+
1
+2
+3
+
+
TeX 3.141592653 (TeX Live 2023)
+kpathsea version 6.3.5
+Copyright 2023 D.E. Knuth.
+
+
+

+ # + Windows11-wsl2(Ubuntu22.04) +

wsl2中安装方式与在Linux中一样。

+

+ # + 找到属于你的编辑器 +

市面上有很多 $\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:

+
    +
  • 全平台通用: +
      +
    • Vs Code:作为地表最强编辑器,Vs Code拥有非常丰富的 $\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。
    • +
    • Jetbrains: 与Vs Code相对应的是Jetbrains系列, 其虽然也有 $\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。
    • +
    • Neovim:如果说Vs Code是编辑器中的王后,那么nvim就是国王。nvim可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。
    • +
    • sublime textnvim固然强大,但其难以上手的特点使得很多人对其望而却步。sublime打破了这个束缚,其界面优雅程度不亚于nvim,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。
    • +
    • TexStudiotexlive默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。
    • +
    +
  • +
  • Windows独占: +
      +
    • Winedt 11:如果不考虑跨平台,那么Winedt 11就是Windows上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。
    • +
    +
  • +
+

综上所述,笔者最推荐Vs Code,但如果你只有Windows平台的使用需求并不介意一点费用的话,请果断购买Winedt 11。同样的,MacOs也拥有独占编辑器,但笔者没用过,在此不做评价。

+

关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。

+

+ # + 一本教程入门 $\LaTeX$ 语法 +

对于所有初学者来说,Ishort-zh-cn都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。

+

如果你已经完成了所有的案例,相信你已经对 $\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。

+

+ # + 打印中文 +

$\LaTeX$ 默认只打印英文,如果没有合理的设置,.tex文件中的中文将无法正确打印。

+

从下图可以看出,目前支持全平台通用的方案只有XeLaTeXLuaTeX。因此,主流方案是一般使用xelatex+ctex编译方案,底层调用xeCJK字符集来实现中文打印。

+
+img +
+

使用时只需在导言区加入,编译器会使用默认字体进行编译:

+
+ +
+
1
+2
+
+
\usepackage[UTF8]{ctex}
+\usepackage{fontspec} % 设置字体
+
+
+

如果要指定字体,则需分别设置font-family

+
+ +
+
1
+2
+3
+4
+5
+
+
\usepackage[UTF8, fontset=none]{ctex}  % 清除默认字体
+\usepackage{fontspec}  % 设置字体
+\setCJKmainfont{SimSun}[AutoFakeBold=true, BoldFont={SimHei}, ItalicFont={KaiTi}]  % 正文字体(宋体,黑体,楷体)
+\setCJKsansfont[AutoFakeBold=3]{KaiTi} % 无衬线字体
+\setCJKmonofont[AutoFakeBold=3]{SimHei} % 等宽字体
+
+
+

同样的,英文也可以自定义字体:

+
+ +
+
1
+
+
\setmainfont{Times New Roman} % 设置英文字体为新罗马体
+
+
+
+

详细配置可以参考:LaTex 中文字体配置指南

+
+

+ # + 打印数学公式 +

你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\LaTeX$,这也是 $\LaTeX$ 为什么被奉为珍宝的原因之一。

+

但是笔者并不建议读者专门花时间来学习如何编写 $\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。

+
    +
  • 在线数学公式生成平台:latexlive可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。
  • +
  • MathType:如果你是Windows用户,那么强烈建议使用MathType来生成数学公式的 $\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。
  • +
  • mathpix:这是一款专为 $\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。
  • +
+

+ # + 绘制表格 +

你一定使用过Excel来绘制表格,但是在 $\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。

+

但不用担心,本文为你介绍开源项目 excel2latex。这是一款Excel插件,可以将你在Excel中绘制的表格自动转译为 $\LaTeX$ 代码。

+

但是这个插件并不是万能的,比如绘制三线表,即使是在Excel中也较为繁琐。因此,最好的处理方式是使用excel2latex插件生成表格主体,然后再自己添加分隔格式。

+

+ # + 插入图片 +

绘制表格和插入图片并称为 $\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。

+

+ # + 学会使用代码片段 +

阅读到这里,相信你已经能够使用 $\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写.sty.cls文件),但这对初学者来说太难了。

+

因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。

+

代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。

+

+ # + Vs Code配置代码片段 +

点击左下角的设置按钮,然后点击设置用户代码片段:

+
+img +
+

在弹出的窗口中输入latex然后选中即可跳转到latex.json文件,我们可以在这里设置我们的代码片段。

+

比如这段设置,保存文件后我们只需要在.tex后缀的文件中输入insertImg然后回车就会自动填充以下代码,并且使用tab来依次输入参数。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+
+
"insertImg": {
+    "prefix": "insertImg",  // 代码片段别名
+    "body": [  // 代码片段主体
+        "\\begin{figure}[H]",
+        "    \\centering",
+        "    \\includegraphics[width=0.8\\textwidth]{$1}",  // 参数1:图片路径
+        "    \\caption{$2}",  // 参数2:图片标题
+        "    \\label{$3}",  // 参数3:图片索引
+        "\\end{figure}$0",
+    ],
+    "description": "insert one img with 0.8 width"
+},
+
+
+

+ # + Winedt 设置代码片段 +

依次点击Option -> Options Interface -> Menus and Toolbar -> Main Menu,修改或添加配置:

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
ITEM="Figure"
+  CAPTION="&Figure"
+  IMAGE="Figure"
+  MACRO="Exe('%b\Menus\Insert\Image.edt');"
+  SHORTCUT="49222::Ctrl+Alt+F"
+  REQ_DOCUMENT=1  
+
+
+

然后就可以使用快捷键Ctrl+Alt+F填充插入图片的代码片段。

+

+ # + 使用模板来专注内容 +

使用 $\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\LaTeX$ 的初衷。

+

因此,常见期刊都会提供对应的 $\LaTeX$ 风格模板和示例,其中主要文件通常为:

+
    +
  • .sty:$\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。
  • +
  • .cls:$\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。
  • +
  • .tex:示例文件,通常会包括论文中会用到的所有样式的示例代码。
  • +
+

阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/feishu-robot/assets/feishu.png b/p/feishu-robot/assets/feishu.png new file mode 100644 index 0000000..703ddf7 Binary files /dev/null and b/p/feishu-robot/assets/feishu.png differ diff --git a/p/feishu-robot/assets/feishuGitlab.png b/p/feishu-robot/assets/feishuGitlab.png new file mode 100644 index 0000000..99bec50 Binary files /dev/null and b/p/feishu-robot/assets/feishuGitlab.png differ diff --git a/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_120x120_fill_box_smart1_3.png b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_120x120_fill_box_smart1_3.png new file mode 100644 index 0000000..9ef1231 Binary files /dev/null and b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_120x120_fill_box_smart1_3.png differ diff --git a/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_1600x0_resize_box_3.png b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_1600x0_resize_box_3.png new file mode 100644 index 0000000..ccd2b79 Binary files /dev/null and b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_1600x0_resize_box_3.png differ diff --git a/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_800x0_resize_box_3.png b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_800x0_resize_box_3.png new file mode 100644 index 0000000..ee9093d Binary files /dev/null and b/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_800x0_resize_box_3.png differ diff --git a/p/feishu-robot/assets/img2.png b/p/feishu-robot/assets/img2.png new file mode 100644 index 0000000..ff02004 Binary files /dev/null and b/p/feishu-robot/assets/img2.png differ diff --git a/p/feishu-robot/assets/img3.png b/p/feishu-robot/assets/img3.png new file mode 100644 index 0000000..227be62 Binary files /dev/null and b/p/feishu-robot/assets/img3.png differ diff --git a/p/feishu-robot/assets/img4.png b/p/feishu-robot/assets/img4.png new file mode 100644 index 0000000..e332fdd Binary files /dev/null and b/p/feishu-robot/assets/img4.png differ diff --git a/p/feishu-robot/assets/img5.png b/p/feishu-robot/assets/img5.png new file mode 100644 index 0000000..83afff8 Binary files /dev/null and b/p/feishu-robot/assets/img5.png differ diff --git a/p/feishu-robot/assets/img6.png b/p/feishu-robot/assets/img6.png new file mode 100644 index 0000000..7bfe754 Binary files /dev/null and b/p/feishu-robot/assets/img6.png differ diff --git a/p/feishu-robot/index.html b/p/feishu-robot/index.html new file mode 100644 index 0000000..a88286c --- /dev/null +++ b/p/feishu-robot/index.html @@ -0,0 +1,771 @@ + + + + +Feishu Robot + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Feishu Robot + + +
+ + +
+ + +
+

+ Feishu Robot +

+ + +

+ Feishu robot automatically monitor Gitlab project and send messages +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 飞书机器人监听 Gitlab 项目 +

在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 push 到 Gitlab。

+

使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。

+
+img +
+

+ # + 准备工作 +

实现这个功能,你需要准备:

+
    +
  • 电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。
  • +
  • 飞书账号:用于创建机器人监听 Gitlab 项目。
  • +
  • 服务器(Linux):用于部署任务,并自动 push 到 Gitlab。
  • +
+

+ # + 创建 Gitlab 账号并新建项目 +

使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/

+

注册完成后新建一个新项目,并添加一个 README.md 文件。

+

+ # + 配置 SSH 秘钥 +

使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
sudo apt update && sudo apt upgrade
+sudo apt install git
+git config --global user.name "name"
+git config --global user.email "email"
+ssh-keygen -C "email" -t rsa
+cat ~/.ssh/id_rsa.pub
+
+
+

其中 name 为 Gitlab 账号的名称,email 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。

+

然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。

+

+ # + Clone 项目并配置任务 +

在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:

+
+img +
+

然后在 Terminal 中找到合适的位置,Clone 项目:

+
+ +
+
1
+
+
git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git
+
+
+

编写你的任务脚本(shellPython 等),然后确保其能正常运行。

+

+ # + 新建飞书指令 +

使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:

+
+img +
+

然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:

+
+img +
+
+img +
+

点击完成后,启用机器人即可。

+

+ # + 创建定时任务 +

首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。

+

本文推荐使用 systemctlsystemdtimer 来创建定时任务。

+

+ # + 创建定时器配置 +

创建一个 task.timer 文件:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
[Unit]
+Description=Your Timer Description
+
+[Timer]
+# OnCalendar设置定时规则,这里是每天10点
+OnCalendar=*-*-* 10:00:00
+
+[Install]
+WantedBy=timers.target
+
+
+

+ # + 创建定时器服务 +

创建一个 task.service 文件(默认使用 root 用户,建议指定 User 用户):

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+
+
[Unit]
+Description=Your Service Description
+
+[Service]
+Type=simple
+User=user
+# 建议使用 shell 脚本
+ExecStart=/path/to/your/script.sh
+
+
+

+ # + 添加定时任务 +

+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
sudo cp your_timer_name.timer /etc/systemd/system/
+sudo cp your_service_name.service /etc/systemd/system/
+
+sudo systemctl daemon-reload
+
+# 手动启用任务,测试任务是否正常运行
+sudo systemctl start your_timer_name.service
+# 输出任务运行日志
+sudo journalctl -u your_timer_name.service
+
+
+

任务运行成功后,使用以下命令启用定时任务:

+
+ +
+
1
+2
+
+
sudo systemctl enable your_timer_name.timer
+sudo systemctl list-timers
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git "a/p/happy-geek-prd/assets/PRD \347\273\223\346\236\204.png" "b/p/happy-geek-prd/assets/PRD \347\273\223\346\236\204.png" new file mode 100644 index 0000000..6f2d27f Binary files /dev/null and "b/p/happy-geek-prd/assets/PRD \347\273\223\346\236\204.png" differ diff --git a/p/happy-geek-prd/assets/prd.jpg b/p/happy-geek-prd/assets/prd.jpg new file mode 100644 index 0000000..7d36da0 Binary files /dev/null and b/p/happy-geek-prd/assets/prd.jpg differ diff --git a/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_120x120_fill_q75_box_smart1.jpg b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..d524551 Binary files /dev/null and b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_1600x0_resize_q75_box.jpg b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..f46ef3c Binary files /dev/null and b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_1600x0_resize_q75_box.jpg differ diff --git a/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_800x0_resize_q75_box.jpg b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..578e244 Binary files /dev/null and b/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_800x0_resize_q75_box.jpg differ diff --git a/p/happy-geek-prd/index.html b/p/happy-geek-prd/index.html new file mode 100644 index 0000000..4478879 --- /dev/null +++ b/p/happy-geek-prd/index.html @@ -0,0 +1,649 @@ + + + + +Happy Geek PRD + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Happy Geek PRD + + +
+ + +
+ + +
+

+ Happy Geek PRD +

+ + +

+ Make PRD that Geek happy to read +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 创作极客喜欢的 PRD +

产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。

+

产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。

+

在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:

+
    +
  • 产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。
  • +
  • 在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。
  • +
  • 开发完成后,项目结果远远不如预期。
  • +
+

因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。

+
+img +
+

+ # + 产品简介 +

+ # + 行业概要 +

+ # + 发行版本 +

+ # + 排期表 +

+ # + 产品设计 +

+ # + 实体关系图 +

+ # + 用户角色权限表 +

+ + + + + + + + + + + + + + +
functionuser1user2
func 01getget
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/linked-list/assets/dataStructures.jpg b/p/linked-list/assets/dataStructures.jpg new file mode 100644 index 0000000..b231665 Binary files /dev/null and b/p/linked-list/assets/dataStructures.jpg differ diff --git a/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..4ea68f3 Binary files /dev/null and b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_1600x0_resize_q75_box.jpg b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..20ccf8e Binary files /dev/null and b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_1600x0_resize_q75_box.jpg differ diff --git a/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_800x0_resize_q75_box.jpg b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..d8f27b2 Binary files /dev/null and b/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_800x0_resize_q75_box.jpg differ diff --git a/p/linked-list/index.html b/p/linked-list/index.html new file mode 100644 index 0000000..6608d4c --- /dev/null +++ b/p/linked-list/index.html @@ -0,0 +1,628 @@ + + + + +Linked List + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Linked List + + +
+ + +
+ + + + +
+

+ Linked List +

+ + +

+ Data Structure: Linked List (CPP and Python) +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 数据结构:链表 +

+ # + 线性表 +

线性表是具有相同数据类型的 $n(n \ge 0)$ 个数据元素有限序列,其中 $n$ 为表长,当 $n = 0$ 时线性表是一个空表。若用 $L$ 命名线性表,则其一般表示为:

+

$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$

+

几个概念:

+
    +
  • $a_i$ 是线性表中的“第 $i$ 个”元素线性表中的位序,位序从 $1$ 开始,数组下标从 $0$ 开始。
  • +
  • $a_1$ 是表头元素,$a_n$ 是表尾元素
  • +
+

除第一个元素外,每个元素有且仅有一个直接前驱;除最后一个元素外,每个元素有且仅有一个直接后继

+

线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。

+

+ # + 链表 +

链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。

+

这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。

+

链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。

+

+ # + 单向链表 +

单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。

+
+img +
+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg b/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62.jpg differ diff --git a/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg b/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..d417298 Binary files /dev/null and b/p/slides/assets/latex.572dd01bfb9fd432fce675b3a4f87c62_hu1e4838b57430da79d946f8d6b8ca0bae_30212_250x150_fill_q75_box_smart1.jpg differ diff --git a/p/slides/assets/latex.jpg b/p/slides/assets/latex.jpg new file mode 100644 index 0000000..eeb4e61 Binary files /dev/null and b/p/slides/assets/latex.jpg differ diff --git a/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..cf0ba5a Binary files /dev/null and b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..5a359f0 Binary files /dev/null and b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_1600x0_resize_q75_box.jpg differ diff --git a/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..7783dc9 Binary files /dev/null and b/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_800x0_resize_q75_box.jpg differ diff --git a/p/slides/index.html b/p/slides/index.html new file mode 100644 index 0000000..fdbbe21 --- /dev/null +++ b/p/slides/index.html @@ -0,0 +1,644 @@ + + + + +Slides + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + Featured image of post Slides + + +
+ + +
+ + + + +
+

+ Slides +

+ + +

+ Draw simple and elegant Slides By LaTeX +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 使用 $\LaTeX$ 绘制 Slides +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/ssh-key-agent/assets/gitssh.png b/p/ssh-key-agent/assets/gitssh.png new file mode 100644 index 0000000..daf4e88 Binary files /dev/null and b/p/ssh-key-agent/assets/gitssh.png differ diff --git a/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_120x120_fill_box_smart1_3.png b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_120x120_fill_box_smart1_3.png new file mode 100644 index 0000000..615042d Binary files /dev/null and b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_120x120_fill_box_smart1_3.png differ diff --git a/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_1600x0_resize_box_3.png b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_1600x0_resize_box_3.png new file mode 100644 index 0000000..d46d3bd Binary files /dev/null and b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_1600x0_resize_box_3.png differ diff --git a/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_800x0_resize_box_3.png b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_800x0_resize_box_3.png new file mode 100644 index 0000000..5d8d152 Binary files /dev/null and b/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_800x0_resize_box_3.png differ diff --git a/p/ssh-key-agent/index.html b/p/ssh-key-agent/index.html new file mode 100644 index 0000000..2d28188 --- /dev/null +++ b/p/ssh-key-agent/index.html @@ -0,0 +1,664 @@ + + + + +SSH Key Agent + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post SSH Key Agent + + +
+ + +
+ + +
+

+ SSH Key Agent +

+ + +

+ Generate multiple ssh keys on one device for different git platforms +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 为不同的 Git 服务生成不同的 SSH Key +

在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:

+
    +
  • 我有多个 Git 平台(Github, Gitlab, …)的账号,每个平台的账号都需要一个 SSH Key。
  • +
  • 在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。
  • +
  • 不同平台的账号,可能使用相同邮箱进行注册。
  • +
+

这时,如何生成和管理 SSH Key 成为一个问题。

+

+ # + 设置局部 Git 信息 +

在不同文件夹下,都可以设置不同的局部 Git 信息:

+
+ +
+
1
+2
+
+
git config user.name "name"
+git config user.email "email"
+
+
+

+ # + 开启 SSH Agent +

开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:

+
+ +
+
1
+
+
eval "$(ssh-agent -s)"
+
+
+

+ # + 生成不同的 SSH Key +

指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:

+
+ +
+
1
+
+
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_name -C "your_email@example.com"
+
+
+

将生成的私钥添加进代理:

+
+ +
+
1
+2
+3
+4
+
+
ssh-add ~/.ssh/id_rsa_name
+
+# MacOS
+ssh-add --apple-use-keychain ~/.ssh/id_rsa_name
+
+
+

+ # + 将公钥添加到 Git 平台 +

首先复制 SSH 公钥:

+
+ +
+
1
+
+
cat ~/.ssh/id_rsa_name.pub
+
+
+

然后到 Git 平台中,添加该公钥。

+ +
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/test/index.html b/p/test/index.html new file mode 100644 index 0000000..6ae6a0e --- /dev/null +++ b/p/test/index.html @@ -0,0 +1,654 @@ + + + + +Test + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Featured image of post Test + + +
+ + +
+ + + + +
+

+ Test +

+ + +

+ test page +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + Test page +

+ # + picture +

nord

+

+ # + math +

$$ +\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } +$$

+

+ # + code +

+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
#include "bits/stdc++.h"
+
+using namespace std;
+
+int main() {
+    cout << "Hello World!" << endl;
+
+    return 0;
+}
+
+
+

+ # + Test $\LaTeX$ +

+ # + $\frac{2}{3}$ +

+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/p/test/nord.jpg b/p/test/nord.jpg new file mode 100644 index 0000000..9776f86 Binary files /dev/null and b/p/test/nord.jpg differ diff --git a/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg new file mode 100644 index 0000000..6d79ce5 Binary files /dev/null and b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg differ diff --git a/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2b077ad Binary files /dev/null and b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg differ diff --git a/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1600x0_resize_q75_box.jpg b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1600x0_resize_q75_box.jpg new file mode 100644 index 0000000..9dacd10 Binary files /dev/null and b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1600x0_resize_q75_box.jpg differ diff --git a/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg new file mode 100644 index 0000000..6d17c34 Binary files /dev/null and b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg differ diff --git a/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_800x0_resize_q75_box.jpg b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_800x0_resize_q75_box.jpg new file mode 100644 index 0000000..c53336b Binary files /dev/null and b/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_800x0_resize_q75_box.jpg differ diff --git a/p/threads/index.html b/p/threads/index.html new file mode 100644 index 0000000..b5ccf4b --- /dev/null +++ b/p/threads/index.html @@ -0,0 +1,829 @@ + + + + +Threads + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ +
+ + +
+

+ Threads +

+ + +

+ MulThread and Thread Pool +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + +

+ # + 线程管理 +

在程序开发中,很多时候都是程序都是串行处理,这没有什么问题。然而,在某些重复工作较多,且性能要求较高的场景,串行处理所需时间往往过于漫长。

+

因此,合理地使用线程管理有助于我们程序的更好运行。但是请注意,不是一味地使用多线程或线程池就一定是好的,适合运行场景的处理方式才是最好的。

+

+ # + 单线程 +

在本文中,我们考虑这样一个场景:有一个非常耗时的计算函数,其计算一次需要 time 秒。

+
+ +
+
1
+2
+3
+4
+5
+6
+
+
void waitTime(int time) {
+    std::chrono::seconds duration(time);
+    std::this_thread::sleep_for(duration);
+    
+    std::cout << "wait for " << time << " seconds" << std::endl;
+}
+
+
+

按照常规的做法,我们串行地对批量任务进行处理:

+
+ +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
+
int main() {
+    std::vector<int> todoList(10, 3);
+
+    for (auto time : todoList) {
+        waitTime(time);
+    }
+
+    return 0;
+}
+
+
+

可以预见,这种处理方法会非常耗时。

+

+ # + 多线程 +

为了加速程序运行和处理的速度,我们可以使用多线程来并行处理。多线程的思想是:先将要进行的任务放入队列中,然后让这些任务同时运行,最终实现加速程序运行的效果。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
+
void mulTreads(std::vector<int> todoList, const int MaxThreads) {
+    std::vector<std::unique_ptr<std::thread>> fetchingThreads;
+
+    for (int i = 0, l = todoList.size(); i < l; i ++) {
+        fetchingThreads.emplace_back(std::make_unique<std::thread>(waitTime, todoList[i]));
+
+        if (fetchingThreads.size() >= MaxThreads) {
+            fetchingThreads.front()->join();
+            fetchingThreads.erase(fetchingThreads.begin());
+        }
+    }
+
+    for (auto &threadPtr : fetchingThreads) {
+        threadPtr->join();
+    }
+}
+
+
+int main() {
+    std::vector<int> todoList(10, 3);
+    mulTreads(todoList, 7);
+
+    return 0;
+}
+
+
+

+ # + 线程池 +

多线程虽好,但是频繁地创建和删除线程,同样会造成时间和空间的浪费。因此,线程池出现了,在每次任务完成之后,保留现有线程并继续处理下一个任务。

+
+ +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+
+
class ThreadPool {
+public:
+    ThreadPool(size_t numThreads) : stop(false) {
+        for (size_t i = 0; i < numThreads; ++i) {
+            workers.emplace_back([this] {
+                while (true) {
+                    std::function<void()> task;
+
+                    {
+                        std::unique_lock<std::mutex> lock(queueMutex);
+                        condition.wait(lock, [this] { return stop || !tasks.empty(); });
+                        if (stop && tasks.empty())
+                            return;
+                        task = std::move(tasks.front());
+                        tasks.pop();
+                    }
+
+                    task();
+                }
+            });
+        }
+    }
+
+    // 添加任务到线程池
+    template<class F>
+    void enqueue(F&& task) {
+        {
+            std::unique_lock<std::mutex> lock(queueMutex);
+            tasks.emplace(std::forward<F>(task));
+        }
+        condition.notify_one();
+    }
+
+    ~ThreadPool() {
+        {
+            std::unique_lock<std::mutex> lock(queueMutex);
+            stop = true;
+        }
+        condition.notify_all();
+        for (std::thread &worker : workers)
+            worker.join();
+    }
+
+private:
+    std::vector<std::thread> workers;
+    std::queue<std::function<void()>> tasks;
+
+    std::mutex queueMutex;
+    std::condition_variable condition;
+    bool stop;
+};
+
+
+int main() {
+    const int numThreads = 7;
+    ThreadPool pool(numThreads); // 创建包含7个线程的线程池
+
+    std::vector<int> todoList(10, 3);
+
+    // 向线程池中添加任务
+    for (const int& time : todoList) {
+        pool.enqueue([time] {
+            waitTime(time);
+        });
+    }
+
+    return 0;
+}
+
+
+
+
+ + +
+ + + + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + + + + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/page/1/index.html b/page/1/index.html new file mode 100644 index 0000000..ffc6c82 --- /dev/null +++ b/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/ + + + + + + diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..80cfb36 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,1093 @@ + + + + + +Pager 2 - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+
+ + Featured image of post SSH Key Agent + + +
+ + +
+ + +
+

+ SSH Key Agent +

+ + +

+ Generate multiple ssh keys on one device for different git platforms +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Feishu Robot + + +
+ + +
+ + +
+

+ Feishu Robot +

+ + +

+ Feishu robot automatically monitor Gitlab project and send messages +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post CppJson Ch01 + + +
+ + +
+ + + + +
+

+ CppJson Ch01 +

+ + +

+ My CppJson tutorial ch01: parse null and bool +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch09 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch09 +

+ + +

+ Sequence Container +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch08 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch08 +

+ + +

+ IO library +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch07 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch07 +

+ + +

+ Class +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch06 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch06 +

+ + +

+ Function +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ +
+
+ + + + + diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 0000000..3f0c83b --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,1101 @@ + + + + + +Pager 3 - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+
+ + Featured image of post C++ Primer Ch04 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch04 +

+ + +

+ Expression +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch03 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch03 +

+ + +

+ Strings, vectors and arrays +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post C++ Primer Ch02 + + +
+ + +
+ + + + +
+

+ C++ Primer Ch02 +

+ + +

+ Variables and basic types +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Slides + + +
+ + +
+ + + + +
+

+ Slides +

+ + +

+ Draw simple and elegant Slides By LaTeX +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post dhuBachelor + + +
+ + +
+ + + + +
+

+ dhuBachelor +

+ + +

+ Donghua University Bachelor's Thesis LaTeX Template User Manual +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Elegant LaTeX + + +
+ + +
+ + + + +
+

+ Elegant LaTeX +

+ + +

+ How to elegantly use LaTeX to create +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ + +
+
+
+ + Featured image of post Camera Calibration + + +
+ + +
+ + +
+

+ Camera Calibration +

+ + +

+ The principle and specific implementation of camera calibration +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ +
+
+ + + + + diff --git a/page/4/index.html b/page/4/index.html new file mode 100644 index 0000000..353cac6 --- /dev/null +++ b/page/4/index.html @@ -0,0 +1,614 @@ + + + + + +Pager 4 - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+
+
+ + Featured image of post Test + + +
+ + +
+ + + + +
+

+ Test +

+ + +

+ test page +

+ +
+ + + + + +
+ +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+
+ +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ +
+
+ + + + + diff --git a/page/index.html b/page/index.html new file mode 100644 index 0000000..61b98b2 --- /dev/null +++ b/page/index.html @@ -0,0 +1,571 @@ + + + + +Pages + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

5 pages

+

Pages

+ +
+
+
+ +
+ + + + + + + + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/page/index.xml b/page/index.xml new file mode 100644 index 0000000..56195e6 --- /dev/null +++ b/page/index.xml @@ -0,0 +1,51 @@ + + + + Pages on 3000ye's Blog + https://3000ye.com/page/ + Recent content in Pages on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 06 Mar 2022 00:00:00 +0000 + Archives + https://3000ye.com/archives/ + Sun, 06 Mar 2022 00:00:00 +0000 + + https://3000ye.com/archives/ + + + + About + https://3000ye.com/about/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/about/ + + + + Friends + https://3000ye.com/friends/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/friends/ + + + + Search + https://3000ye.com/search/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/search/ + + + + Stars + https://3000ye.com/stars/ + Mon, 01 Jan 0001 00:00:00 +0000 + + https://3000ye.com/stars/ + + + + + diff --git a/page/page/1/index.html b/page/page/1/index.html new file mode 100644 index 0000000..8b4d00d --- /dev/null +++ b/page/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/page/ + + + + + + diff --git a/post/index.html b/post/index.html new file mode 100644 index 0000000..c62d8a3 --- /dev/null +++ b/post/index.html @@ -0,0 +1,673 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

22 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/post/index.xml b/post/index.xml new file mode 100644 index 0000000..98d7100 --- /dev/null +++ b/post/index.xml @@ -0,0 +1,4722 @@ + + + + Posts on 3000ye's Blog + https://3000ye.com/post/ + Recent content in Posts on 3000ye's Blog + Hugo -- gohugo.io + en-us + Thu, 21 Mar 2024 23:12:32 +0800 + Clear C++ Ch09: Function application + https://3000ye.com/p/clear-c-ch09-function-application/ + Thu, 21 Mar 2024 23:12:32 +0800 + + https://3000ye.com/p/clear-c-ch09-function-application/ + <img src="https://3000ye.com/p/clear-c-ch09-function-application/assets/clearcpp.jpg" alt="Featured image of post Clear C++ Ch09: Function application" /><h1 id="函数的应用"> + <a href="#%e5%87%bd%e6%95%b0%e7%9a%84%e5%ba%94%e7%94%a8">#</a> + 函数的应用 +</h1><h2 id="函数模板"> + <a href="#%e5%87%bd%e6%95%b0%e6%a8%a1%e6%9d%bf">#</a> + 函数模板 +</h2> + + + Threads + https://3000ye.com/p/threads/ + Mon, 18 Mar 2024 21:06:19 +0800 + + https://3000ye.com/p/threads/ + <h1 id="线程管理"> + <a href="#%e7%ba%bf%e7%a8%8b%e7%ae%a1%e7%90%86">#</a> + 线程管理 +</h1><p>在程序开发中,很多时候都是程序都是串行处理,这没有什么问题。然而,在某些重复工作较多,且性能要求较高的场景,串行处理所需时间往往过于漫长。</p> +<p>因此,合理地使用线程管理有助于我们程序的更好运行。但是请注意,不是一味地使用多线程或线程池就一定是好的,适合运行场景的处理方式才是最好的。</p> +<h2 id="单线程"> + <a href="#%e5%8d%95%e7%ba%bf%e7%a8%8b">#</a> + 单线程 +</h2><p>在本文中,我们考虑这样一个场景:有一个非常耗时的计算函数,其计算一次需要 <code>time</code> 秒。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">waitTime</span><span class="p">(</span><span class="kt">int</span> <span class="n">time</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">seconds</span> <span class="n">duration</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">duration</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;wait for &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">time</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; seconds&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>按照常规的做法,我们串行地对批量任务进行处理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以预见,这种处理方法会非常耗时。</p> +<h2 id="多线程"> + <a href="#%e5%a4%9a%e7%ba%bf%e7%a8%8b">#</a> + 多线程 +</h2><p>为了加速程序运行和处理的速度,我们可以使用多线程来并行处理。多线程的思想是:先将要进行的任务放入队列中,然后让这些任务同时运行,最终实现加速程序运行的效果。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mulTreads</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">,</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;&gt;</span> <span class="n">fetchingThreads</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">l</span> <span class="o">=</span> <span class="n">todoList</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">l</span><span class="p">;</span> <span class="n">i</span> <span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span><span class="p">(</span><span class="n">waitTime</span><span class="p">,</span> <span class="n">todoList</span><span class="p">[</span><span class="n">i</span><span class="p">]));</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">front</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">begin</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">threadPtr</span> <span class="p">:</span> <span class="n">fetchingThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">threadPtr</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">mulTreads</span><span class="p">(</span><span class="n">todoList</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="线程池"> + <a href="#%e7%ba%bf%e7%a8%8b%e6%b1%a0">#</a> + 线程池 +</h2><p>多线程虽好,但是频繁地创建和删除线程,同样会造成时间和空间的浪费。因此,线程池出现了,在每次任务完成之后,保留现有线程并继续处理下一个任务。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ThreadPool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span><span class="p">(</span><span class="n">size_t</span> <span class="n">numThreads</span><span class="p">)</span> <span class="o">:</span> <span class="n">stop</span><span class="p">(</span><span class="nb">false</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numThreads</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">workers</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">([</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;</span> <span class="n">task</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">wait</span><span class="p">(</span><span class="n">lock</span><span class="p">,</span> <span class="p">[</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stop</span> <span class="o">||</span> <span class="o">!</span><span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">();</span> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">stop</span> <span class="o">&amp;&amp;</span> <span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">task</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">tasks</span><span class="p">.</span><span class="n">front</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">task</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 添加任务到线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">F</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="n">enqueue</span><span class="p">(</span><span class="n">F</span><span class="o">&amp;&amp;</span> <span class="n">task</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">emplace</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span><span class="p">(</span><span class="n">task</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_one</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">~</span><span class="n">ThreadPool</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">stop</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_all</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span> <span class="o">&amp;</span><span class="nl">worker</span> <span class="p">:</span> <span class="n">workers</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">worker</span><span class="p">.</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span> <span class="n">workers</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;&gt;</span> <span class="n">tasks</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">mutex</span> <span class="n">queueMutex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">condition_variable</span> <span class="n">condition</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">stop</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">int</span> <span class="n">numThreads</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span> <span class="n">pool</span><span class="p">(</span><span class="n">numThreads</span><span class="p">);</span> <span class="c1">// 创建包含7个线程的线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 向线程池中添加任务 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">pool</span><span class="p">.</span><span class="n">enqueue</span><span class="p">([</span><span class="n">time</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Linked List + https://3000ye.com/p/linked-list/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/p/linked-list/ + <img src="https://3000ye.com/p/linked-list/assets/dataStructures.jpg" alt="Featured image of post Linked List" /><h1 id="数据结构链表"> + <a href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e9%93%be%e8%a1%a8">#</a> + 数据结构:链表 +</h1><h2 id="线性表"> + <a href="#%e7%ba%bf%e6%80%a7%e8%a1%a8">#</a> + 线性表 +</h2><p>线性表是具有<strong>相同</strong>数据类型的 $n(n \ge 0)$ 个<strong>数据元素</strong>的<strong>有限序列</strong>,其中 $n$ 为<strong>表长</strong>,当 $n = 0$ 时线性表是一个<strong>空表</strong>。若用 $L$ 命名线性表,则其一般表示为:</p> +<p>$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$</p> +<p>几个概念:</p> +<ul> +<li>$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的<strong>位序</strong>,位序从 $1$ 开始,数组下标从 $0$ 开始。</li> +<li>$a_1$ 是<strong>表头元素</strong>,$a_n$ 是<strong>表尾元素</strong>。</li> +</ul> +<p>除第一个元素外,每个元素有且仅有一个<strong>直接前驱</strong>;除最后一个元素外,每个元素有且仅有一个<strong>直接后继</strong>。</p> +<p>线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。</p> +<h2 id="链表"> + <a href="#%e9%93%be%e8%a1%a8">#</a> + 链表 +</h2><p>链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。</p> +<p>这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。</p> +<p>链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。</p> +<h3 id="单向链表"> + <a href="#%e5%8d%95%e5%90%91%e9%93%be%e8%a1%a8">#</a> + 单向链表 +</h3><p>单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。</p> +<div style='display: flex; justify-content: center;'> +<img src='https://oi-wiki.org/ds/images/list.svg' alt='img' style='zoom:100%;' /> +</div> + + + + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + CppJson Ch02 + https://3000ye.com/p/cppjson-ch02/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/p/cppjson-ch02/ + <img src="https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch02" /><h1 id="cppjson-第二章节number-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%ba%8c%e7%ab%a0%e8%8a%82number-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第二章节:<code>number</code> 值解析 +</h1> + + + 2023 Summary + https://3000ye.com/p/2023-summary/ + Sun, 31 Dec 2023 15:40:26 +0800 + + https://3000ye.com/p/2023-summary/ + <img src="https://3000ye.com/p/2023-summary/assets/wakatime.png" alt="Featured image of post 2023 Summary" /><h1 id="用桌面变化来总结-2023"> + <a href="#%e7%94%a8%e6%a1%8c%e9%9d%a2%e5%8f%98%e5%8c%96%e6%9d%a5%e6%80%bb%e7%bb%93-2023">#</a> + 用桌面变化来总结 2023 +</h1><h2 id="3月16日我有-5-块屏幕"> + <a href="#3%e6%9c%8816%e6%97%a5%e6%88%91%e6%9c%89-5-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 3月16日,我有 5 块屏幕 +</h2><p>这段时间是我同时使用屏幕数量最多的时候,主屏幕写代码,副屏幕看网页;surface 和工控屏做直播推流;iPad 负责预览直播画面:</p> +<ul> +<li>主屏幕(中间):27 寸 2k,副屏幕(最左):15.6 寸 2k</li> +<li>surface(最右),工控屏(最小):7 寸 1080p</li> +<li>iPad(非统一背景)</li> +</ul> +<p>几乎每天都在宿舍,买了本《明解 C++》一边学语言一边学算法。虽然算法已经忘得差不多了,但坚实的 <code>C++</code> 基础对我帮助很大,甚至我现在实习的主要内容就是 <code>C++</code> 开发。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/03-16.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="5月10日我有-2-块屏幕"> + <a href="#5%e6%9c%8810%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 5月10日,我有 2 块屏幕 +</h2><p>学完了 <code>C++</code> 语言基础后,我开始意识到数据结构对编程的重要性。同时也有为考研做准备的打算,我开始每天往返图书馆,刷王道考研的 CS408 课程。</p> +<p>为了方便往返图书馆并尽可能满足我多屏幕的需求,买了一台可以 180 度开合的笔记本(ThinkBook 14)和一个立式支架,搭配 ThinkPad 的 GaN 100W 充电器和雷电线,只需 2 根线完成所有的连接。这样的好处是可以上面写代码下面看课程,同时使用外接键盘不会很突兀。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/05-10.jpg' alt='img' style='zoom:15%;' /> +</div> +<h2 id="6月18日我有-2-块屏幕"> + <a href="#6%e6%9c%8818%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 6月18日,我有 2 块屏幕 +</h2><p>春季学期结束后,我也放弃了考研的打算,开始往机器学习和 Pytorch 的方向学习。翻出以前买的西瓜书和南瓜书,对着 Github 上的开源笔记边看边学。由于宿舍桌子太窄了,放三块屏幕过于拥挤,于是将 15.6 寸的副屏收了起来。</p> +<p>趁着 618 换了罗技的 master 3s 鼠标,手感确实比雷蛇的 click Pro 好很多。同时每天都坚持录视频,之前买的麦克风也派上了用场。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/06-18.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="7月19日我有-1-块屏幕"> + <a href="#7%e6%9c%8819%e6%97%a5%e6%88%91%e6%9c%89-1-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 7月19日,我有 1 块屏幕 +</h2><p>随着 Pytorch 学习的深入,笔记本的核显已经无法满足需求。而此时矿潮已经开始逐渐褪去,随着 4090 的发布老显卡的价格开始走低,索性在咸鱼上淘了一张服务器版的 2080Ti:</p> +<ul> +<li>300A 核心,三星显存</li> +<li>纯铜涡轮散热,尾部供电</li> +</ul> +<p>其 11G 的显存足以满足入门需求,同时后期还可以加焊 22G 显存,是一张性能和成长空间都不错的显卡。</p> +<p>同时为了最大限度使用显卡,直接把机器刷成了 Ubuntu server,将之前的监控屏用来监控显卡状态。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/07-19.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="7月20日我有-3-块屏幕"> + <a href="#7%e6%9c%8820%e6%97%a5%e6%88%91%e6%9c%89-3-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 7月20日,我有 3 块屏幕 +</h2><p>搞深度学习,真的是屏幕越多越好,有太多的资料和数据集看不过来:</p> +<ul> +<li>主显示器用来看资料</li> +<li>副显示器用来看数据集(竖直摆放)</li> +<li>笔记本屏幕用来写代码</li> +</ul> +<div style='display: flex; justify-content: center;'> +<img src='assets/07-20.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="9月6日我有-1-块屏幕"> + <a href="#9%e6%9c%886%e6%97%a5%e6%88%91%e6%9c%89-1-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 9月6日,我有 1 块屏幕 +</h2><p>秋季学期重回宿舍后,开始迷上了 Minecraft 和泡茶,一杯茶一个种子就是一天。这段时间是最放松的时间,每天都在搭方块。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/09-06.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="10月19日我有-3-块屏幕"> + <a href="#10%e6%9c%8819%e6%97%a5%e6%88%91%e6%9c%89-3-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 10月19日,我有 3 块屏幕 +</h2><p>开始实习后,第一次用上了 MacOS。但尴尬的是,工作用的编译和测试环境还是 Windows,所以基本上都是在 Mac 上面通过 SSH 远程 Windows 进行开发(VsCode 大法好):</p> +<ul> +<li>开发环境:MacBookPro(左边)和主屏幕:28 寸 4k</li> +<li>编译环境:dell Windows 主机(无屏幕)</li> +<li>测试环境:hp | ThinkPad(右边)</li> +</ul> +<p>同时,我也负责服务器上的容器部署等相关工作,所以我有幸同时使用 3 大主流操作系统。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/10-19.jpg' alt='img' style='zoom:50%;' /> +</div> +<h2 id="12月31日我有-2-块屏幕"> + <a href="#12%e6%9c%8831%e6%97%a5%e6%88%91%e6%9c%89-2-%e5%9d%97%e5%b1%8f%e5%b9%95">#</a> + 12月31日,我有 2 块屏幕 +</h2><p>适应实习的节奏后,晚上回宿舍的几个小时是每天最舒服的,因为可以不用管公司的事情专注自己喜欢的项目。</p> +<p>趁着放假买了一块树莓派 4B 在宿舍折腾,初步搭了 Seafile 和 Gitlab。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/12-31.jpg' alt='img' style='zoom:50%;' /> +</div> + + + Happy Geek PRD + https://3000ye.com/p/happy-geek-prd/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/p/happy-geek-prd/ + <img src="https://3000ye.com/p/happy-geek-prd/assets/prd.jpg" alt="Featured image of post Happy Geek PRD" /><h1 id="创作极客喜欢的-prd"> + <a href="#%e5%88%9b%e4%bd%9c%e6%9e%81%e5%ae%a2%e5%96%9c%e6%ac%a2%e7%9a%84-prd">#</a> + 创作极客喜欢的 PRD +</h1><p>产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。</p> +<p>产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。</p> +<p>在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:</p> +<ul> +<li>产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。</li> +<li>在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。</li> +<li>开发完成后,项目结果远远不如预期。</li> +</ul> +<p>因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/PRD 结构.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="产品简介"> + <a href="#%e4%ba%a7%e5%93%81%e7%ae%80%e4%bb%8b">#</a> + 产品简介 +</h2><h2 id="行业概要"> + <a href="#%e8%a1%8c%e4%b8%9a%e6%a6%82%e8%a6%81">#</a> + 行业概要 +</h2><h2 id="发行版本"> + <a href="#%e5%8f%91%e8%a1%8c%e7%89%88%e6%9c%ac">#</a> + 发行版本 +</h2><h3 id="排期表"> + <a href="#%e6%8e%92%e6%9c%9f%e8%a1%a8">#</a> + 排期表 +</h3><h3 id="产品设计"> + <a href="#%e4%ba%a7%e5%93%81%e8%ae%be%e8%ae%a1">#</a> + 产品设计 +</h3><h4 id="实体关系图"> + <a href="#%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb%e5%9b%be">#</a> + 实体关系图 +</h4><h4 id="用户角色权限表"> + <a href="#%e7%94%a8%e6%88%b7%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e8%a1%a8">#</a> + 用户角色权限表 +</h4><table> +<thead> +<tr> +<th>function</th> +<th>user1</th> +<th>user2</th> +</tr> +</thead> +<tbody> +<tr> +<td>func 01</td> +<td>get</td> +<td>get</td> +</tr> +</tbody> +</table> + + + + SSH Key Agent + https://3000ye.com/p/ssh-key-agent/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/p/ssh-key-agent/ + <img src="https://3000ye.com/p/ssh-key-agent/assets/gitssh.png" alt="Featured image of post SSH Key Agent" /><h1 id="为不同的-git-服务生成不同的-ssh-key"> + <a href="#%e4%b8%ba%e4%b8%8d%e5%90%8c%e7%9a%84-git-%e6%9c%8d%e5%8a%a1%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 为不同的 Git 服务生成不同的 SSH Key +</h1><p>在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:</p> +<ul> +<li>我有多个 Git 平台(Github, Gitlab, &hellip;)的账号,每个平台的账号都需要一个 SSH Key。</li> +<li>在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。</li> +<li>不同平台的账号,可能使用相同邮箱进行注册。</li> +</ul> +<p>这时,如何生成和管理 SSH Key 成为一个问题。</p> +<h2 id="设置局部-git-信息"> + <a href="#%e8%ae%be%e7%bd%ae%e5%b1%80%e9%83%a8-git-%e4%bf%a1%e6%81%af">#</a> + 设置局部 Git 信息 +</h2><p>在不同文件夹下,都可以设置不同的局部 Git 信息:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config user.email <span class="s2">&#34;email&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="开启-ssh-agent"> + <a href="#%e5%bc%80%e5%90%af-ssh-agent">#</a> + 开启 SSH Agent +</h2><p>开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="生成不同的-ssh-key"> + <a href="#%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 生成不同的 SSH Key +</h2><p>指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -f ~/.ssh/id_rsa_name -C <span class="s2">&#34;your_email@example.com&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>将生成的私钥添加进代理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-add ~/.ssh/id_rsa_name +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># MacOS</span> +</span></span><span class="line"><span class="cl">ssh-add --apple-use-keychain ~/.ssh/id_rsa_name +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="将公钥添加到-git-平台"> + <a href="#%e5%b0%86%e5%85%ac%e9%92%a5%e6%b7%bb%e5%8a%a0%e5%88%b0-git-%e5%b9%b3%e5%8f%b0">#</a> + 将公钥添加到 Git 平台 +</h2><p>首先复制 SSH 公钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat ~/.ssh/id_rsa_name.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后到 Git 平台中,添加该公钥。</p> + + + + Feishu Robot + https://3000ye.com/p/feishu-robot/ + Fri, 22 Dec 2023 13:47:39 +0800 + + https://3000ye.com/p/feishu-robot/ + <img src="https://3000ye.com/p/feishu-robot/assets/feishuGitlab.png" alt="Featured image of post Feishu Robot" /><h1 id="飞书机器人监听-gitlab-项目"> + <a href="#%e9%a3%9e%e4%b9%a6%e6%9c%ba%e5%99%a8%e4%ba%ba%e7%9b%91%e5%90%ac-gitlab-%e9%a1%b9%e7%9b%ae">#</a> + 飞书机器人监听 Gitlab 项目 +</h1><p>在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 <code>push</code> 到 Gitlab。</p> +<p>使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/feishu.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="准备工作"> + <a href="#%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c">#</a> + 准备工作 +</h2><p>实现这个功能,你需要准备:</p> +<ul> +<li>电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。</li> +<li>飞书账号:用于创建机器人监听 Gitlab 项目。</li> +<li>服务器(Linux):用于部署任务,并自动 <code>push</code> 到 Gitlab。</li> +</ul> +<h2 id="创建-gitlab-账号并新建项目"> + <a href="#%e5%88%9b%e5%bb%ba-gitlab-%e8%b4%a6%e5%8f%b7%e5%b9%b6%e6%96%b0%e5%bb%ba%e9%a1%b9%e7%9b%ae">#</a> + 创建 Gitlab 账号并新建项目 +</h2><p>使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/</p> +<p>注册完成后新建一个新项目,并添加一个 <code>README.md</code> 文件。</p> +<h2 id="配置-ssh-秘钥"> + <a href="#%e9%85%8d%e7%bd%ae-ssh-%e7%a7%98%e9%92%a5">#</a> + 配置 SSH 秘钥 +</h2><p>使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install git +</span></span><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config --global user.email <span class="s2">&#34;email&#34;</span> +</span></span><span class="line"><span class="cl">ssh-keygen -C <span class="s2">&#34;email&#34;</span> -t rsa +</span></span><span class="line"><span class="cl">cat ~/.ssh/id_rsa.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中 <code>name</code> 为 Gitlab 账号的名称,<code>email</code> 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。</p> +<p>然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。</p> +<h2 id="clone-项目并配置任务"> + <a href="#clone-%e9%a1%b9%e7%9b%ae%e5%b9%b6%e9%85%8d%e7%bd%ae%e4%bb%bb%e5%8a%a1">#</a> + Clone 项目并配置任务 +</h2><p>在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img3.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后在 Terminal 中找到合适的位置,Clone 项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git +</span></span></code></pre></td></tr></table> +</div> +</div><p>编写你的任务脚本(<code>shell</code> 或 <code>Python</code> 等),然后确保其能正常运行。</p> +<h2 id="新建飞书指令"> + <a href="#%e6%96%b0%e5%bb%ba%e9%a3%9e%e4%b9%a6%e6%8c%87%e4%bb%a4">#</a> + 新建飞书指令 +</h2><p>使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img4.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img5.png' alt='img' style='zoom:50%;' /> +</div> +<div style='display: flex; justify-content: center;'> +<img src='assets/img6.png' alt='img' style='zoom:50%;' /> +</div> +<p>点击完成后,启用机器人即可。</p> +<h2 id="创建定时任务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 创建定时任务 +</h2><p>首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。</p> +<p>本文推荐使用 <code>systemctl</code> 和 <code>systemd</code> 的 <code>timer</code> 来创建定时任务。</p> +<h3 id="创建定时器配置"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e9%85%8d%e7%bd%ae">#</a> + 创建定时器配置 +</h3><p>创建一个 <code>task.timer</code> 文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Timer Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span> +</span></span><span class="line"><span class="cl"><span class="c1"># OnCalendar设置定时规则,这里是每天10点</span> +</span></span><span class="line"><span class="cl"><span class="na">OnCalendar</span><span class="o">=</span><span class="s">*-*-* 10:00:00</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Install]</span> +</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建定时器服务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e6%9c%8d%e5%8a%a1">#</a> + 创建定时器服务 +</h3><p>创建一个 <code>task.service</code> 文件(默认使用 root 用户,建议指定 <code>User</code> 用户):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Service Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Service]</span> +</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">simple</span> +</span></span><span class="line"><span class="cl"><span class="na">User</span><span class="o">=</span><span class="s">user</span> +</span></span><span class="line"><span class="cl"><span class="c1"># 建议使用 shell 脚本</span> +</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/path/to/your/script.sh</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加定时任务"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 添加定时任务 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo cp your_timer_name.timer /etc/systemd/system/ +</span></span><span class="line"><span class="cl">sudo cp your_service_name.service /etc/systemd/system/ +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">sudo systemctl daemon-reload +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 手动启用任务,测试任务是否正常运行</span> +</span></span><span class="line"><span class="cl">sudo systemctl start your_timer_name.service +</span></span><span class="line"><span class="cl"><span class="c1"># 输出任务运行日志</span> +</span></span><span class="line"><span class="cl">sudo journalctl -u your_timer_name.service +</span></span></code></pre></td></tr></table> +</div> +</div><p>任务运行成功后,使用以下命令启用定时任务:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> your_timer_name.timer +</span></span><span class="line"><span class="cl">sudo systemctl list-timers +</span></span></code></pre></td></tr></table> +</div> +</div> + + + CppJson Ch01 + https://3000ye.com/p/cppjson-ch01/ + Sat, 16 Dec 2023 23:33:44 +0800 + + https://3000ye.com/p/cppjson-ch01/ + <img src="https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch01" /><h1 id="cppjson-第一章节自动测试null-和-bool-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%b8%80%e7%ab%a0%e8%8a%82%e8%87%aa%e5%8a%a8%e6%b5%8b%e8%af%95null-%e5%92%8c-bool-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第一章节:自动测试,<code>NULL</code> 和 <code>bool</code> 值解析 +</h1><h2 id="json-是什么"> + <a href="#json-%e6%98%af%e4%bb%80%e4%b9%88">#</a> + JSON 是什么 +</h2><p>JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为<a class="link" href="https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" target="_blank" rel="noopener" + >ECMA-404</a>。</p> +<p>虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。</p> +<p>例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;title&#34;</span><span class="o">:</span> <span class="s2">&#34;Design Patterns&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;subtitle&#34;</span><span class="o">:</span> <span class="s2">&#34;Elements of Reusable Object-Oriented Software&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;author&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Erich Gamma&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Richard Helm&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Ralph Johnson&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;John Vlissides&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;year&#34;</span><span class="o">:</span> <span class="mi">2009</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;weight&#34;</span><span class="o">:</span> <span class="mf">1.8</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;hardcover&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;publisher&#34;</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Company&#34;</span><span class="o">:</span> <span class="s2">&#34;Pearson Education&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Country&#34;</span><span class="o">:</span> <span class="s2">&#34;India&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;website&#34;</span><span class="o">:</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。</p> +<p>从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:</p> +<ul> +<li>null: 表示为 null</li> +<li>boolean: 表示为 true 或 false</li> +<li>number: 一般的浮点数表示方式,在下一单元详细说明</li> +<li>string: 表示为 &ldquo;&hellip;&rdquo;</li> +<li>array: 表示为 [ &hellip; ]</li> +<li>object: 表示为 { &hellip; }</li> +</ul> +<p>我们要实现的 JSON 库,主要是完成 3 个需求:</p> +<ol> +<li>把 JSON 文本解析为一个树状数据结构(parse)。</li> +<li>提供接口访问该数据结构(access)。</li> +<li>把数据结构转换成 JSON 文本(stringify)。</li> +</ol> +<p><img src="https://3000ye.com/p/cppjson-ch01/assets/requirement.png" + width="440" + height="78" + srcset="https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png 480w, https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + + class="gallery-image" + data-flex-grow="564" + data-flex-basis="1353px" + +></p> +<p>我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。</p> +<h2 id="搭建编译环境"> + <a href="#%e6%90%ad%e5%bb%ba%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#</a> + 搭建编译环境 +</h2><p>我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。</p> +<p>我们的 JSON 库名为 CppJson,代码文件只有 3 个:</p> +<ol> +<li><code>include/cppjson.hpp</code>:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。</li> +<li><code>cppjson.cpp</code>:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。</li> +<li><code>cppjsonTest.cpp</code>:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。</li> +</ol> +<p>为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 <a class="link" href="https://cmake.org/" target="_blank" rel="noopener" + >CMake</a>。</p> +<p>在 OS X 平台中,在命令行通过命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir build +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build +</span></span><span class="line"><span class="cl">cmake -DCMAKE_BUILD_TYPE<span class="o">=</span>Debug .. +</span></span><span class="line"><span class="cl">make +</span></span></code></pre></td></tr></table> +</div> +</div><p>将 Debug 改成 Release 就会生成 Release 配置的 makefile。</p> +<p>在 Vscode 中,可以通过配置 <code>tasks.json</code> 文件来进行自动 build:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//.vscode/tasks.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdir&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-p&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;-DCMAKE_BUILD_TYPE=Debug&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//在此处添加其它CMAKE选项 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;..&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-j16&#34;</span><span class="p">,],</span> <span class="c1">//根据机器cpu核心数量自行调整 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOrder&#34;</span><span class="p">:</span> <span class="s2">&#34;sequence&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOn&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> <span class="s2">&#34;make&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后执行 build 生成的文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ./build/cppjson_test_ch01 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">16/16 <span class="o">(</span>100.00%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。</p> +<h2 id="头文件与-api-设计"> + <a href="#%e5%a4%b4%e6%96%87%e4%bb%b6%e4%b8%8e-api-%e8%ae%be%e8%ae%a1">#</a> + 头文件与 API 设计 +</h2><p><code>Cpp</code> 语言有头文件的概念,需要使用 <code>#include</code>去引入头文件中的类型声明和函数声明。但由于头文件也可以 <code>#include</code> 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#pragma once +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonType</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NULL</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_TRUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_FALSE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NUMBER</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_STRING</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_ARRAY</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_OBJECT</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 <code>cppjson_value</code> 结构体表示,我们会称它为一个 JSON 值(JSON value)。</p> +<p>在此单元中,我们只需要实现 <code>null</code>, <code>true</code> 和 <code>false</code> 的解析,因此该结构体只需要存储一个 <code>cppjsonType</code>,之后的单元会逐步加入其他数据。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjsonType</span> <span class="n">type</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_value</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,我们现在只需要两个 API 函数,一个是解析 JSON:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>传入的 JSON 文本是一个 <code>string</code> 字符串,由于我们不应该改动这个输入字符串,所以使用 <code>const std::string</code> 类型。</p> +<p>返回值是以下这些枚举类中的值,无错误会返回 <code>cppjsonParseCode::OK</code>,其他值在下节解释。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonParseCode</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">OK</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">INVALID_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">ROOT_NOT_SINGULAR</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时我们只需要一个访问结果的函数,就是获取其类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonType</span> <span class="nf">cppjson_get_type</span><span class="p">(</span><span class="k">const</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="json-语法子集"> + <a href="#json-%e8%af%ad%e6%b3%95%e5%ad%90%e9%9b%86">#</a> + JSON 语法子集 +</h2><p>下面是此单元的 JSON 语法子集,使用 <a class="link" href="https://tools.ietf.org/html/rfc7159" target="_blank" rel="noopener" + >RFC7159</a> 中的 <a class="link" href="https://tools.ietf.org/html/rfc5234" target="_blank" rel="noopener" + >ABNF</a> 表示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">JSON-text</span> <span class="err">=</span> <span class="err">ws</span> <span class="err">value</span> <span class="err">ws</span> +</span></span><span class="line"><span class="cl"><span class="err">ws</span> <span class="err">=</span> <span class="err">*(%x</span><span class="mi">20</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">09</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">A</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">D)</span> +</span></span><span class="line"><span class="cl"><span class="err">value</span> <span class="err">=</span> <span class="kc">null</span> <span class="err">/</span> <span class="kc">false</span> <span class="err">/</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="kc">null</span> <span class="err">=</span> <span class="s2">&#34;null&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">false</span> <span class="err">=</span> <span class="s2">&#34;false&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">true</span> <span class="err">=</span> <span class="s2">&#34;true&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当中 <code>%xhh</code> 表示以 16 进制表示的字符,<code>/</code> 是多选一,<code>*</code> 是零或多个,<code>()</code> 用于分组。</p> +<p>那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。</p> +<p>第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。</p> +<p>第三行是说,我们现时的值只可以是 <code>null</code>、<code>false</code> 或 <code>true</code>,它们分别有对应的字面值(literal)。</p> +<p>我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。</p> +<p>在这个 JSON 语法子集下,我们定义 3 种错误码:</p> +<ul> +<li>若一个 JSON 只含有空白,传回 <code>LEPT_PARSE_EXPECT_VALUE</code>。</li> +<li>若一个值之后,在空白之后还有其他字符,传回 <code>LEPT_PARSE_ROOT_NOT_SINGULAR</code>。</li> +<li>若值不是那三种字面值,传回 <code>LEPT_PARSE_INVALID_VALUE</code>。</li> +</ul> +<h2 id="单元测试"> + <a href="#%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95">#</a> + 单元测试 +</h2><p>许多同学在做练习题时,都是以 <code>printf</code>/<code>cout</code> 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。</p> +<p>常用的单元测试框架有 xUnit 系列,如 C++ 的 <a class="link" href="https://github.com/google/googletest" target="_blank" rel="noopener" + >Google Test</a>、C# 的 <a class="link" href="https://www.nunit.org/" target="_blank" rel="noopener" + >NUnit</a>。我们为了简单起见,会编写一个极简单的单元测试方式。</p> +<p>一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:</p> +<ol> +<li>加入一个测试。</li> +<li>运行所有测试,新的测试应该会失败。</li> +<li>编写实现代码。</li> +<li>运行所有测试,若有测试失败回到3。</li> +<li>重构代码。</li> +<li>回到 1。</li> +</ol> +<p>TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。</p> +<p>但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。</p> +<p>回到 CppJson 项目,<code>cppjsonTest.cpp</code> 包含了一个极简的单元测试框架:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;include/cppjson.hpp&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;typeinfo&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">main_ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 测试宏接口 +</span></span></span><span class="line"><span class="cl"><span class="c1">// flag 表示测试点是否通过,如果未通过则打印异常信息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define EXPECT_BASE(flag, expect, actual) \ +</span></span></span><span class="line"><span class="cl"><span class="cp"> do {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> test_count ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> if (flag) test_pass ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> else {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> fprintf(stderr, &#34;%s:%d, expect = %s(%d), actual = %s(%d)\n&#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast&lt;int&gt;(expect), typeid(actual).name(), static_cast&lt;int&gt;(actual));\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> main_ret = 1;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> }\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> } while(0) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse_null</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_value</span> <span class="n">v</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_FALSE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">,</span> <span class="n">cppjson_parse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">,</span> <span class="s">&#34;null&#34;</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">,</span> <span class="n">cppjson_get_type</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse_null</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d/%d (%3.2f%%) passed</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">test_pass</span><span class="p">,</span> <span class="n">test_count</span><span class="p">,</span> <span class="n">test_pass</span> <span class="o">*</span> <span class="mf">100.0</span> <span class="o">/</span> <span class="n">test_count</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">main_ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时只提供了一个 <code>EXPECT_TYPE(expect, actual)</code> 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。</p> +<p>若按照 TDD 的步骤,我们先写一个测试,如上面的 <code>test_parse_null()</code>,而 <code>cppjson_parse()</code> 只返回 <code>cppjsonParseCode::OK</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/CppJson/ch01/cppjsonTest.cpp:30, <span class="nv">expect</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>0<span class="o">)</span>, <span class="nv">actual</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>2<span class="o">)</span> +</span></span><span class="line"><span class="cl">15/16 <span class="o">(</span>93.75%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>为通过的测试是因为 <code>cppjson_parse()</code> 没有把 <code>v.type</code> 改成 <code>cppjsonType::CPPJSON_NULL</code>,造成失败。我们再实现 <code>lept_parse()</code> 令到它能通过测试。</p> +<p>然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。</p> +<h2 id="实现解析器"> + <a href="#%e5%ae%9e%e7%8e%b0%e8%a7%a3%e6%9e%90%e5%99%a8">#</a> + 实现解析器 +</h2><p>有了 API 的设计、单元测试,终于要实现解析器了。</p> +<p>首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 <code>cppjson_context</code> 结构体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_context</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// json 解析函数:ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span> <span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_context</span> <span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 分别解析 ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">cppjson_parse_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span> <span class="o">?</span> <span class="n">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:</p> +<ul> +<li>n ➔ null</li> +<li>t ➔ true</li> +<li>f ➔ false</li> +<li>&quot; ➔ string</li> +<li>0-9/- ➔ number</li> +<li>[ ➔ array</li> +<li>{ ➔ object</li> +</ul> +<p>所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39; &#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\t&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\r&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span> <span class="o">++</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 ws2 之后是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">ROOT_NOT_SINGULAR</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 检测 null 值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_null</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">head</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">head</span> <span class="o">!=</span> <span class="s">&#34;null&#34;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 value +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_value</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;n&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_null</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;t&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_true</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;f&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_false</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;\0&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">EXPECT_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch09 + https://3000ye.com/p/c-primer-ch09/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/p/c-primer-ch09/ + <img src="https://3000ye.com/p/c-primer-ch09/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch09" /><h1 id="顺序容器"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8">#</a> + 顺序容器 +</h1><p>一个容器就是一些特定类型对象的集合,<strong>顺序容器(sequence container)</strong> 为程序员提供了控制元素存储和访问顺序的能力。</p> +<h2 id="顺序容器概述"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8%e6%a6%82%e8%bf%b0">#</a> + 顺序容器概述 +</h2><p>下面列出了标准库中的顺序容器,不同容器有不同的性能折中:</p> +<ul> +<li>想容器添加或从容器删除元素的代价。</li> +<li>非顺序访问容器中元素的代价。</li> +</ul> +<table> +<thead> +<tr> +<th>容器类型</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector</code></td> +<td>可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。</td> +</tr> +<tr> +<td><code>deque</code></td> +<td>双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。</td> +</tr> +<tr> +<td><code>list</code></td> +<td>双向链表。只支持双向顺序访问。在<code>list</code>中任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>forward_list</code></td> +<td>单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>array</code></td> +<td>固定大小数组。支持快速随机访问。不能添加或者删除元素。</td> +</tr> +<tr> +<td><code>string</code></td> +<td>与<code>vector</code>相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。</td> +</tr> +</tbody> +</table> +<ul> +<li>除了固定大小的 <code>array</code> 外,其他容器都提供高效、灵活的内存管理。</li> +<li>通常使用 <code>vector</code> 是最好的选择,除非你有很好的理由选择其他容器。</li> +<li>如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序要求随记访问元素,应使用 <code>vector</code> 或 <code>deque</code>。</li> +<li>如果程序要求在容器的中间插入或删除元素,应使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 <code>deque</code>。</li> +</ul> +<h2 id="容器库概念"> + <a href="#%e5%ae%b9%e5%99%a8%e5%ba%93%e6%a6%82%e5%bf%b5">#</a> + 容器库概念 +</h2><p>容器类型操作上形成了一种层次:</p> +<ul> +<li>某些操作是所有容器类型都提供的。</li> +<li>另一些操作仅针对顺序容器、关联容器或无序容器。</li> +</ul> +<h3 id="类型"> + <a href="#%e7%b1%bb%e5%9e%8b">#</a> + 类型 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iterator</code></td> +<td>此容器类型的迭代器类型</td> +</tr> +<tr> +<td><code>const_iterator</code></td> +<td>可以读取元素但不能修改元素的迭代器类型</td> +</tr> +<tr> +<td><code>size_type</code></td> +<td>无符号整数类型,足够保存此种容器类型最大可能的大小</td> +</tr> +<tr> +<td><code>difference_type</code></td> +<td>带符号整数类型,足够保存两个迭代器之间的距离</td> +</tr> +<tr> +<td><code>value_type</code></td> +<td>元素类型</td> +</tr> +<tr> +<td><code>reference</code></td> +<td>元素的左值类型;和<code>value_type &amp;</code>含义相同</td> +</tr> +<tr> +<td><code>const_reference</code></td> +<td>元素的<code>const</code>左值类型,即<code>const value_type &amp;</code></td> +</tr> +</tbody> +</table> +<h3 id="构造函数"> + <a href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0">#</a> + 构造函数 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>C c;</code></td> +<td>默认构造函数,构造空容器</td> +</tr> +<tr> +<td><code>C c1(c2);</code>或<code>C c1 = c2;</code></td> +<td>构造<code>c2</code>的拷贝<code>c1</code></td> +</tr> +<tr> +<td><code>C c(b, e)</code></td> +<td>构造<code>c</code>,将迭代器<code>b</code>和<code>e</code>指定范围内的所有元素拷贝到<code>c</code></td> +</tr> +<tr> +<td><code>C c(a, b, c...)</code></td> +<td>列表初始化<code>c</code></td> +</tr> +<tr> +<td><code>C c(n)</code></td> +<td>只支持顺序容器,且不包括<code>array</code>,包含<code>n</code>个元素,这些元素进行了值初始化</td> +</tr> +<tr> +<td><code>C c(n, t)</code></td> +<td>包含<code>n</code>个初始值为<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>只有顺序容器的构造函数才接受大小参数,关联容器并不支持。</li> +<li><code>array</code>具有固定大小。</li> +<li>和其他容器不同,默认构造的<code>array</code>是非空的。</li> +<li>直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。</li> +<li>使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。</li> +</ul> +<h3 id="赋值和swap"> + <a href="#%e8%b5%8b%e5%80%bc%e5%92%8cswap">#</a> + 赋值和<code>swap</code> +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c1 = c2;</code></td> +<td>将<code>c1</code>中的元素替换成<code>c2</code>中的元素</td> +</tr> +<tr> +<td><code>c1 = {a, b, c...}</code></td> +<td>将<code>c1</code>中的元素替换成列表中的元素(不适用于<code>array</code>)</td> +</tr> +<tr> +<td><code>c1.swap(c2)</code></td> +<td>交换<code>c1</code>和<code>c2</code>的元素</td> +</tr> +<tr> +<td><code>swap(c1, c2)</code></td> +<td>等价于<code>c1.swap(c2)</code></td> +</tr> +<tr> +<td><code>c.assign(b, e)</code></td> +<td>将<code>c</code>中的元素替换成迭代器<code>b</code>和<code>e</code>表示范围中的元素,<code>b</code>和<code>e</code>不能指向<code>c</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(il)</code></td> +<td>将<code>c</code>中的元素替换成初始化列表<code>il</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(n, r)</code></td> +<td>将<code>c</code>中的元素替换为<code>n</code>个值是<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>使用非成员版本的<code>swap</code>是一个好习惯。</li> +<li><code>assign</code>操作不适用于关联容器和<code>array</code></li> +</ul> +<h3 id="大小"> + <a href="#%e5%a4%a7%e5%b0%8f">#</a> + 大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.size()</code></td> +<td><code>c</code>中元素的数目(不支持<code>forward_list</code>)</td> +</tr> +<tr> +<td><code>c.max_size()</code></td> +<td><code>c</code>中可保存的最大元素数目</td> +</tr> +<tr> +<td><code>c.empty()</code></td> +<td>若<code>c</code>中存储了元素,返回<code>false</code>,否则返回<code>true</code></td> +</tr> +</tbody> +</table> +<h3 id="添加元素"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%85%83%e7%b4%a0">#</a> + 添加元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.push_back(t)</code></td> +<td>在<code>c</code>尾部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_back(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.push_front(t)</code></td> +<td>在<code>c</code>头部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_front(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前创建一个值是<code>t</code>的元素,返回指向新元素的迭代器</td> +</tr> +<tr> +<td><code>c.emplace(p, args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, n, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前插入<code>n</code>个值为<code>t</code>的元素,返回指向第一个新元素的迭代器;如果<code>n</code>是0,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, b, e)</code></td> +<td>将迭代器<code>b</code>和<code>e</code>范围内的元素,插入到<code>p</code>指向的元素之前;如果范围为空,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, il)</code></td> +<td><code>il</code>是一个花括号包围中的元素值列表,将其插入到<code>p</code>指向的元素之前;如果<code>il</code>是空,则返回<code>p</code></td> +</tr> +</tbody> +</table> +<ul> +<li>因为这些操作会改变大小,因此不适用于<code>array</code>。</li> +<li><code>forward_list</code>有自己专有版本的<code>insert</code>和<code>emplace</code>。</li> +<li><code>forward_list</code>不支持<code>push_back</code>和<code>emplace_back</code>。</li> +<li>当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。</li> +<li><code>emplace</code>开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。</li> +<li>传递给<code>emplace</code>的参数必须和元素类型的构造函数相匹配。</li> +</ul> +<h3 id="访问元素"> + <a href="#%e8%ae%bf%e9%97%ae%e5%85%83%e7%b4%a0">#</a> + 访问元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.back()</code></td> +<td>返回<code>c</code>中尾元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c.front()</code></td> +<td>返回<code>c</code>中头元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c[n]</code></td> +<td>返回<code>c</code>中下标是<code>n</code>的元素的引用,<code>n</code>时候一个无符号证书。若<code>n&gt;=c.size()</code>,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.at(n)</code></td> +<td>返回下标为<code>n</code>的元素引用。如果下标越界,则抛出<code>out_of_range</code>异常</td> +</tr> +</tbody> +</table> +<ul> +<li>访问成员函数返回的是引用。</li> +<li><code>at</code>和下标操作只适用于<code>string</code>、<code>vector</code>、<code>deque</code>、<code>array</code>。</li> +<li><code>back</code>不适用于<code>forward_list</code>。</li> +<li>如果希望下标是合法的,可以使用<code>at</code>函数。</li> +</ul> +<h3 id="删除元素"> + <a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0">#</a> + 删除元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.pop_back()</code></td> +<td>删除<code>c</code>中尾元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.pop_front()</code></td> +<td>删除<code>c</code>中首元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.erase(p)</code></td> +<td>删除迭代器<code>p</code>指向的元素,返回一个指向被删除元素之后的元素的迭代器,若<code>p</code>本身是尾后迭代器,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.erase(b, e)</code></td> +<td>删除迭代器<code>b</code>和<code>e</code>范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若<code>e</code>本身就是尾后迭代器,则返回尾后迭代器</td> +</tr> +<tr> +<td><code>c.clear()</code></td> +<td>删除<code>c</code>中所有元素,返回<code>void</code></td> +</tr> +</tbody> +</table> +<ul> +<li>会改变容器大小,不适用于<code>array</code>。</li> +<li><code>forward_list</code>有特殊版本的<code>erase</code></li> +<li><code>forward_list</code>不支持<code>pop_back</code></li> +<li><code>vector</code>和<code>string</code>不支持<code>pop_front</code></li> +</ul> +<h3 id="特殊的-forwad_list操作"> + <a href="#%e7%89%b9%e6%ae%8a%e7%9a%84-forwad_list%e6%93%8d%e4%bd%9c">#</a> + 特殊的 <code>forwad_list</code>操作 +</h3><ul> +<li>链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。</li> +<li><code>forward_list</code>定义了<code>before_begin</code>,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。</li> +</ul> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>lst.before_begin()</code></td> +<td>返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。</td> +</tr> +<tr> +<td><code>lst.cbefore_begin()</code></td> +<td>同上,但是返回的是常量迭代器。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象</td> +</tr> +<tr> +<td><code>lst.insert_after(p, n, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象,<code>n</code>是数量。若<code>n</code>是0则函数行为未定义</td> +</tr> +<tr> +<td><code>lst.insert_after(p, b, e)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由迭代器<code>b</code>和<code>e</code>指定范围。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, il)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由<code>il</code>指定初始化列表。</td> +</tr> +<tr> +<td><code>emplace_after(p, args)</code></td> +<td>使用<code>args</code>在<code>p</code>之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若<code>p</code>为尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(p)</code></td> +<td>删除<code>p</code>指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若<code>p</code>指向<code>lst</code>的尾元素或者是一个尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(b, e)</code></td> +<td>类似上面,删除对象换成从<code>b</code>到<code>e</code>指定的范围。</td> +</tr> +</tbody> +</table> +<h3 id="改变容器大小"> + <a href="#%e6%94%b9%e5%8f%98%e5%ae%b9%e5%99%a8%e5%a4%a7%e5%b0%8f">#</a> + 改变容器大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.resize(n)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,若<code>n&lt;c.size()</code>,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化</td> +</tr> +<tr> +<td><code>c.resize(n, t)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,任何新添加的元素都初始化为值<code>t</code></td> +</tr> +</tbody> +</table> +<h3 id="获取迭代器"> + <a href="#%e8%8e%b7%e5%8f%96%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 获取迭代器 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.begin()</code>, <code>c.end()</code></td> +<td>返回指向<code>c</code>的首元素和尾元素之后位置的迭代器</td> +</tr> +<tr> +<td><code>c.cbegin()</code>, <code>c.cend()</code></td> +<td>返回<code>const_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>以<code>c</code>开头的版本是C++11新标准引入的</li> +<li>当不需要写访问时,应该使用<code>cbegin</code>和<code>cend</code>。</li> +</ul> +<h3 id="反向容器的额外成员"> + <a href="#%e5%8f%8d%e5%90%91%e5%ae%b9%e5%99%a8%e7%9a%84%e9%a2%9d%e5%a4%96%e6%88%90%e5%91%98">#</a> + 反向容器的额外成员 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>reverse_iterator</code></td> +<td>按逆序寻址元素的迭代器</td> +</tr> +<tr> +<td><code>const_reverse_iterator</code></td> +<td>不能修改元素的逆序迭代器</td> +</tr> +<tr> +<td><code>c.rbegin()</code>, <code>c.rend()</code></td> +<td>返回指向<code>c</code>的尾元素和首元素之前位置的迭代器</td> +</tr> +<tr> +<td><code>c.crbegin()</code>, <code>c.crend()</code></td> +<td>返回<code>const_reverse_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>不支持<code>forward_list</code></li> +</ul> + + + + C++ Primer Ch08 + https://3000ye.com/p/c-primer-ch08/ + Sun, 10 Dec 2023 15:24:27 +0800 + + https://3000ye.com/p/c-primer-ch08/ + <img src="https://3000ye.com/p/c-primer-ch08/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch08" /><h1 id="io-库"> + <a href="#io-%e5%ba%93">#</a> + IO 库 +</h1><p>我们的程序已经使用了很多 IO 库设施:</p> +<ul> +<li><code>istream</code> (输入流)类型,提供输入操作。</li> +<li><code>ostream</code> (输出流)类型,提供输出操作。</li> +<li><code>cin</code>,一个 <code>istream</code> 对象,从标准输入读取数据。</li> +<li><code>cout</code>,一个 <code>ostream</code> 对象,想标准输出写入数据。</li> +<li><code>cerr</code>,一个 <code>ostream</code> 对象,通常用于输出程序错误信息,写入到标准错误。</li> +<li><code>&gt;&gt;</code> 运算符,用来从一个 <code>istream</code> 对象中读取输入数据。</li> +<li><code>&lt;&lt;</code> 运算符,用来向一个 <code>ostream</code> 对象中写入输出数据。</li> +<li><code>getline</code> 函数,从一个给定的 <code>istream</code> 对象中读取一行数据,存入到一个给定的 <code>string</code> 对象中。</li> +</ul> +<h2 id="io-库-1"> + <a href="#io-%e5%ba%93-1">#</a> + IO 库 +</h2><p>IO 类型和对象一般都是操纵 <code>char</code> 数据的,但有些使用需要对文件、<code>string</code> 进行操作,因此分别定义了三个头文件:</p> +<ul> +<li><code>iostream</code> 头文件:从标准流中读写数据,<code>istream</code>、<code>ostream</code> 等。</li> +<li><code>fstream</code> 头文件:从文件中读写数据,<code>ifstream</code>、<code>ofstream</code> 等。</li> +<li><code>sstream</code> 头文件:从字符串中读写数据,<code>istringstream</code>、<code>ostringstream</code> 等。</li> +</ul> + + + + C++ Primer Ch07 + https://3000ye.com/p/c-primer-ch07/ + Sun, 10 Dec 2023 14:35:42 +0800 + + https://3000ye.com/p/c-primer-ch07/ + <img src="https://3000ye.com/p/c-primer-ch07/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch07" /><h1 id="类"> + <a href="#%e7%b1%bb">#</a> + 类 +</h1><p>类的基本思想是 <strong>数据抽象(data abstraction)</strong> 和 <strong>封装(encapsulation)</strong>。数据抽象是一种依赖于 <strong>接口(interface)</strong> 和 <strong>实现(implementation)</strong> 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。</p> +<p>封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。</p> +<p>类要想实现数据抽象和封装,首先需要定义一个 <strong>抽象数据类型(abstract data type)</strong>。</p> +<h2 id="定义抽象数据类型"> + <a href="#%e5%ae%9a%e4%b9%89%e6%8a%bd%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#</a> + 定义抽象数据类型 +</h2><h2 id="访问控制与封装"> + <a href="#%e8%ae%bf%e9%97%ae%e6%8e%a7%e5%88%b6%e4%b8%8e%e5%b0%81%e8%a3%85">#</a> + 访问控制与封装 +</h2><p>我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 <code>C++</code> 中使用 <strong>访问说明符(access specifiers)</strong> 加强类的封装:</p> +<ul> +<li>定义在 <code>public</code> 说明符之后的成员,在整个程序内都可被访问。</li> +<li>定义在 <code>private</code> 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。</li> +</ul> +<p>定义新的 <code>Sales_data</code> 类:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sales_data</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bookNo</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="n">units_sold</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">revenue</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="nf">avg_price</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">units_sold</span> <span class="o">?</span> <span class="n">revenue</span> <span class="o">/</span> <span class="nl">units_sold</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">p</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="n">units_sold</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">revenue</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">istream</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">isbn</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">bookNo</span><span class="p">;</span> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span> <span class="o">&amp;</span><span class="n">combine</span><span class="p">(</span><span class="k">const</span> <span class="n">Sales_data</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch06 + https://3000ye.com/p/c-primer-ch06/ + Fri, 08 Dec 2023 21:13:28 +0800 + + https://3000ye.com/p/c-primer-ch06/ + <img src="https://3000ye.com/p/c-primer-ch06/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch06" /><h1 id="函数"> + <a href="#%e5%87%bd%e6%95%b0">#</a> + 函数 +</h1><p>函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。</p> +<h2 id="函数基础"> + <a href="#%e5%87%bd%e6%95%b0%e5%9f%ba%e7%a1%80">#</a> + 函数基础 +</h2><p>一个典型的 <strong>函数(function)</strong> 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 <strong>形参(parameter)</strong> 组成的列表以及函数体。</p> +<p>我们通过 <strong>调用运算符(call operator)</strong> 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 <strong>实参(argument)</strong> 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。</p> +<p><strong>编写函数</strong>:例如编写一个求 <code>n</code> 的阶乘的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fact</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">*=</span> <span class="n">n</span> <span class="o">--</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><strong>调用函数</strong>:要调用 <code>fact</code> 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;5! = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,<strong>主调函数(calling funciton)</strong> 的执行暂停,<strong>被调函数(called funciton)</strong> 开始执行。</p> +<h3 id="局部对象"> + <a href="#%e5%b1%80%e9%83%a8%e5%af%b9%e8%b1%a1">#</a> + 局部对象 +</h3><p>在 <code>C++</code> 语言中,名字有作用域,对象有 <strong>生命周期(lifetime)</strong>:</p> +<ul> +<li>名字的作用域是程序文本的一部分,名字在其中可见。</li> +<li>对象的生命周期是程序执行过程中该对象存在的一段时间。</li> +</ul> +<p>在函数体内,形参和内部定义的变量统称为 <strong>局部变量(local variable)</strong>,它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。</p> +<p><strong>局部静态对象</strong>:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 <code>static</code> 类型从而获得这样的对象,<strong>局部静态对象(local static object)</strong> 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。</p> +<h2 id="参数传递"> + <a href="#%e5%8f%82%e6%95%b0%e4%bc%a0%e9%80%92">#</a> + 参数传递 +</h2> + + + C++ Primer Ch04 + https://3000ye.com/p/c-primer-ch04/ + Tue, 28 Nov 2023 08:49:13 +0800 + + https://3000ye.com/p/c-primer-ch04/ + <img src="https://3000ye.com/p/c-primer-ch04/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch04" /><h1 id="表达式"> + <a href="#%e8%a1%a8%e8%be%be%e5%bc%8f">#</a> + 表达式 +</h1><p>表达式由一个或多个 <strong>运算对象(operand)</strong> 组成对表达式求值将得到一个 <strong>结果(result)</strong>。</p> +<h2 id="基础"> + <a href="#%e5%9f%ba%e7%a1%80">#</a> + 基础 +</h2><h3 id="基本概念"> + <a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a> + 基本概念 +</h3><p><code>C++</code> 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。</p> +<p>对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。</p> +<p>在表达式求值过程中,小整数类型(<code>bool</code>、<code>char</code>、<code>short</code>)等通常会被 <strong>提升(promoted)</strong> 成较大的整数类型(<code>int</code>)。</p> +<p>当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 <strong>重载运算符(overloaded operator)</strong>。</p> +<p><code>C++</code> 的表达式要不然是 <strong>右值(rvalue)</strong>,要不然就是 <strong>左值(lvalue)</strong>,这两个名词是从 <code>C</code> 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p> +<h3 id="求值顺序"> + <a href="#%e6%b1%82%e5%80%bc%e9%a1%ba%e5%ba%8f">#</a> + 求值顺序 +</h3><p>在大多数情况下,表达式求值的顺序是没有明确指定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">f1</span><span class="p">()</span> <span class="o">*</span> <span class="n">f2</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们知道 <code>f1</code> 和 <code>f2</code> 一定会在执行乘法之前被调用,但是无法知道是 <code>f1</code> 先被调用还是 <code>f2</code> 先被调用。对于没有指定调用顺序的程序来说,如果 <code>f1</code> 和 <code>f2</code> 同时修改了同一个对象,将会引发错误并产生未定义的行为。</p> +<h2 id="算术运算符"> + <a href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 算术运算符 +</h2><p><strong>溢出</strong>:当计算的结果超出该类型所能表示的范围时就会产生溢出。</p> +<p><strong><code>bool</code> 类型不应该参与计算</strong>。</p> +<p><strong>取余运算</strong>:<code>m % n</code> 的结果的符号与 <code>m</code> 相同。</p> +<h2 id="逻辑运算符"> + <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 逻辑运算符 +</h2><p><strong>短路求值</strong>:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。</p> +<h2 id="赋值运算符"> + <a href="#%e8%b5%8b%e5%80%bc%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 赋值运算符 +</h2> + + + C++ Primer Ch03 + https://3000ye.com/p/c-primer-ch03/ + Tue, 14 Nov 2023 22:14:23 +0800 + + https://3000ye.com/p/c-primer-ch03/ + <img src="https://3000ye.com/p/c-primer-ch03/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch03" /><h1 id="字符串向量和数组"> + <a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%90%91%e9%87%8f%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 字符串、向量和数组 +</h1><h2 id="命名空间的-using-声明"> + <a href="#%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e7%9a%84-using-%e5%a3%b0%e6%98%8e">#</a> + 命名空间的 <code>using</code> 声明 +</h2><p>我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用<code>using</code>进行声明:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">;</span> <span class="c1">// 单独使用某个函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 批量声明 std 中所有函数 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>头文件中不应该包含 <code>using</code> 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。</p> +</blockquote> +<h2 id="标准库类型-string"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-string">#</a> + 标准库类型 <code>string</code> +</h2><p>标准库类型 <code>string</code> 表示可变长的字符序列,使用 <code>string</code> 类型必须首先包含 <code>string</code> 头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="定义和初始化-string-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-string-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>string</code> 对象 +</h3><p>初始化 <code>string</code> 对象的方式:</p> +<table> +<thead> +<tr> +<th>方式</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>string s1</code></td> +<td>默认初始化,<code>s1</code>是个空字符串</td> +</tr> +<tr> +<td><code>string s2(s1)</code></td> +<td><code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s2 = s1</code></td> +<td>等价于<code>s2(s1)</code>,<code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s3(&quot;value&quot;)</code></td> +<td><code>s3</code>是字面值“value”的副本,除了字面值最后的那个空字符外</td> +</tr> +<tr> +<td><code>string s3 = &quot;value&quot;</code></td> +<td>等价于<code>s3(&quot;value&quot;)</code>,<code>s3</code>是字面值&quot;value&quot;的副本</td> +</tr> +<tr> +<td><code>string s4(n, 'c')</code></td> +<td>把<code>s4</code>初始化为由连续<code>n</code>个字符<code>c</code>组成的串</td> +</tr> +</tbody> +</table> +<p>拷贝初始化(copy initialization):使用 <code>=</code> 将一个已有的对象拷贝到正在创建的对象。</p> +<p>直接初始化(direct initialization):通过括号给对象赋值。</p> +<h3 id="string-对象的操作"> + <a href="#string-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>string</code> 对象的操作 +</h3><p><code>string</code>的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>os &lt;&lt; s</code></td> +<td>将<code>s</code>写到输出流<code>os</code>当中,返回<code>os</code></td> +</tr> +<tr> +<td><code>is &gt;&gt; s</code></td> +<td>从<code>is</code>中读取字符串赋给<code>s</code>,字符串以空白分割,返回<code>is</code></td> +</tr> +<tr> +<td><code>getline(is, s)</code></td> +<td>从<code>is</code>中读取一行赋给<code>s</code>,返回<code>is</code></td> +</tr> +<tr> +<td><code>s.empty()</code></td> +<td><code>s</code>为空返回<code>true</code>,否则返回<code>false</code></td> +</tr> +<tr> +<td><code>s.size()</code></td> +<td>返回<code>s</code>中字符的个数</td> +</tr> +<tr> +<td><code>s[n]</code></td> +<td>返回<code>s</code>中第<code>n</code>个字符的引用,位置<code>n</code>从0计起</td> +</tr> +<tr> +<td><code>s1+s2</code></td> +<td>返回<code>s1</code>和<code>s2</code>连接后的结果</td> +</tr> +<tr> +<td><code>s1=s2</code></td> +<td>用<code>s2</code>的副本代替<code>s1</code>中原来的字符</td> +</tr> +<tr> +<td><code>s1==s2</code></td> +<td>如果<code>s1</code>和<code>s2</code>中所含的字符完全一样,则它们相等;<code>string</code>对象的相等性判断对字母的大小写敏感</td> +</tr> +<tr> +<td><code>s1!=s2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td> +<td>利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)</td> +</tr> +</tbody> +</table> +<p>读取 <code>string</code> 对象:</p> +<ul> +<li>使用 <code>IO</code> 操作符 <code>&gt;&gt;</code> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。</li> +<li>使用 <code>getline()</code> 函数读取:将一整行读取为 <code>string</code> 对象,包括空白。</li> +</ul> +<p><code>s.size()</code> 返回 <code>string::size_type</code> 类型,是 <strong>无符号</strong> 类型的值,不能和 <code>int</code> 混用。</p> +<p><code>s1 + s2</code> 使用时,必须保证至少其中一个为 <code>string</code> 类型。例如:<code>string s = &quot;hello&quot; + &quot;world&quot;</code> 错误,其 <code>+</code> 两边都为字符串字面值。</p> +<p><strong>字符串字面值</strong> 和 <code>string</code> 是不同的类型。</p> +<h3 id="处理-string-对象中的字符"> + <a href="#%e5%a4%84%e7%90%86-string-%e5%af%b9%e8%b1%a1%e4%b8%ad%e7%9a%84%e5%ad%97%e7%ac%a6">#</a> + 处理 <code>string</code> 对象中的字符 +</h3><p><code>C++</code> 修改了 <code>c</code> 的标准库 <code>ctype.h</code> 为 <code>cctype</code>,其中定义了一组标准函数:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>isalnum(c)</code></td> +<td>当<code>c</code>是字母或数字时为真</td> +</tr> +<tr> +<td><code>isalpha(c)</code></td> +<td>当<code>c</code>是字母时为真</td> +</tr> +<tr> +<td><code>iscntrl(c)</code></td> +<td>当<code>c</code>是控制字符时为真</td> +</tr> +<tr> +<td><code>isdigit(c)</code></td> +<td>当<code>c</code>是数字时为真</td> +</tr> +<tr> +<td><code>isgraph(c)</code></td> +<td>当<code>c</code>不是空格但可以打印时为真</td> +</tr> +<tr> +<td><code>islower(c)</code></td> +<td>当<code>c</code>是小写字母时为真</td> +</tr> +<tr> +<td><code>isprint(c)</code></td> +<td>当<code>c</code>是可打印字符时为真</td> +</tr> +<tr> +<td><code>ispunct(c)</code></td> +<td>当<code>c</code>是标点符号时为真</td> +</tr> +<tr> +<td><code>isspace(c)</code></td> +<td>当<code>c</code>是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)</td> +</tr> +<tr> +<td><code>isupper(c)</code></td> +<td>当<code>c</code>是大写字母时为真</td> +</tr> +<tr> +<td><code>isxdigit(c)</code></td> +<td>当<code>c</code>是十六进制数字时为真</td> +</tr> +<tr> +<td><code>tolower(c)</code></td> +<td>当<code>c</code>是大写字母,输出对应的小写字母;否则原样输出<code>c</code></td> +</tr> +<tr> +<td><code>toupper(c)</code></td> +<td>当<code>c</code>是小写字母,输出对应的大写字母;否则原样输出<code>c</code></td> +</tr> +</tbody> +</table> +<p>遍历字符串:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">c</span> <span class="p">:</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>str[idx]</code> 中的 <code>idx</code> 为 <code>string::size_type</code> 类型,如果使用 <code>int</code> 会隐式转换为该类型。</p> +<h2 id="标准库类型-vector"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-vector">#</a> + 标准库类型 <code>vector</code> +</h2><p>标准库类型 <code>vector</code> 表示对象的集合,其中给所有对象的类型都相同。因为 <code>vector</code> 容纳着其他对象,所以称其为 <strong>容器(container)</strong>,使用 <code>vector</code> 必须包含其头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>vector</code> 同时也是 <strong>类模板(class template)</strong>,模板本身不是类或函数,但可以使用模板创建类,这个过程称为 <strong>实例化(instantiation)</strong>。</p> +<p>当使用模板时,需要指出编译器应把类或函数实例化成何种类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// ls 保存 int 类型的对象 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">files</span><span class="p">;</span> <span class="c1">// 该向量中的元素是 vector 对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p><code>vector</code> 是模板,<code>vector&lt;int&gt;</code> 是类型。</p> +</blockquote> +<h3 id="定义和初始化-vector-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-vector-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>vector</code> 对象 +</h3><p>初始化<code>vector</code>对象的方法:</p> +<table> +<thead> +<tr> +<th>方法</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector&lt;T&gt; v1</code></td> +<td><code>v1</code>是一个空<code>vector</code>,它潜在的元素是<code>T</code>类型的,执行默认初始化</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2(v1)</code></td> +<td><code>v2</code>中包含有<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2 = v1</code></td> +<td>等价于<code>v2(v1)</code>,<code>v2</code>中包含<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v3(n, val)</code></td> +<td><code>v3</code>包含了n个重复的元素,每个元素的值都是<code>val</code></td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v4(n)</code></td> +<td><code>v4</code>包含了n个重复地执行了值初始化的对象</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5{a, b, c...}</code></td> +<td><code>v5</code>包含了初始值个数的元素,每个元素被赋予相应的初始值</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5={a, b, c...}</code></td> +<td>等价于<code>v5{a, b, c...}</code></td> +</tr> +</tbody> +</table> +<h3 id="vector-对象的操作"> + <a href="#vector-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>vector</code> 对象的操作: +</h3><p><code>vector</code>支持的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>v.emtpy()</code></td> +<td>如果<code>v</code>不含有任何元素,返回真;否则返回假</td> +</tr> +<tr> +<td><code>v.size()</code></td> +<td>返回<code>v</code>中元素的个数</td> +</tr> +<tr> +<td><code>v.push_back(t)</code></td> +<td>向<code>v</code>的尾端添加一个值为<code>t</code>的元素</td> +</tr> +<tr> +<td><code>v[n]</code></td> +<td>返回<code>v</code>中第<code>n</code>个位置上元素的<strong>引用</strong></td> +</tr> +<tr> +<td><code>v1 = v2</code></td> +<td>用<code>v2</code>中的元素拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 = {a,b,c...}</code></td> +<td>用列表中元素的拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 == v2</code></td> +<td><code>v1</code>和<code>v2</code>相等当且仅当它们的元素数量相同且对应位置的元素值都相同</td> +</tr> +<tr> +<td><code>v1 != v2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>,<code>&lt;=</code>,<code>&gt;</code>, <code>&gt;=</code></td> +<td>以字典顺序进行比较</td> +</tr> +</tbody> +</table> +<h2 id="迭代器介绍"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e4%bb%8b%e7%bb%8d">#</a> + 迭代器介绍 +</h2><p>除了下标运算符外,<strong>迭代器(iterator)</strong> 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。</p> +<h3 id="使用迭代器"> + <a href="#%e4%bd%bf%e7%94%a8%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 使用迭代器 +</h3><p>拥有迭代器的类型都具有 <code>begin</code> 和 <code>end</code> 成员,其中 <code>begin</code> 成员返回指向第一个元素的迭代器:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">e</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="c1">// b 和 e 类型相同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><code>end</code> 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 <code>end</code> 指向容器的 <strong>尾后(off the end)</strong> 元素。这样的迭代器通常没有意义,只是作为标记,被称为 <strong>尾后迭代器(off-the-end iterator)</strong> 或 <strong>尾迭代器(end iterator)</strong>。</p> +<blockquote> +<p>若容器为空,则 <code>begin</code> 和 <code>end</code> 都返回尾后迭代器。</p> +</blockquote> +<p>标准容器迭代器的运算符:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>*iter</code></td> +<td>返回迭代器<code>iter</code>所指向的<strong>元素的引用</strong></td> +</tr> +<tr> +<td><code>iter-&gt;mem</code></td> +<td>等价于<code>(*iter).mem</code></td> +</tr> +<tr> +<td><code>++iter</code></td> +<td>令<code>iter</code>指示容器中的下一个元素</td> +</tr> +<tr> +<td><code>--iter</code></td> +<td>令<code>iter</code>指示容器中的上一个元素</td> +</tr> +<tr> +<td><code>iter1 == iter2</code></td> +<td>判断两个迭代器是否相等</td> +</tr> +</tbody> +</table> +<blockquote> +<p>泛型编程:尽量使用 <code>!=</code> 来对迭代器进行判断</p> +</blockquote> +<p>迭代器也拥有自己的类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="p">;</span> <span class="c1">// it 是 vector&lt;int&gt; 类型的迭代器,可以读写元素 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">const_iterator</span> <span class="n">it2</span><span class="p">;</span> <span class="c1">// it2 只能读,不能写 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果容器中的值为常量,则 <code>begin</code> 和 <code>end</code> 返回 <code>const_iterator</code>,否则返回 <code>iterator</code>。</p> +<p>解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="s">&#34;str1&#34;</span><span class="p">,</span> <span class="s">&#34;str2&#34;</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">ls</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> <span class="c1">// s 为 &#34;str1&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">).</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 解引用访问 string 成员 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 作用同上 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="迭代器运算"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e8%bf%90%e7%ae%97">#</a> + 迭代器运算 +</h3><p><code>string</code> 和 <code>vector</code> 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 <strong>迭代器运算(iterator arithmetic)</strong>:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iter + n</code></td> +<td>迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter - n</code></td> +<td>迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter1 += n</code></td> +<td>迭代器加法的复合赋值语句,将<code>iter1</code>加n的结果赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 -= n</code></td> +<td>迭代器减法的复合赋值语句,将<code>iter2</code>减n的加过赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 - iter2</code></td> +<td>两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。</td> +</tr> +<tr> +<td><code>&gt;</code>、<code>&gt;=</code>、<code>&lt;</code>、<code>&lt;=</code></td> +<td>迭代器的关系运算符,如果某迭代器</td> +</tr> +</tbody> +</table> +<p>当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 <code>difference_type</code> 类型,是带符号整数型。</p> +<h2 id="数组"> + <a href="#%e6%95%b0%e7%bb%84">#</a> + 数组 +</h2><p>数组可以看做 <code>vector</code> 的低配版,其 <strong>长度固定</strong>。</p> +<h3 id="定义和初始化内置数组"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96%e5%86%85%e7%bd%ae%e6%95%b0%e7%bb%84">#</a> + 定义和初始化内置数组 +</h3><p>数组的声明和定义形如 <code>a[d]</code>,其中 <code>a</code> 是数组的名字,<code>d</code> 是数组的维度(大于 0):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="n">cnt2</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">arr2</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整型指针的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr3</span><span class="p">[</span><span class="n">cnt</span><span class="p">];</span> <span class="c1">// 报错,cnt 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr4</span><span class="p">[</span><span class="n">cnt2</span><span class="p">];</span> <span class="c1">// 含有 42 和整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> <span class="c1">// 自动计算长度的数组 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> <span class="c1">// 报错,长度不够 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a2</span><span class="p">[]</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组来初始化数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组进行赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="访问数组元素"> + <a href="#%e8%ae%bf%e9%97%ae%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0">#</a> + 访问数组元素 +</h3><p>数组的下标为 <code>size_t</code> 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。</p> +<blockquote> +<p>下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。</p> +</blockquote> +<h3 id="指针和数组"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 指针和数组 +</h3><p>使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">ls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ls</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// 数组的元素的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// 等价于 *p2 = &amp;ls[0] +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="c-风格字符串"> + <a href="#c-%e9%a3%8e%e6%a0%bc%e5%ad%97%e7%ac%a6%e4%b8%b2">#</a> + <code>C</code> 风格字符串 +</h3><p>字符串字面值是一种通用结构的实例,这种结构是 <code>C++</code> 由 <code>C</code> 继承而来的 <strong><code>C</code> 风格字符串(C-style character string)</strong> 。 +按此习惯书写的字符串存放在字符数组中并以 <strong>空字符结束(null terminated)</strong>。</p> +<blockquote> +<p>在 <code>C++</code> 程序中尽量不要使用 <code>C</code> 风格字符串,容易引起安全漏洞且不方便。</p> +</blockquote> +<p>C标准库String函数,定义在<code>&lt;cstring&gt;</code> 中:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>strlen(p)</code></td> +<td>返回<code>p</code>的长度,空字符不计算在内</td> +</tr> +<tr> +<td><code>strcmp(p1, p2)</code></td> +<td>比较<code>p1</code>和<code>p2</code>的相等性。如果<code>p1==p2</code>,返回0;如果<code>p1&gt;p2</code>,返回一个正值;如果<code>p1&lt;p2</code>,返回一个负值。</td> +</tr> +<tr> +<td><code>strcat(p1, p2)</code></td> +<td>将<code>p2</code>附加到<code>p1</code>之后,返回<code>p1</code></td> +</tr> +<tr> +<td><code>strcpy(p1, p2)</code></td> +<td>将<code>p2</code>拷贝给<code>p1</code>,返回<code>p1</code></td> +</tr> +</tbody> +</table> +<h2 id="多维数组"> + <a href="#%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 多维数组 +</h2><p>严格来说,<code>C++</code> 语言中没有多维数组,所谓的多维数组实际是数组的数组。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用范围-for-语句处理多维数组"> + <a href="#%e4%bd%bf%e7%94%a8%e8%8c%83%e5%9b%b4-for-%e8%af%ad%e5%8f%a5%e5%a4%84%e7%90%86%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 使用范围 <code>for</code> 语句处理多维数组 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">row</span><span class="p">:</span> <span class="n">arr</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">col</span><span class="p">:</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">col</span> <span class="o">=</span> <span class="n">cnt</span><span class="p">;</span> <span class="n">cnt</span> <span class="o">++</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch02 + https://3000ye.com/p/c-primer-ch02/ + Mon, 06 Nov 2023 22:34:55 +0800 + + https://3000ye.com/p/c-primer-ch02/ + <img src="https://3000ye.com/p/c-primer-ch02/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch02" /><h1 id="变量和基本类型"> + <a href="#%e5%8f%98%e9%87%8f%e5%92%8c%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b">#</a> + 变量和基本类型 +</h1><p>变量提供一个具名的、可供程序操作的存储空间。<code>C++</code>中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。</p> +<h2 id="变量的声明和定义"> + <a href="#%e5%8f%98%e9%87%8f%e7%9a%84%e5%a3%b0%e6%98%8e%e5%92%8c%e5%ae%9a%e4%b9%89">#</a> + 变量的声明和定义 +</h2><p>为了允许把程序拆分成多个逻辑部分来编写,<code>C++</code>语言支持<strong>分离式编译(separate complication)</strong> 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。</p> +<p>为了支持分离式编译,<code>C++</code>语言将声明和定义区分开来。<strong>声明(declaration)</strong> 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 <strong>定义(definition)</strong> 负责创建与名字关联的实体。</p> +<p>变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 声明 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义并赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义而非声明,extern失效 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><strong>注意:</strong> 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。</p> +<h3 id="标识符"> + <a href="#%e6%a0%87%e8%af%86%e7%ac%a6">#</a> + 标识符 +</h3><p><code>C++</code>的 <strong>标识符(identifier)</strong> 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。</p> +<h3 id="作用域"> + <a href="#%e4%bd%9c%e7%94%a8%e5%9f%9f">#</a> + 作用域 +</h3><p><strong>作用域(scope)</strong> 是程序的一部分,在其中名字有特定的含义,<code>C++</code>语言中大多数作用域都以花括号分隔。</p> +<p>作用域能彼此包含,被包含(嵌套)的作用域称为 <strong>内层作用域(inner scope)</strong>,包含着别的作用域的作用域称为 <strong>外层作用域(outer scope)</strong>。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 全局作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">inner</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span> <span class="c1">// 内层作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用全局变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 局部重新定义,覆盖全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用局部变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 显式访问全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">::</span><span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="复合类型"> + <a href="#%e5%a4%8d%e5%90%88%e7%b1%bb%e5%9e%8b">#</a> + 复合类型 +</h2><p><strong>复合类型(compound type)</strong> 是指基于其他类型定义的类型,主要介绍引用和指针。</p> +<h3 id="引用"> + <a href="#%e5%bc%95%e7%94%a8">#</a> + 引用 +</h3><p><strong>引用(reference)</strong> 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 <strong>绑定(bind)</strong> 在一起,而不是将初始者拷贝给引用。</p> +<blockquote> +<p>引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。</p> +</blockquote> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 引用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 修改 y 的值,实际是修改 x 的值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针"> + <a href="#%e6%8c%87%e9%92%88">#</a> + 指针 +</h3><p><strong>指针(pointer)</strong> 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:</p> +<ul> +<li>指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。</li> +<li>指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ip1</span><span class="p">,</span> <span class="o">*</span><span class="n">ip2</span><span class="p">;</span> <span class="c1">// ip1 和 ip2 都是指向 int 类型对象的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">dp</span><span class="p">,</span> <span class="o">*</span><span class="n">dp2</span><span class="p">;</span> <span class="c1">// dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>指针存放某个对象的地址,想获取该地址,需要使用 <strong>取地址符(<code>&amp;</code>)</strong>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。</p> +</blockquote> +<p>指针的值(即地址)应属于下列 4 种状态之一:</p> +<ul> +<li>指向一个对象。</li> +<li>指向紧邻对象所占空间的下一个位置。</li> +<li>空指针,意味着指针没有指向任何对象。</li> +<li>无效指针,也就是上述情况之外的其他值。</li> +</ul> +<p>如果指针指向了一个对象,则允许使用 <strong>解引用符(<code>*</code>)</strong> 来访问该对象:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">p</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 由符号 * 得到指针 p 所指的对象,输出 42 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>解引用操作仅适用于有效指针,无效指针无法解引用。</p> +</blockquote> +<p><strong>空指针(null pointer)</strong> 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。</p> +<p>生成空指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> <span class="c1">// 等价于 int *p1 = 0 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。</p> +<p>指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi 为空指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">;</span> <span class="c1">// pi2 存放 i 的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi3</span><span class="p">;</span> <span class="c1">// pi3 的值无法确定 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="n">pi3</span> <span class="o">=</span> <span class="n">pi2</span><span class="p">;</span> <span class="c1">// pi3 和 pi2 指向同一个对象 i +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi2 变为空指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="const-限定符"> + <a href="#const-%e9%99%90%e5%ae%9a%e7%ac%a6">#</a> + const 限定符 +</h2><p>有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字<code>const</code>对变量的类型加以限定:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> <span class="c1">// 限定缓冲区大小为 512 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>默认情况下,<code>const</code>对象被设定为仅在单个文件内有效。当多个文件中出现了同名的<code>const</code>变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上<code>extern</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 定义文件 xxx.cpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 声明文件 xxx.hpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针和-const"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c-const">#</a> + 指针和 const +</h3><p>与引用一样,也可以令指针指向常量与非常量。<strong>指向常量的指针(pointer to const)</strong> 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// val 为常量对像,其值不能改变 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 报错,pi 为普通指针,不能存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 使用指向常量的指针存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// 报错,不能给指向常量的指针赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="处理类型"> + <a href="#%e5%a4%84%e7%90%86%e7%b1%bb%e5%9e%8b">#</a> + 处理类型 +</h2><p>随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。</p> +<h3 id="类型别名"> + <a href="#%e7%b1%bb%e5%9e%8b%e5%88%ab%e5%90%8d">#</a> + 类型别名 +</h3><p><strong>类型别名(type alias)</strong> 是一个名字,它是某种类型的同义词。</p> +<p>使用关键字<code>typedef</code>定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用 <strong>别名声明(alias declaration)</strong> 定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="auto-类型说明符"> + <a href="#auto-%e7%b1%bb%e5%9e%8b%e8%af%b4%e6%98%8e%e7%ac%a6">#</a> + auto 类型说明符 +</h3><p>编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此<code>C++11</code>引入了<code>auto</code>类型说明符,它能自动分析表达式结果的类型。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">item</span> <span class="o">=</span> <span class="n">val1</span> <span class="o">+</span> <span class="n">val2</span><span class="p">;</span> <span class="c1">// item 初始化为 val1 和 val2 相加的结果 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>auto</code>定义或声明多个变量时,所有变量的类型必须一致:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mf">4.2</span><span class="p">;</span> <span class="c1">// 报错,同一行的变量类型必须一致 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="decltype-类型指示符"> + <a href="#decltype-%e7%b1%bb%e5%9e%8b%e6%8c%87%e7%a4%ba%e7%ac%a6">#</a> + decltype 类型指示符 +</h3><p>有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。</p> +<p>因此<code>C++11</code>引入了<code>decltype</code>类型指示符,它的作用是返回操作数的数据类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">decltype</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// z 为 int 类型 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="自定义数据结构"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84">#</a> + 自定义数据结构 +</h2><p>内置的数据类型并不能满足所有的需求,因此<code>c++</code>提供了自定义数据类型的方式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">jack</span><span class="p">{</span><span class="s">&#34;jack&#34;</span><span class="p">,</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 先声明后赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">castor</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="n">castor</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;castor&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">sex</span> <span class="o">=</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">gender</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义数据结构使用别名"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%bd%bf%e7%94%a8%e5%88%ab%e5%90%8d">#</a> + 自定义数据结构使用别名 +</h3><p>和内置数据类型一样,自定义数据结构也能使用别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="n">studeng</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 或直接在定义时使用别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编写自己的头文件"> + <a href="#%e7%bc%96%e5%86%99%e8%87%aa%e5%b7%b1%e7%9a%84%e5%a4%b4%e6%96%87%e4%bb%b6">#</a> + 编写自己的头文件 +</h3><p>当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// studeng.h +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp">#define STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;string&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="cp">#endif +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + Slides + https://3000ye.com/p/slides/ + Fri, 20 Oct 2023 10:04:55 +0800 + + https://3000ye.com/p/slides/ + <img src="https://3000ye.com/p/slides/assets/latex.jpg" alt="Featured image of post Slides" /><h1 id="使用-latex-绘制-slides"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e7%bb%98%e5%88%b6-slides">#</a> + 使用 $\LaTeX$ 绘制 Slides +</h1> + + + dhuBachelor + https://3000ye.com/p/dhubachelor/ + Thu, 12 Oct 2023 16:41:55 +0800 + + https://3000ye.com/p/dhubachelor/ + <img src="https://3000ye.com/p/dhubachelor/assets/latex.jpg" alt="Featured image of post dhuBachelor" /><h1 id="东华大学学士毕业论文-latex-模板使用手册"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e5%a3%ab%e6%af%95%e4%b8%9a%e8%ae%ba%e6%96%87-latex-%e6%a8%a1%e6%9d%bf%e4%bd%bf%e7%94%a8%e6%89%8b%e5%86%8c">#</a> + 东华大学学士毕业论文 LaTeX 模板使用手册 +</h1><h2 id="导入模板"> + <a href="#%e5%af%bc%e5%85%a5%e6%a8%a1%e6%9d%bf">#</a> + 导入模板 +</h2><h3 id="使用-git-克隆仓库"> + <a href="#%e4%bd%bf%e7%94%a8-git-%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93">#</a> + 使用 Git 克隆仓库 +</h3><p>将仓库克隆到你需要的位置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@github.com:3000ye/dhuBachelor.git +</span></span><span class="line"><span class="cl"><span class="c1"># 或</span> +</span></span><span class="line"><span class="cl">git clone https://github.com/3000ye/dhuBachelor.git +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="下载-zip-文件"> + <a href="#%e4%b8%8b%e8%bd%bd-zip-%e6%96%87%e4%bb%b6">#</a> + 下载 Zip 文件 +</h3><p>如果你不会使用<code>Git</code>,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载<code>Zip</code>文件:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/zip.png" alt="img" style="zoom:100%"/> +</div> +<h3 id="微信公众号获取"> + <a href="#%e5%be%ae%e4%bf%a1%e5%85%ac%e4%bc%97%e5%8f%b7%e8%8e%b7%e5%8f%96">#</a> + 微信公众号获取 +</h3><p>如果你没有科学代理,无法进入<code>Github</code>,请扫码关注公众号:<code>3000ye Blog</code>然后回复<code>latex模板</code>获取百度网盘分享链接。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/weixin.jpg" alt="img" style="zoom:100%"/> +</div> +<h2 id="开始使用"> + <a href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8">#</a> + 开始使用 +</h2><h3 id="安装字体"> + <a href="#%e5%ae%89%e8%a3%85%e5%ad%97%e4%bd%93">#</a> + 安装字体 +</h3><p>在使用模板之前,请先找到并打开<code>fonts</code>文件夹,安装里面的所有字体。</p> +<h3 id="配置-tex-环境"> + <a href="#%e9%85%8d%e7%bd%ae-tex-%e7%8e%af%e5%a2%83">#</a> + 配置 TeX 环境 +</h3><p>如果你是 $\LaTeX$ 小白,请先行阅读:<a class="link" href="https://3000ye.com/p/elegant-latex/" target="_blank" rel="noopener" + >使用 LaTeX 优雅地完成创作</a>,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。</p> +<h3 id="尝试编译"> + <a href="#%e5%b0%9d%e8%af%95%e7%bc%96%e8%af%91">#</a> + 尝试编译 +</h3><p>使用你喜欢的编辑器,打开<code>dhuBachelor.tex</code>文件,选择<code>xelatex</code>命令编译文件。如果没有出现报错,同目录下会生成一个<code>dhuBachelor.pdf</code>文件,这就是我们的论文。</p> +<h2 id="各个组件说明"> + <a href="#%e5%90%84%e4%b8%aa%e7%bb%84%e4%bb%b6%e8%af%b4%e6%98%8e">#</a> + 各个组件说明 +</h2><p>看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。</p> +<p>本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。</p> +<h3 id="论文题目"> + <a href="#%e8%ae%ba%e6%96%87%e9%a2%98%e7%9b%ae">#</a> + 论文题目 +</h3><p>根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reTitle</span><span class="nb">{</span>中文题目<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\reTitleEN</span><span class="nb">{</span>英文题目<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="摘要"> + <a href="#%e6%91%98%e8%a6%81">#</a> + 摘要 +</h3><p>根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reAbstract</span> <span class="c">% 中文摘要 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reAbstractEN</span> <span class="c">% 英文摘要 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>摘要内容直接写在摘要下方,首行缩进两字符。</p> +<h3 id="关键词"> + <a href="#%e5%85%b3%e9%94%ae%e8%af%8d">#</a> + 关键词 +</h3><p>中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。</p> +<p>英文关键词:Times New Roman(标题加黑)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reKeyword</span><span class="nb">{</span>关键词1,关键词2,关键词3,关键词4<span class="nb">}</span> <span class="c">% 中文关键词 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reKeywordEN</span><span class="nb">{</span>Keyword1, Keyword2, Keyword3<span class="nb">}</span> <span class="c">% 英文关键词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="目录"> + <a href="#%e7%9b%ae%e5%bd%95">#</a> + 目录 +</h3><p>目录标题需居中显示,添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\tableofcontents</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="各级标题"> + <a href="#%e5%90%84%e7%ba%a7%e6%a0%87%e9%a2%98">#</a> + 各级标题 +</h3><p>规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reSection</span><span class="nb">{</span>一级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsection</span><span class="nb">{</span>二级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsubsection</span><span class="nb">{</span>三级标题<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="有序列表"> + <a href="#%e6%9c%89%e5%ba%8f%e5%88%97%e8%a1%a8">#</a> + 有序列表 +</h3><p>规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\orderedList</span><span class="nb">{</span> <span class="c">% 使用 (i) 排序,缩进 2 字符 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> <span class="c">% \par的作用是将内容换行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> +</span></span><span class="line"><span class="cl"> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="数学公式"> + <a href="#%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 数学公式 +</h3><p>行内公式:<code>$f(x) = x + 1$</code></p> +<p>跨行公式:跨行公式请使用<code>equation</code>环境,默认按照章节自动编号。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> x = a<span class="nb">_</span>0 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>1 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>2 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>3 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>4<span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改<code>width</code>大小和图片路径,并根据实际更改图例和索引。</p> +<p>注意:图片需要保存在<code>assets/</code>目录中才能被正确插入,读者可以自己新建其他目录实现插入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] <span class="c">% 图片位于文字下方 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> <span class="c">% 居中 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="c">% 设置图片占页面宽度的比例(默认0.8) +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片标题<span class="nb">}</span> <span class="c">% 图例,按章节编号 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>fig: 数据结构2<span class="nb">}</span> <span class="c">% 图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>有时可能需要多图并排,模板使用<code>minipage</code>来实现并排展示,使用时可以修改子图所占比例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\hspace</span><span class="nb">{</span>1em<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入表格"> + <a href="#%e6%8f%92%e5%85%a5%e8%a1%a8%e6%a0%bc">#</a> + 插入表格 +</h3><p>使用 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a> 工具生成表格代码后,需要手动添加分割线(<code>\toprule, \midrule, \bottomrule</code>),以达到三线表的格式要求。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||l<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> parameter <span class="nb">&amp;</span> Description <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">I</span><span class="s">$</span> <span class="nb">&amp;</span> Land area collection <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">J</span><span class="s">$</span> <span class="nb">&amp;</span> Flower pollination demand set <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">D_j</span><span class="s">$</span> <span class="nb">&amp;</span> Number of pollinating bees required for flower pollination <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">T_k</span><span class="s">$</span> <span class="nb">&amp;</span> Honeycomb size grade, <span class="s">$</span><span class="nb">k </span><span class="o">=</span><span class="nb"> </span><span class="m">1</span><span class="nb">, </span><span class="m">2</span><span class="nb">, </span><span class="nv">\cdots</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">B</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum number of hive <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">R_{ik}</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum influence radius of a single honeycomb <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 一个表<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>对于需要多表并排的情况,和图片的方式类似,使用<code>minipage</code>来实现:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格1标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格2标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bb%a3%e7%a0%81">#</a> + 插入代码 +</h3><p>可以直接在<code>.tex</code>文件中编写代码,并指定语言和标题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>lstlisting<span class="nb">}</span>[language=c++,title=<span class="nb">{</span>code.cpp<span class="nb">}</span>] +</span></span><span class="line"><span class="cl">#include &#34;bits/stdc++.h&#34; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">using namespace std; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">int main() <span class="nb">{</span> +</span></span><span class="line"><span class="cl"> cout &lt;&lt; &#34;3000ye 的 LaTeX 模板!&#34; &lt;&lt; endl; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> return 0; +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>lstlisting<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在<code>assets/</code>目录下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\lstinputlisting</span><span class="na">[language=c++, title=code.cpp]</span><span class="nb">{</span>code/code.cpp<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入伪代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bc%aa%e4%bb%a3%e7%a0%81">#</a> + 插入伪代码 +</h3><p>使用宏包<code>algorithm, algorithmic</code>来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>Example Pseudocode<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="m">0</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\IF</span> <span class="nb">{</span><span class="s">$</span><span class="nb">x</span><span class="nv">\leq</span><span class="nb"> </span><span class="m">0</span><span class="s">$</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">+</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ELSE</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">-</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ENDIF</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="参考文献"> + <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae">#</a> + 参考文献 +</h3><p>参考文献使用<code>\bibitem</code>来添加,添加时需要手动更改<code>{RNi}</code>索引(<code>i</code>是你文献的序号)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reference</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN1<span class="nb">}</span> 参考文献1 +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN2<span class="nb">}</span> 参考文献2 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="致谢"> + <a href="#%e8%87%b4%e8%b0%a2">#</a> + 致谢 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reThanks</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> 致谢,3000ye 的 <span class="k">\LaTeX</span> 模板! +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Elegant LaTeX + https://3000ye.com/p/elegant-latex/ + Mon, 09 Oct 2023 17:18:55 +0800 + + https://3000ye.com/p/elegant-latex/ + <img src="https://3000ye.com/p/elegant-latex/assets/latex.jpg" alt="Featured image of post Elegant LaTeX" /><h1 id="使用-latex-优雅地完成创作"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e4%bc%98%e9%9b%85%e5%9c%b0%e5%ae%8c%e6%88%90%e5%88%9b%e4%bd%9c">#</a> + 使用 $\LaTeX$ 优雅地完成创作 +</h1><p>$\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\LaTeX$ 使用 $\TeX$ 作为它的排版引擎,学习 $\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。</p> +<h2 id="从安装-tex-引擎开始"> + <a href="#%e4%bb%8e%e5%ae%89%e8%a3%85-tex-%e5%bc%95%e6%93%8e%e5%bc%80%e5%a7%8b">#</a> + 从安装 $\TeX$ 引擎开始 +</h2><p>$\TeX$ 引擎类似于 <code>gcc/g++</code> 或 <code>Python</code>,用于编译 $\LaTeX$ 文档。</p> +<p>不同平台中 $\TeX$ 的安装方法不尽相同,本文提供:<code>Windows11</code>、<code>Linux(Ubuntu 22.04)</code>、<code>MacOs(12.7)</code>、<code>Windows11-wsl2(Ubuntu22.04)</code>的安装方法。</p> +<p>如果你只是想简单体验 $\LaTeX$,可以使用 <a class="link" href="https://www.overleaf.com/" target="_blank" rel="noopener" + >overleaf</a> 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。</p> +<h3 id="windows11"> + <a href="#windows11">#</a> + Windows11 +</h3><p>进入网站<a class="link" href="https://tug.org/texlive/windows.html#install" target="_blank" rel="noopener" + >tug.org for windows</a>,点击<code>install-tl-windows.exe</code>下载 $\TeX$ 安装器,然后运行安装即可。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/windowsInstall.png" alt="img" style="zoom:100%"/> +</div> +<p>不过,这种方法需要一直联网安装,网速不好的环境可以直接下载<code>iso</code>镜像进行本地安装。本文给出清华源镜像地址:<a class="link" href="https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/" target="_blank" rel="noopener" + >mirrors.tuna</a>,下载后缀为<code>.iso</code>的文件(只用下载一个)。</p> +<p>下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击<code>install-tl-windows.bat</code>文件,使用管理员打开,然后按照指引安装即可。</p> +<p>最新版本的安装器会自动添加环境变量,安装完成后打开<code>cmd</code>然后输入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/W32TeX<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="linuxubuntu2204"> + <a href="#linuxubuntu2204">#</a> + Linux(Ubuntu22.04) +</h3><p>打开终端,然后执行安装命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update +</span></span><span class="line"><span class="cl">sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install texlive-full +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/Debian<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="macos127"> + <a href="#macos127">#</a> + MacOs(12.7) +</h3><p>打开终端,然后执行安装命令(推荐安装无窗体版本):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install mactex-no-gui +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="windows11-wsl2ubuntu2204"> + <a href="#windows11-wsl2ubuntu2204">#</a> + Windows11-wsl2(Ubuntu22.04) +</h3><p>在<code>wsl2</code>中安装方式与在<code>Linux</code>中一样。</p> +<h2 id="找到属于你的编辑器"> + <a href="#%e6%89%be%e5%88%b0%e5%b1%9e%e4%ba%8e%e4%bd%a0%e7%9a%84%e7%bc%96%e8%be%91%e5%99%a8">#</a> + 找到属于你的编辑器 +</h2><p>市面上有很多 $\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:</p> +<ul> +<li>全平台通用: +<ul> +<li><code>Vs Code</code>:作为地表最强编辑器,<code>Vs Code</code>拥有非常丰富的 $\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。</li> +<li><code>Jetbrains</code>: 与<code>Vs Code</code>相对应的是<code>Jetbrains</code>系列, 其虽然也有 $\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。</li> +<li><code>Neovim</code>:如果说<code>Vs Code</code>是编辑器中的王后,那么<code>nvim</code>就是国王。<code>nvim</code>可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。</li> +<li><code>sublime text</code>:<code>nvim</code>固然强大,但其难以上手的特点使得很多人对其望而却步。<code>sublime</code>打破了这个束缚,其界面优雅程度不亚于<code>nvim</code>,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。</li> +<li><code>TexStudio</code>:<code>texlive</code>默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。</li> +</ul> +</li> +<li><code>Windows</code>独占: +<ul> +<li><code>Winedt 11</code>:如果不考虑跨平台,那么<code>Winedt 11</code>就是<code>Windows</code>上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。</li> +</ul> +</li> +</ul> +<p>综上所述,笔者最推荐<code>Vs Code</code>,但如果你只有<code>Windows</code>平台的使用需求并不介意一点费用的话,请果断购买<code>Winedt 11</code>。同样的,<code>MacOs</code>也拥有独占编辑器,但笔者没用过,在此不做评价。</p> +<p>关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。</p> +<h2 id="一本教程入门-latex-语法"> + <a href="#%e4%b8%80%e6%9c%ac%e6%95%99%e7%a8%8b%e5%85%a5%e9%97%a8-latex-%e8%af%ad%e6%b3%95">#</a> + 一本教程入门 $\LaTeX$ 语法 +</h2><p>对于所有初学者来说,<a class="link" href="https://mirror-hk.koddos.net/CTAN/info/lshort/chinese/lshort-zh-cn.pdf" target="_blank" rel="noopener" + >Ishort-zh-cn</a>都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。</p> +<p>如果你已经完成了所有的案例,相信你已经对 $\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。</p> +<h3 id="打印中文"> + <a href="#%e6%89%93%e5%8d%b0%e4%b8%ad%e6%96%87">#</a> + 打印中文 +</h3><p>$\LaTeX$ 默认只打印英文,如果没有合理的设置,<code>.tex</code>文件中的中文将无法正确打印。</p> +<p>从下图可以看出,目前支持全平台通用的方案只有<code>XeLaTeX</code>和<code>LuaTeX</code>。因此,主流方案是一般使用<code>xelatex+ctex</code>编译方案,底层调用<code>xeCJK</code>字符集来实现中文打印。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/xeCJK.png' alt='img' style='zoom:100%;' /> +</div> +<p>使用时只需在导言区加入,编译器会使用默认字体进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8]</span><span class="nb">{</span>ctex<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果要指定字体,则需分别设置<code>font-family</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8, fontset=none]</span><span class="nb">{</span>ctex<span class="nb">}</span> <span class="c">% 清除默认字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmainfont</span><span class="nb">{</span>SimSun<span class="nb">}</span>[AutoFakeBold=true, BoldFont=<span class="nb">{</span>SimHei<span class="nb">}</span>, ItalicFont=<span class="nb">{</span>KaiTi<span class="nb">}</span>] <span class="c">% 正文字体(宋体,黑体,楷体) +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKsansfont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>KaiTi<span class="nb">}</span> <span class="c">% 无衬线字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmonofont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>SimHei<span class="nb">}</span> <span class="c">% 等宽字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>同样的,英文也可以自定义字体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\setmainfont</span><span class="nb">{</span>Times New Roman<span class="nb">}</span> <span class="c">% 设置英文字体为新罗马体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>详细配置可以参考:<a class="link" href="https://zhuanlan.zhihu.com/p/538459335" target="_blank" rel="noopener" + >LaTex 中文字体配置指南</a></p> +</blockquote> +<h3 id="打印数学公式"> + <a href="#%e6%89%93%e5%8d%b0%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 打印数学公式 +</h3><p>你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\LaTeX$,这也是 $\LaTeX$ 为什么被奉为珍宝的原因之一。</p> +<p>但是笔者并不建议读者专门花时间来学习如何编写 $\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。</p> +<ul> +<li>在线数学公式生成平台:<a class="link" href="https://www.latexlive.com/" target="_blank" rel="noopener" + >latexlive</a>可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。</li> +<li><code>MathType</code>:如果你是<code>Windows</code>用户,那么强烈建议使用<code>MathType</code>来生成数学公式的 $\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。</li> +<li><code>mathpix</code>:这是一款专为 $\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。</li> +</ul> +<h3 id="绘制表格"> + <a href="#%e7%bb%98%e5%88%b6%e8%a1%a8%e6%a0%bc">#</a> + 绘制表格 +</h3><p>你一定使用过<code>Excel</code>来绘制表格,但是在 $\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。</p> +<p>但不用担心,本文为你介绍开源项目 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a>。这是一款<code>Excel</code>插件,可以将你在<code>Excel</code>中绘制的表格自动转译为 $\LaTeX$ 代码。</p> +<p>但是这个插件并不是万能的,比如绘制三线表,即使是在<code>Excel</code>中也较为繁琐。因此,最好的处理方式是使用<code>excel2latex</code>插件生成表格主体,然后再自己添加分隔格式。</p> +<h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>绘制表格和插入图片并称为 $\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。</p> +<h2 id="学会使用代码片段"> + <a href="#%e5%ad%a6%e4%bc%9a%e4%bd%bf%e7%94%a8%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + 学会使用代码片段 +</h2><p>阅读到这里,相信你已经能够使用 $\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写<code>.sty</code>和<code>.cls</code>文件),但这对初学者来说太难了。</p> +<p>因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。</p> +<p>代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。</p> +<h3 id="vs-code配置代码片段"> + <a href="#vs-code%e9%85%8d%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Vs Code配置代码片段 +</h3><p>点击左下角的设置按钮,然后点击设置用户代码片段:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/snippets.jpg' alt='img' style='zoom:100%;' /> +</div> +<p>在弹出的窗口中输入<code>latex</code>然后选中即可跳转到<code>latex.json</code>文件,我们可以在这里设置我们的代码片段。</p> +<p>比如这段设置,保存文件后我们只需要在<code>.tex</code>后缀的文件中输入<code>insertImg</code>然后回车就会自动填充以下代码,并且使用<code>tab</code>来依次输入参数。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;insertImg&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;prefix&#34;</span><span class="p">:</span> <span class="s2">&#34;insertImg&#34;</span><span class="p">,</span> <span class="c1">// 代码片段别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;body&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="c1">// 代码片段主体 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\begin{figure}[H]&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\centering&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\includegraphics[width=0.8\\textwidth]{$1}&#34;</span><span class="p">,</span> <span class="c1">// 参数1:图片路径 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\caption{$2}&#34;</span><span class="p">,</span> <span class="c1">// 参数2:图片标题 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\label{$3}&#34;</span><span class="p">,</span> <span class="c1">// 参数3:图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\end{figure}$0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;insert one img with 0.8 width&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="winedt-设置代码片段"> + <a href="#winedt-%e8%ae%be%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Winedt 设置代码片段 +</h3><p>依次点击<code>Option -&gt; Options Interface -&gt; Menus and Toolbar -&gt; Main Menu</code>,修改或添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">ITEM</span><span class="o">=</span><span class="s">&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> CAPTION=&#34;&amp;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> IMAGE=&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> MACRO=&#34;Exe(&#39;%b\Menus\Insert\Image.edt&#39;);&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> SHORTCUT=&#34;49222::Ctrl+Alt+F&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> REQ_DOCUMENT=1 </span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后就可以使用快捷键<code>Ctrl+Alt+F</code>填充插入图片的代码片段。</p> +<h2 id="使用模板来专注内容"> + <a href="#%e4%bd%bf%e7%94%a8%e6%a8%a1%e6%9d%bf%e6%9d%a5%e4%b8%93%e6%b3%a8%e5%86%85%e5%ae%b9">#</a> + 使用模板来专注内容 +</h2><p>使用 $\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\LaTeX$ 的初衷。</p> +<p>因此,常见期刊都会提供对应的 $\LaTeX$ 风格模板和示例,其中主要文件通常为:</p> +<ul> +<li><code>.sty</code>:$\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。</li> +<li><code>.cls</code>:$\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。</li> +<li><code>.tex</code>:示例文件,通常会包括论文中会用到的所有样式的示例代码。</li> +</ul> +<p>阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。</p> + + + + Camera Calibration + https://3000ye.com/p/camera-calibration/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/camera-calibration/ + <img src="https://3000ye.com/p/camera-calibration/board.jpg" alt="Featured image of post Camera Calibration" /><h1 id="由浅到深理解相机标定"> + <a href="#%e7%94%b1%e6%b5%85%e5%88%b0%e6%b7%b1%e7%90%86%e8%a7%a3%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 由浅到深理解相机标定 +</h1><h2 id="何为相机标定"> + <a href="#%e4%bd%95%e4%b8%ba%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 何为相机标定 +</h2><p>在图像测量过程以及机器视觉应用中,为确定空间无哦表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。</p> +<p>在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为<strong>相机标定(或摄像头标定)</strong>。</p> +<p><strong>相机标定</strong>涉及的知识面很广:成像几何、镜头畸变、单应矩阵、非线性优化等。</p> +<p><strong>相机标定</strong>有自标定(找图像中特征点)、标定板标定(特征点易求,稳定性好),一般采用标定板标定。</p> +<p><strong>相机标定</strong>按照相机是否静止,可分为静态相机标定(标定板动,相机静止),动态相机标定(标定板静止,相机运动)。</p> +<h3 id="为什么需要标定"> + <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e6%a0%87%e5%ae%9a">#</a> + 为什么需要标定 +</h3><p>任何理论物理模型都是在特定假设上对真实事物的近似,然而在实际应用中存在误差,普通相机的成像模型也不例外(透视投影)。</p> +<p>实际中,普通相机成像误差的主要来源有两部分:</p> +<ul> +<li>第一是sensor(传感器)制造产生的误差,比如sensor成像单元不是正方形,sensor歪斜。</li> +<li>第二是镜头制造和安装产生的误差,镜头一般存在非线性的径向畸变;镜头与相机sensor安装不平行,还会产生切向畸变。</li> +</ul> +<h2 id="相机标定的目的和意义"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e7%9a%84%e7%9b%ae%e7%9a%84%e5%92%8c%e6%84%8f%e4%b9%89">#</a> + 相机标定的目的和意义 +</h2><p>我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。</p> +<p>相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp" + width="898" + height="615" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp 480w, https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="146" + data-flex-basis="350px" + +></p> +<p>这个逼近的过程就是「相机标定」,我们用简单的数学模型来表达复杂的成像过程,并且求出成像的反过程。标定之后的相机,可以进行三维场景的重建,即深度的感知。</p> +<h2 id="相关术语"> + <a href="#%e7%9b%b8%e5%85%b3%e6%9c%af%e8%af%ad">#</a> + 相关术语 +</h2><p><strong>焦点:<strong>在几何光学中有时也称为</strong>像点</strong>,是源头的光线经过物镜后汇聚的点。</p> +<p><strong>焦距:<strong>也称为</strong>焦长</strong>,是光学系统中衡量光的聚集或发散的度量方式,指从透镜中心到光聚集之焦点的距离。亦是照相机中,从镜片光学中心到底片、CCD或CMOS等成像平面的距离。</p> +<p>正透镜、负透镜、凹面镜和凸面镜的焦点<code>F</code>和焦距<code>f</code>:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/1.png" alt="img" style="zoom:50%;" /> +</div> +<p><strong>镜头(Lenses)</strong>:是将拍摄景物在传感器上成像的器件,它通常由几片透镜、光圈叶片、对焦马达等光学元件组成。</p> +<p><strong>传感器(Sensor)</strong>:是摄像头组成的核心,其作用是作为相机的感光元件。摄像头传感器主要有两种,一种是CCD传感器,一种是CMOS传感器,两者区别在于:CCD的优势在于成像质量好,但是由于制造工艺复杂,成本居高不下,特别是大型CCD,价格非常高昂。在相同分辨率下,CMOS价格比CCD便宜,但是CMOS器件产生的图像质量相比CCD来说要低一些。</p> +<p><strong>光心</strong>:凸透镜近轴光线中,入射线和与其对应且相平行的出射线构成共轭光线,其入射点跟出射点的连线与主光轴的交点,称为凸透镜的焦点,位于透镜中央的点叫光心。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/3.png" alt="img" style="zoom:50%;" /> +</div> +<p>从图中可知,<code>O</code>为光心,<code>F</code>为焦点。每个透镜主轴上都有一个特殊点,凡是通过该点的光,其传播方向不变,这个点叫光心。经过光心的光线的传播方向不会发生改变。</p> +<h2 id="相机标定原理模型"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e5%8e%9f%e7%90%86%e6%a8%a1%e5%9e%8b">#</a> + 相机标定原理模型 +</h2><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定.drawio" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<h3 id="针孔相机模型"> + <a href="#%e9%92%88%e5%ad%94%e7%9b%b8%e6%9c%ba%e6%a8%a1%e5%9e%8b">#</a> + 针孔相机模型 +</h3><p>我们通常将相机看成如下所示的透镜模型:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/2.png" alt="img" style="zoom:50%;" /> +</div> +<p>在实际分析时,通常将其简化为针孔模型(小孔成像):</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/4.png" alt="img" style="zoom:50%;" /> +</div> +<p>一般为了分析简单,将成像平面画在对称位置,这样图像不再颠倒:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/5.png" alt="img" style="zoom:50%;" /> +</div> +<h3 id="四个坐标系"> + <a href="#%e5%9b%9b%e4%b8%aa%e5%9d%90%e6%a0%87%e7%b3%bb">#</a> + 四个坐标系 +</h3><p><strong>世界坐标系</strong>:用户定义的三维世界的坐标系,用于描述目标物体在真实世界里的位置。单位通常为米(m)。该坐标系作用于三维空间。</p> +<p><strong>相机坐标系</strong>:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位通常为米(m)。相机坐标系的原点在光心,其 $X_c、Y_c$ 轴分别与像面的两边平行,其 $Z_c$ 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点(实际情况中可能存在<strong>主点偏移</strong>),相机坐标系与图像坐标系之间的距离为焦距 $f$。该坐标系作用于三维空间。</p> +<p><strong>图像坐标系</strong>:为了描述成像过程中物体从相机坐标系到图像坐标系的投影投射关系而引入,方便进一步得到像素坐标系下的坐标。其原点是相机光轴与像面的交点(称为主点),即<strong>图像的中心点</strong>。其 $x, y$ 轴和像素坐标系的 $u, v$ 轴平行,故图像坐标系和像素坐标系实际是平移关系。单位通常为毫米(mm)。该坐标系作用于二维空间。</p> +<p><strong>像素坐标系</strong>:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为像素。像素坐标平面和图像坐标系平面重合,但像素坐标系原点位于图像左上角。该坐标系作用于二维空间。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/6.png" + width="1248" + height="841" + srcset="https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="148" + data-flex-basis="356px" + +></p> +<h3 id="相机外参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%a4%96%e5%8f%82">#</a> + 相机外参 +</h3><p>将世界坐标系中的点映射到相机坐标系:相机坐标系是世界坐标系通过<strong>刚体变换</strong>得到的。</p> +<blockquote> +<p>刚体变换能够保持物体中各点的距离和角度,常见的刚体变换有:平移、旋转和镜像。</p> +</blockquote> +<p>我们先只考虑旋转,假设将坐标系以 $X$ 轴为中心进行旋转,即 $X$ 不变,旋转 $Y - Z$ 平面。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/7.png" alt="img" style="zoom: 33%;" /> +</div> +<p>假设旋转角度为 $\theta$,即 $\angle Y&rsquo; O Y = \angle Z&rsquo; O Z = \theta$。旋转前的坐标系为 $X - Y - Z$,旋转后的坐标系为 $X&rsquo; - Y&rsquo; - Z&rsquo;$。假设点 $P$ 在 $X - Y - Z$ 中的坐标为($X_w, Y_w, Z_w$),旋转后,其在 $X&rsquo; - Y&rsquo; - Z&rsquo;$ 中的坐标为($X_c, Y_c, Z_c$): +$$ +X_C = X_w +$$</p> +<p>$$ +\begin{array}{l} +Y_c &amp; = OC + CD = OA \cdot \sin \theta + BP \\ +&amp; = Z_w \cdot \sin \theta + AP \cdot \cos \theta \\ +&amp; = Z_w \sin \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>$$ +\begin{array}{l} +Z_c &amp; = PD = AC - AB \\ +&amp; = AO \cdot \cos \theta - AP \cdot \cos \theta \\ +&amp; = Z_w \cos \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>写成矩阵形式: +$$\displaystyle \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} = \mathbf{R_{cw}} \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} or \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} = \mathbf{R_{wc}} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} $$ +推广到每个方向,可得到 $\mathbf{R_{cw}}, \mathbf{R_{wc}}$ 为: +$$ +\mathbf{R_{cw}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; \sin \theta \\ +0 &amp; - \sin \theta &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; - \sin \theta \\ +0 &amp; \sin \theta &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; \sin \theta \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; - \sin \theta \\ +0 &amp; 1 &amp; 0 \\ +\sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; \sin \theta &amp; 0 \\ - \sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +, +\mathbf{R_{wc}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; - \sin \theta &amp; 0 \\ +\sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +$$</p> +<p>这里我们使用右手笛卡尔三维坐标系:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/14.png" alt="img" style="zoom: 25%;" /> +</div> +<p>旋转可分为<strong>主动旋转</strong>与<strong>被动旋转</strong>。<strong>主动旋转</strong>是指将向量逆时针围绕旋转轴所做出的旋转。<strong>被动旋转</strong>是对坐标轴本身进行的逆时针旋转,它相当于主动旋转的逆操作。关于右手笛卡尔坐标系的 $X, Y, Z$ 轴的旋转分别叫做<code>roll</code>,<code>pitch</code>和<code>yaw</code>旋转:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/15.png" alt="img" style="zoom: 20%;" /> +</div> +<p>因为逆时针和顺时针旋转会得到不一样的旋转矩阵,所以我们统一如下:</p> +<p>绕 $X$ 轴的主动旋转定义为($\theta_x$ 是<code>roll</code>角 ): +$$ +R(X_A, \theta_x) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta_x &amp; - \sin \theta_x \\ +0 &amp; \sin \theta_x &amp; \cos \theta_x +\end{bmatrix} = +\exp \left ( \theta_x +\begin{bmatrix} +0 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; -1\\ +0 &amp; 1 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Y$ 轴的主动旋转定义为($\theta_y$ 是<code>pitch</code>角): +$$ +R(Y_A, \theta_y) = +\begin{bmatrix} +\cos \theta_y &amp; 0 &amp; \sin \theta_y \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta_y &amp; 0 &amp; \cos \theta_y +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; 0 &amp; 1\\ +0 &amp; 0 &amp; 0\\ -1 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Z$ 轴的主动旋转定义为($\theta_z$ 是<code>yaw</code>角): +$$ +R(Z_A, \theta_z) = +\begin{bmatrix} +\cos \theta_z &amp; - \sin \theta_z &amp; 0 \\ +\sin \theta_z &amp; \cos \theta_z &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; -1 &amp; 0\\ +1 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +将上述三个旋转矩阵结合起来,最终的旋转矩阵(设绕 $X, Y, Z$ 轴旋转的角度分别为 $\alpha, \beta, \gamma$): +$$ +\begin{array}{ll} +M(\alpha, \beta, \gamma) &amp; = R_x(\alpha) R_y(\beta) R_z(\gamma) \\ +&amp; = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \alpha &amp; - \sin \alpha \\ +0 &amp; \sin \alpha &amp; \cos \alpha +\end{bmatrix} +\begin{bmatrix} +\cos \beta &amp; 0 &amp; \sin \beta \\ +0 &amp; 1 &amp; 0 \\ - \sin \beta &amp; 0 &amp; \cos \beta +\end{bmatrix} +\begin{bmatrix} +\cos \gamma &amp; -\sin \gamma &amp; 0 \\ +\sin \gamma &amp; \cos \gamma &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} \\ +&amp; = \begin{bmatrix} +\cos \gamma \cos \beta &amp; - \sin \gamma \cos \alpha + \cos \gamma \sin \beta \sin \alpha &amp; \sin \gamma \sin \alpha + \cos \gamma \sin \beta \cos \alpha \\ +\sin \gamma \cos \beta &amp; \cos \gamma \cos \alpha + \sin \gamma \sin \beta \sin \alpha &amp; - \cos \gamma \sin \alpha + \sin \gamma \sin \beta \cos \alpha \\ - \sin \beta &amp; \cos \beta \sin \alpha &amp; \cos \beta \cos \alpha +\end{bmatrix} +\end{array} +$$</p> +<p>此时我们再加上平移向量 $T$ 便可完成从世界坐标系到相机坐标系的这个刚体变换了:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c +\end{bmatrix} = +\begin{bmatrix} +r_{11} &amp; r_{12} &amp; r_{13} \\ +r_{21} &amp; r_{22} &amp; r_{23} \\ +r_{31} &amp; r_{32} &amp; r_{33} +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + +\begin{bmatrix} +t_x \\ +t_y \\ +t_z +\end{bmatrix} = +\mathbf{R} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + T +$$</p> +<p>可进一步写成如下形式:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0_3^T &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$\mathbf{R}$ 和 $\mathbf{T}$ 便是相机外参。</p> +<h3 id="相机内参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%86%85%e5%8f%82">#</a> + 相机内参 +</h3><p>首先考虑图像坐标系($xy$)和像素坐标系($uv$)之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/8.png" + width="369" + height="245" + srcset="https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="150" + data-flex-basis="361px" + +></p> +<p>$$ +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix}= +\begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix} +$$</p> +<p>$dx$ 表示一个像素点在 $x$ 方向的长度是多少毫米,$dy$ 表示一个像素点在 $y$ 方向的长度是多少毫米;$(u_0, v_0)$ 为图像的中心点。</p> +<p>然后考虑相机坐标系和图像坐标系之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/9.png" + width="353" + height="346" + srcset="https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="102" + data-flex-basis="244px" + +></p> +<p>$$ +\Delta ABO_c \sim \Delta oCO_c, \Delta PBO_c \sim \Delta pCO_c +$$ +$$ +\displaystyle \frac{AB}{oC} = \frac{AO_c}{oO_c} = \frac{PB}{p C} = \frac{X_c}{x} = \frac{Z_c}{f} = \frac{Y_c}{y} +$$ +$$ +x = f \displaystyle \frac{X_c}{Z_c}, y = f \frac{Y_c}{Z_c} +$$</p> +<p>$$ +Z_c \begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix}= +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$f$ 是焦距,结合外参我们最终可以得到世界坐标系和像素坐标系之间的映射关系:</p> +<p>$$ +\begin{array}{l} +\lambda \begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} &amp; = +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix}\\ +&amp; = +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +\end{array} +$$ +其中,相机内参为(不考虑图像传感器的特性): +$$ +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +$$</p> +<p>其中,$f_x, f_y$ 即为焦距的物理距离在像素坐标系中的长度,相机内参标定主要是标定相机的焦距、主点、歪斜等内部参数。</p> +<h2 id="可能存在的影响"> + <a href="#%e5%8f%af%e8%83%bd%e5%ad%98%e5%9c%a8%e7%9a%84%e5%bd%b1%e5%93%8d">#</a> + 可能存在的影响 +</h2><h3 id="主点偏移"> + <a href="#%e4%b8%bb%e7%82%b9%e5%81%8f%e7%a7%bb">#</a> + 主点偏移 +</h3><p>主点是光轴和相机成像平面的交点,在理想情况下,图像坐标系和相机坐标系原点重合,不存在坐标系偏移。但在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动:</p> +<div style="display: flex; justify-content: center;"> + <img src="assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png" alt="img" style="zoom: 67%;" /> +</div> +<p>考虑主点偏移后,图像坐标和3D在相机坐标系的关系为:</p> +<p>$$ +\begin{matrix} +u = f \frac{X}{Z} + O_x \\ +v = f \frac{X}{Z} + O_y +\end{matrix} +$$ +此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; 0 &amp; O_x &amp; 0 \\ +0 &amp; f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>仔细观察就会发现,该关系与上面提到的关系是等价的,只不过上面使用坐标 $(u_0, v_0)$ 来代表偏移量 $(O_x, O_y)$。</p> +<h3 id="图像传感器特征"> + <a href="#%e5%9b%be%e5%83%8f%e4%bc%a0%e6%84%9f%e5%99%a8%e7%89%b9%e5%be%81">#</a> + 图像传感器特征 +</h3><p>图像传感器像原尺寸在制造过程可能不是正方形,同时可能存在歪斜(skewed),因此需要考虑这些影响因素,传感器歪斜和不是正方形主要对相机 $x$ 和 $y$ 方向的焦距产生影响。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/v2-a812da34b739588fa9142c46839ad281_1440w.png" alt="img" style="zoom: 50%;" /> +</div> +<p>此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; s &amp; O_x &amp; 0 \\ +0 &amp; \eta f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = [K, 0_3] P +$$ +其中,$K$ 矩阵即为最终的内参矩阵。</p> +<h3 id="镜头畸变"> + <a href="#%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 镜头畸变 +</h3><p>小孔成像模型虽然充分考虑了相机内部参数对成像的影响,但没有考虑成像系统另一个重要的部分,镜头。镜头常用的有普通镜头、广角镜头、鱼眼镜头等,在无人驾驶和视觉slam领域,鱼眼镜头和广角镜头用的很多,主要是视角很大,可以观测到更多的信息。任何镜头都存在不同程度的畸变,不同类型的镜头用到的畸变模型也不相同。</p> +<p>在几何光学和阴极射线管(CRT)显示中,畸变(distortion)是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括<strong>径向畸变(radial distortion)<strong>和</strong>切向畸变(tangential distortion)</strong>。</p> +<p>径向畸变来自于透镜形状,主要是由于透镜不同部位放大倍率不同造成的。切向畸变来自于整个相机的组装过程,主要是由于透镜安装与成像平面不平行造成的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/10.png" + width="456" + height="300" + srcset="https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="152" + data-flex-basis="364px" + +></p> +<h4 id="径向畸变"> + <a href="#%e5%be%84%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 径向畸变 +</h4><p>透过镜头边缘的光线很容易产生径向畸变,这种现象来源于“筒形”或“鱼眼”的影响。光线离镜头中心越远,畸变越大。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png" + width="765" + height="248" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="308" + data-flex-basis="740px" + +></p> +<p>从图像可以看出,径向畸变以某一个中心往外延伸,且越往外,畸变越大;显然畸变与距离成一种非线性的变换关系,参考众多文献,可以用多项式来近似: +$$ +\begin{matrix} +x_{rcrt} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\\\ +y_{rcrt} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$k_1, k_2, k_3$ 是径向畸变系数,$r^2 = x^2 + y^2$。</p> +<h4 id="切向畸变"> + <a href="#%e5%88%87%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 切向畸变 +</h4><p>切向畸变主要发生在相机sensor和镜头不平行的情况下;因为有夹角,所以光透过镜头传到图像传感器上时,成像位置发生了变化。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/11.png" + width="926" + height="385" + srcset="https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="240" + data-flex-basis="577px" + +> +$$ +\begin{matrix} +x_{tcrt} = x + [2p_1 xy + p_2 (r^2 + 2 x^2)] \\\\ +y_{tcrt} = y + [2p_2 xy + p_1 (r^2 + 2 y^2)] +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$p_1, p_2$ 是切向畸变系数,$r^2 = x^2 + y^2$。</p> +<h3 id="消除镜头畸变"> + <a href="#%e6%b6%88%e9%99%a4%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 消除镜头畸变 +</h3><p>考虑镜头畸变前,我们可以将相机标定简单描述为以下过程:像素坐标 $(u_{ccd}, v_{ccd})$ $\to$ 图像坐标 $(x, y)$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<p>此时我们考虑加入镜头畸变: +$$ +\begin{matrix} +x_{crt} = x_{rcrt} + x_{tcrt} \\\\ +y_{crt} = y_{rcrt} + y_{tcrt} +\end{matrix} +$$ +得到消除镜头畸变的相机标定流程:像素坐标 $(u_{ccd - crt}, v_{ccd - crt})$ $\to$ 图像坐标 $(x_{crt}, y_{crt})$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<h2 id="标定板的作用"> + <a href="#%e6%a0%87%e5%ae%9a%e6%9d%bf%e7%9a%84%e4%bd%9c%e7%94%a8">#</a> + 标定板的作用 +</h2><h3 id="相机标定中的参数"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e4%b8%ad%e7%9a%84%e5%8f%82%e6%95%b0">#</a> + 相机标定中的参数 +</h3><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<p>针孔相机模型中,只要确定这9个参数就可以唯一的确定针孔相机模型:</p> +<p>$$ +f_x,f_y,O_x,O_y,k_1,k_2,k_3,p_1,p_2 +$$</p> +<p>这个过程就称为「相机标定」,其中前4个我们称为内参数,后5个称为畸变参数,畸变参数是为了补充内参的。所以一旦相机结构固定,包括镜头结构固定,对焦距离固定,我们就可以用这9个的参数去近似这个相机。这里说的「镜头结构固定」,按我个人的理解,除了焦距固定之外,也应当包含光圈固定,因为改变光圈的大小,除了景深之外,是有可能改变针孔相机模型中的光心位置,但是影响并不是很大。这意味着标定好的相机如果改变光圈大小,会使得标定误差变大但应该不会大到难以接受的地步。</p> +<p>对于针孔相机本身需要拟合的方程如下:</p> +<p>$$ +\begin{bmatrix} +u_{ccd - crt} * Z\\ +v_{ccd - crt} * Z\\ +Z +\end{bmatrix} = +J(k_1, k_2, k_3, p_1, p_2) +\begin{bmatrix} +f_x &amp; 0 &amp; O_x \\ +0 &amp; f_y &amp; O_y \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X \\ +Y \\ +X +\end{bmatrix} +$$</p> +<p>因此,我们现在的任务就是找出一大堆具有对应关系的像点 ${(u_{ccd - crt}, v_{ccd - crt}) ^T }$ 和物点 ${ (X, Y, Z)^T }$ 的点作为样本,来训练出模型的参数。这里就引发了两个问题:</p> +<ul> +<li>这么多像点和物点如何匹配?</li> +<li>即便现在知道物点的位置,如何用相机坐标系来表达物点的位置 $(X, Y, Z)$?</li> +</ul> +<p>为了解决上述问题,标定板应运而生。标定板的一大作用,确定物点和像点的对应性。这里用到的原理主要是「透视不变性」,打个比方,你近看一个人和远看一个人,虽然他的鼻子大小变了,你看鼻子的视角也变了,但是拓扑结构肯定是不变的,你也不可能把鼻子看成是嘴巴。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png" + width="803" + height="363" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="221" + data-flex-basis="530px" + +></p> +<p>所以在标定板中,印刷了拓扑结构,广泛应用的是棋盘格和圆点格,这两种之所以成为主流,不仅是因为它们的拓扑结构明确且均匀,更重要的是检测其拓扑结构的算法简单且有效。棋盘格检测的是角点,只要对拍摄到的棋盘格图像横纵两个方向计算梯度就可获得;而圆点格的检测只需要对拍摄到的圆点格图样计算质心即可。假如你开发了一套非常完美的检测人脸全部特征的算法,你完全可以用你的照片当作标定板。</p> +<p>按照我的经验,圆点格的效果应该是好于棋盘格,因为圆点质心的「透视不变性」要比棋盘格的角点稳定的多。下图是同样尺寸、同样比例棋盘格和圆点在最大重投影误差处的误差对比,红色十字是提取的角点/质心,绿色圆圈是针孔相机模型计算出来认为的角点/质心位置。</p> +<p>但是圆点格的检测似乎是Halcon的专利(存疑),因此OpenCV和Matlab标定工具箱用的是棋盘格,要用圆点格得要自己写算法。下文中提到的标定板说的都是棋盘格。</p> +<p>标定板的第二大作用是把标定板中的角点变换到相机坐标系下的坐标 $(X, Y, Z)$。对于标定的初学者来说,很容易忽略的一点是标定板是具有标定板坐标系的。换句话说,标定板中的每个角点,在标定板坐标系下的位置是确定并且是已知的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png" + width="447" + height="404" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="110" + data-flex-basis="265px" + +></p> +<p>而标定板坐标系变换到相机坐标系的变换矩阵,我们称它的元素为外参数。</p> +<h3 id="如何使用标定板"> + <a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%a0%87%e5%ae%9a%e6%9d%bf">#</a> + 如何使用标定板 +</h3><p>如果用OpenCV或Matlab标定工具箱进行标定,需要给出棋盘格的物理尺寸,这其实就是在建立标定板坐标系,从测量的角度讲,标定板的精度是相机标定精度的基准,是误差传递链上的第一个环节。所以为了使针孔相机模型更逼近真实相机,对标定板的质量有以下要求(按重要性顺序):</p> +<ul> +<li>标定板的平面度高,棋盘格是直角。</li> +<li>标定板每个格子尺寸的高一致性。</li> +<li>真实尺寸与标称尺寸的差异小。</li> +</ul> + + + + Test + https://3000ye.com/p/test/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/test/ + <img src="https://3000ye.com/p/test/nord.jpg" alt="Featured image of post Test" /><h1 id="test-page"> + <a href="#test-page">#</a> + Test page +</h1><h2 id="picture"> + <a href="#picture">#</a> + picture +</h2><p><img src="https://3000ye.com/p/test/nord.jpg" + width="5472" + height="2976" + srcset="https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg 480w, https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="nord" + + + class="gallery-image" + data-flex-grow="183" + data-flex-basis="441px" + +></p> +<h2 id="math"> + <a href="#math">#</a> + math +</h2><p>$$ +\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } +$$</p> +<h2 id="code"> + <a href="#code">#</a> + code +</h2><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;bits/stdc++.h&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello World!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="test-latex"> + <a href="#test-latex">#</a> + Test $\LaTeX$ +</h2><h2 id="frac23"> + <a href="#frac23">#</a> + $\frac{2}{3}$ +</h2> + + + + diff --git a/post/page/1/index.html b/post/page/1/index.html new file mode 100644 index 0000000..4a14c91 --- /dev/null +++ b/post/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/post/ + + + + + + diff --git a/post/page/2/index.html b/post/page/2/index.html new file mode 100644 index 0000000..d3cee61 --- /dev/null +++ b/post/page/2/index.html @@ -0,0 +1,679 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

22 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/post/page/3/index.html b/post/page/3/index.html new file mode 100644 index 0000000..1417f8f --- /dev/null +++ b/post/page/3/index.html @@ -0,0 +1,679 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

22 pages

+

Posts

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/post/page/4/index.html b/post/page/4/index.html new file mode 100644 index 0000000..880f932 --- /dev/null +++ b/post/page/4/index.html @@ -0,0 +1,562 @@ + + + + +Posts + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

22 pages

+

Posts

+ +
+
+
+ +
+ + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/scss/style.min.c9c9e57b8cdc7dc9923ac984facd618ebfef66edb2d45045d1a17dad3cea7a7b.css b/scss/style.min.c9c9e57b8cdc7dc9923ac984facd618ebfef66edb2d45045d1a17dad3cea7a7b.css new file mode 100644 index 0000000..db22187 --- /dev/null +++ b/scss/style.min.c9c9e57b8cdc7dc9923ac984facd618ebfef66edb2d45045d1a17dad3cea7a7b.css @@ -0,0 +1,10 @@ +/*!* Hugo Theme Stack +* +* @author: Jimmy Cai +* @website: https://jimmycai.com +* @link: https://github.com/CaiJimmy/hugo-theme-stack*/:root{--main-top-padding:35px;--body-background:#f5f5fa;--accent-color:#34495e;--accent-color-darker:#2c3e50;--accent-color-text:#fff;--body-text-color:#707070;--tag-border-radius:4px;--section-separation:40px;--scrollbar-thumb:hsl(0, 0%, 85%);--scrollbar-track:var(--body-background)}@media(min-width:1280px){:root{--main-top-padding:50px}}:root[data-scheme=dark]{--body-background:#303030;--accent-color:#ecf0f1;--accent-color-darker:#bdc3c7;--accent-color-text:#000;--body-text-color:rgba(255, 255, 255, 0.7);--scrollbar-thumb:hsl(0, 0%, 40%);--scrollbar-track:var(--body-background)}:root{--sys-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", "Droid Sans", "Helvetica Neue";--zh-font-family:"PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei";--base-font-family:"Lato", var(--sys-font-family), var(--zh-font-family), sans-serif;--code-font-family:Menlo, Monaco, Consolas, "Courier New", var(--zh-font-family), monospace}:root{--card-background:#fff;--card-background-selected:#eaeaea;--card-text-color-main:#000;--card-text-color-secondary:#747474;--card-text-color-tertiary:#767676;--card-separator-color:rgba(218, 218, 218, 0.5);--card-border-radius:10px;--card-padding:20px;--small-card-padding:25px 20px}@media(min-width:768px){:root{--card-padding:25px}}@media(min-width:1280px){:root{--card-padding:30px}}@media(min-width:768px){:root{--small-card-padding:25px}}:root[data-scheme=dark]{--card-background:#424242;--card-background-selected:rgba(255, 255, 255, 0.16);--card-text-color-main:rgba(255, 255, 255, 0.9);--card-text-color-secondary:rgba(255, 255, 255, 0.7);--card-text-color-tertiary:rgba(255, 255, 255, 0.5);--card-separator-color:rgba(255, 255, 255, 0.12)}:root{--article-font-family:var(--base-font-family);--article-font-size:1.6rem;--article-line-height:1.85}@media(min-width:768px){:root{--article-font-size:1.7rem}}:root{--blockquote-border-size:4px;--blockquote-background-color:rgb(248 248 248);--heading-border-size:4px;--link-background-color:189, 195, 199;--link-background-opacity:0.5;--link-background-opacity-hover:0.7;--pre-background-color:#272822;--pre-text-color:#f8f8f2;--code-background-color:rgba(0, 0, 0, 0.12);--code-text-color:#808080;--table-border-color:#dadada;--tr-even-background-color:#efefee;--kbd-border-color:#dadada}:root[data-scheme=dark]{--code-background-color:#272822;--code-text-color:rgba(255, 255, 255, 0.9);--table-border-color:#717171;--tr-even-background-color:#545454;--blockquote-background-color:rgb(75 75 75)}:root{--shadow-l1:0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l2:0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l3:0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);--shadow-l4:0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 0px 1px rgba(0, 0, 0, 0.04)}[data-scheme=light]{--pre-text-color:#272822;--pre-background-color:#fafafa}[data-scheme=light] .chroma{color:#272822;background-color:#fafafa}[data-scheme=light] .chroma .err{color:#960050}[data-scheme=light] .chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}[data-scheme=light] .chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:100%;display:block}[data-scheme=light] .chroma .lntable>tbody{display:block;width:100%}[data-scheme=light] .chroma .lntable>tbody>tr{display:flex;width:100%}[data-scheme=light] .chroma .lntable>tbody>tr>td:last-child{overflow-x:auto}[data-scheme=light] .chroma .hl{display:block;width:100%;background-color:#ffc}[data-scheme=light] .chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f;display:block}[data-scheme=light] .chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}[data-scheme=light] .chroma .k{color:#00a8c8}[data-scheme=light] .chroma .kc{color:#00a8c8}[data-scheme=light] .chroma .kd{color:#00a8c8}[data-scheme=light] .chroma .kn{color:#f92672}[data-scheme=light] .chroma .kp{color:#00a8c8}[data-scheme=light] .chroma .kr{color:#00a8c8}[data-scheme=light] .chroma .kt{color:#00a8c8}[data-scheme=light] .chroma .n{color:#111}[data-scheme=light] .chroma .na{color:#75af00}[data-scheme=light] .chroma .nb{color:#111}[data-scheme=light] .chroma .bp{color:#111}[data-scheme=light] .chroma .nc{color:#75af00}[data-scheme=light] .chroma .no{color:#00a8c8}[data-scheme=light] .chroma .nd{color:#75af00}[data-scheme=light] .chroma .ni{color:#111}[data-scheme=light] .chroma .ne{color:#75af00}[data-scheme=light] .chroma .nf{color:#75af00}[data-scheme=light] .chroma .fm{color:#111}[data-scheme=light] .chroma .nl{color:#111}[data-scheme=light] .chroma .nn{color:#111}[data-scheme=light] .chroma .nx{color:#75af00}[data-scheme=light] .chroma .py{color:#111}[data-scheme=light] .chroma .nt{color:#f92672}[data-scheme=light] .chroma .nv{color:#111}[data-scheme=light] .chroma .vc{color:#111}[data-scheme=light] .chroma .vg{color:#111}[data-scheme=light] .chroma .vi{color:#111}[data-scheme=light] .chroma .vm{color:#111}[data-scheme=light] .chroma .l{color:#ae81ff}[data-scheme=light] .chroma .ld{color:#d88200}[data-scheme=light] .chroma .s{color:#d88200}[data-scheme=light] .chroma .sa{color:#d88200}[data-scheme=light] .chroma .sb{color:#d88200}[data-scheme=light] .chroma .sc{color:#d88200}[data-scheme=light] .chroma .dl{color:#d88200}[data-scheme=light] .chroma .sd{color:#d88200}[data-scheme=light] .chroma .s2{color:#d88200}[data-scheme=light] .chroma .se{color:#ae81ff}[data-scheme=light] .chroma .sh{color:#d88200}[data-scheme=light] .chroma .si{color:#d88200}[data-scheme=light] .chroma .sx{color:#d88200}[data-scheme=light] .chroma .sr{color:#d88200}[data-scheme=light] .chroma .s1{color:#d88200}[data-scheme=light] .chroma .ss{color:#d88200}[data-scheme=light] .chroma .m{color:#ae81ff}[data-scheme=light] .chroma .mb{color:#ae81ff}[data-scheme=light] .chroma .mf{color:#ae81ff}[data-scheme=light] .chroma .mh{color:#ae81ff}[data-scheme=light] .chroma .mi{color:#ae81ff}[data-scheme=light] .chroma .il{color:#ae81ff}[data-scheme=light] .chroma .mo{color:#ae81ff}[data-scheme=light] .chroma .o{color:#f92672}[data-scheme=light] .chroma .ow{color:#f92672}[data-scheme=light] .chroma .p{color:#111}[data-scheme=light] .chroma .c{color:#75715e}[data-scheme=light] .chroma .ch{color:#75715e}[data-scheme=light] .chroma .cm{color:#75715e}[data-scheme=light] .chroma .c1{color:#75715e}[data-scheme=light] .chroma .cs{color:#75715e}[data-scheme=light] .chroma .cp{color:#75715e}[data-scheme=light] .chroma .cpf{color:#75715e}[data-scheme=light] .chroma .gd{color:#f92672}[data-scheme=light] .chroma .ge{font-style:italic}[data-scheme=light] .chroma .gi{color:#75af00}[data-scheme=light] .chroma .gs{font-weight:700}[data-scheme=light] .chroma .gu{color:#75715e}[data-scheme=dark]{--pre-text-color:#f8f8f2;--pre-background-color:#272822}[data-scheme=dark] .chroma{color:#f8f8f2;background-color:#272822}[data-scheme=dark] .chroma .err{color:#bb0064}[data-scheme=dark] .chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}[data-scheme=dark] .chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:100%;display:block}[data-scheme=dark] .chroma .lntable>tbody{display:block;width:100%}[data-scheme=dark] .chroma .lntable>tbody>tr{display:flex;width:100%}[data-scheme=dark] .chroma .lntable>tbody>tr>td:last-child{overflow-x:auto}[data-scheme=dark] .chroma .hl{display:block;width:100%;background-color:#ffc}[data-scheme=dark] .chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f;display:block}[data-scheme=dark] .chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}[data-scheme=dark] .chroma .k{color:#66d9ef}[data-scheme=dark] .chroma .kc{color:#66d9ef}[data-scheme=dark] .chroma .kd{color:#66d9ef}[data-scheme=dark] .chroma .kn{color:#f92672}[data-scheme=dark] .chroma .kp{color:#66d9ef}[data-scheme=dark] .chroma .kr{color:#66d9ef}[data-scheme=dark] .chroma .kt{color:#66d9ef}[data-scheme=dark] .chroma .n{color:#f8f8f2}[data-scheme=dark] .chroma .na{color:#a6e22e}[data-scheme=dark] .chroma .nb{color:#f8f8f2}[data-scheme=dark] .chroma .bp{color:#f8f8f2}[data-scheme=dark] .chroma .nc{color:#a6e22e}[data-scheme=dark] .chroma .no{color:#66d9ef}[data-scheme=dark] .chroma .nd{color:#a6e22e}[data-scheme=dark] .chroma .ni{color:#f8f8f2}[data-scheme=dark] .chroma .ne{color:#a6e22e}[data-scheme=dark] .chroma .nf{color:#a6e22e}[data-scheme=dark] .chroma .fm{color:#f8f8f2}[data-scheme=dark] .chroma .nl{color:#f8f8f2}[data-scheme=dark] .chroma .nn{color:#f8f8f2}[data-scheme=dark] .chroma .nx{color:#a6e22e}[data-scheme=dark] .chroma .py{color:#f8f8f2}[data-scheme=dark] .chroma .nt{color:#f92672}[data-scheme=dark] .chroma .nv{color:#f8f8f2}[data-scheme=dark] .chroma .vc{color:#f8f8f2}[data-scheme=dark] .chroma .vg{color:#f8f8f2}[data-scheme=dark] .chroma .vi{color:#f8f8f2}[data-scheme=dark] .chroma .vm{color:#f8f8f2}[data-scheme=dark] .chroma .l{color:#ae81ff}[data-scheme=dark] .chroma .ld{color:#e6db74}[data-scheme=dark] .chroma .s{color:#e6db74}[data-scheme=dark] .chroma .sa{color:#e6db74}[data-scheme=dark] .chroma .sb{color:#e6db74}[data-scheme=dark] .chroma .sc{color:#e6db74}[data-scheme=dark] .chroma .dl{color:#e6db74}[data-scheme=dark] .chroma .sd{color:#e6db74}[data-scheme=dark] .chroma .s2{color:#e6db74}[data-scheme=dark] .chroma .se{color:#ae81ff}[data-scheme=dark] .chroma .sh{color:#e6db74}[data-scheme=dark] .chroma .si{color:#e6db74}[data-scheme=dark] .chroma .sx{color:#e6db74}[data-scheme=dark] .chroma .sr{color:#e6db74}[data-scheme=dark] .chroma .s1{color:#e6db74}[data-scheme=dark] .chroma .ss{color:#e6db74}[data-scheme=dark] .chroma .m{color:#ae81ff}[data-scheme=dark] .chroma .mb{color:#ae81ff}[data-scheme=dark] .chroma .mf{color:#ae81ff}[data-scheme=dark] .chroma .mh{color:#ae81ff}[data-scheme=dark] .chroma .mi{color:#ae81ff}[data-scheme=dark] .chroma .il{color:#ae81ff}[data-scheme=dark] .chroma .mo{color:#ae81ff}[data-scheme=dark] .chroma .o{color:#f92672}[data-scheme=dark] .chroma .ow{color:#f92672}[data-scheme=dark] .chroma .p{color:#f8f8f2}[data-scheme=dark] .chroma .c{color:#75715e}[data-scheme=dark] .chroma .ch{color:#75715e}[data-scheme=dark] .chroma .cm{color:#75715e}[data-scheme=dark] .chroma .c1{color:#75715e}[data-scheme=dark] .chroma .cs{color:#75715e}[data-scheme=dark] .chroma .cp{color:#75715e}[data-scheme=dark] .chroma .cpf{color:#75715e}[data-scheme=dark] .chroma .gd{color:#f92672}[data-scheme=dark] .chroma .ge{font-style:italic}[data-scheme=dark] .chroma .gi{color:#a6e22e}[data-scheme=dark] .chroma .gs{font-weight:700}[data-scheme=dark] .chroma .gu{color:#75715e}:root{--menu-icon-separation:40px;--container-padding:15px;--widget-separation:var(--section-separation)}.container{margin-left:auto;margin-right:auto}.container .left-sidebar{order:-3;max-width:var(--left-sidebar-max-width)}.container .right-sidebar{order:-1;max-width:var(--right-sidebar-max-width)}@media(min-width:1024px){.container .right-sidebar{display:flex}}@media(min-width:768px){.container.extended{max-width:1024px;--left-sidebar-max-width:25%;--right-sidebar-max-width:30%}}@media(min-width:1024px){.container.extended{max-width:1280px;--left-sidebar-max-width:20%;--right-sidebar-max-width:30%}}@media(min-width:1280px){.container.extended{max-width:1536px;--left-sidebar-max-width:15%;--right-sidebar-max-width:25%}}@media(min-width:768px){.container.compact{--left-sidebar-max-width:25%;max-width:768px}}@media(min-width:1024px){.container.compact{max-width:1024px;--left-sidebar-max-width:20%}}@media(min-width:1280px){.container.compact{max-width:1280px}}.flex{display:flex;flex-direction:row}.flex.column{flex-direction:column}.flex.on-phone--column{flex-direction:column}@media(min-width:768px){.flex.on-phone--column{flex-direction:unset}}.flex .full-width{width:100%}main.main{order:-2;min-width:0;max-width:100%;flex-grow:1;display:flex;flex-direction:column;gap:var(--section-separation)}@media(min-width:768px){main.main{padding-top:var(--main-top-padding)}}.main-container{min-height:100vh;align-items:flex-start;padding:0 15px;gap:var(--section-separation);padding-top:var(--main-top-padding)}@media(min-width:768px){.main-container{padding:0 20px}}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}/*!* Hamburgers +* @description Tasty CSS-animated hamburgers +* @author Jonathan Suh @jonsuh +* @site https://jonsuh.com/hamburgers +* @link https://github.com/jonsuh/hamburgers*/.hamburger{padding-top:10px;display:inline-block;cursor:pointer;transition-property:opacity,filter;transition-duration:.15s;transition-timing-function:linear;font:inherit;color:inherit;text-transform:none;background-color:transparent;border:0;margin:0;overflow:visible}.hamburger:hover{opacity:.7}.hamburger.is-active:hover{opacity:.7}.hamburger.is-active .hamburger-inner,.hamburger.is-active .hamburger-inner::before,.hamburger.is-active .hamburger-inner::after{background-color:#000}.hamburger-box{width:30px;height:24px;display:inline-block;position:relative}.hamburger-inner{display:block;top:50%;margin-top:-2px}.hamburger-inner,.hamburger-inner::before,.hamburger-inner::after{width:30px;height:2px;background-color:var(--card-text-color-main);border-radius:4px;position:absolute;transition-property:transform;transition-duration:.15s;transition-timing-function:ease}.hamburger-inner::before,.hamburger-inner::after{content:"";display:block}.hamburger-inner::before{top:-10px}.hamburger-inner::after{bottom:-10px}.hamburger--spin .hamburger-inner{transition-duration:.22s;transition-timing-function:cubic-bezier(.55,.055,.675,.19)}.hamburger--spin .hamburger-inner::before{transition:top .1s .25s ease-in,opacity .1s ease-in}.hamburger--spin .hamburger-inner::after{transition:bottom .1s .25s ease-in,transform .22s cubic-bezier(.55,.055,.675,.19)}.hamburger--spin.is-active .hamburger-inner{transform:rotate(225deg);transition-delay:.12s;transition-timing-function:cubic-bezier(.215,.61,.355,1)}.hamburger--spin.is-active .hamburger-inner::before{top:0;opacity:0;transition:top .1s ease-out,opacity .1s .12s ease-out}.hamburger--spin.is-active .hamburger-inner::after{bottom:0;transform:rotate(-90deg);transition:bottom .1s ease-out,transform .22s .12s cubic-bezier(.215,.61,.355,1)}#toggle-menu{background:0 0;border:none;position:absolute;right:0;top:0;z-index:2;cursor:pointer;outline:none}[dir=rtl] #toggle-menu{left:0;right:auto}@media(min-width:768px){#toggle-menu{display:none}}#toggle-menu.is-active .hamburger-inner,#toggle-menu.is-active .hamburger-inner::before,#toggle-menu.is-active .hamburger-inner::after{background-color:var(--accent-color)}#main-menu{list-style:none;overflow-y:auto;flex-grow:1;font-size:1.4rem;background-color:var(--card-background);box-shadow:var(--shadow-l1);display:none;margin:0 calc(var(--container-padding) * -1);padding:30px}@media(min-width:1280px){#main-menu{padding:15px 0}}#main-menu,#main-menu .menu-bottom-section ol{flex-direction:column;gap:30px}@media(min-width:1280px){#main-menu,#main-menu .menu-bottom-section ol{gap:25px}}#main-menu.show{display:flex}@media(min-width:768px){#main-menu{align-items:flex-end;display:flex;background-color:transparent;padding:0;box-shadow:none;margin:0}}#main-menu li{position:relative;vertical-align:middle;padding:0}@media(min-width:768px){#main-menu li{width:100%}}#main-menu li svg{stroke:currentColor;stroke-width:1.33;width:20px;height:20px}#main-menu li a{height:100%;display:inline-flex;align-items:center;color:var(--body-text-color);gap:var(--menu-icon-separation)}#main-menu li span{flex:1}#main-menu li.current a{color:var(--accent-color);font-weight:700}#main-menu li.menu-bottom-section{margin-top:auto}#main-menu li.menu-bottom-section ol{display:flex;padding-left:0}.menu-social{list-style:none;padding:0;margin:0;display:flex;flex-direction:row;gap:10px}.menu-social svg{width:24px;height:24px;stroke:var(--body-text-color);stroke-width:1.33}.article-list{display:flex;flex-direction:column;gap:var(--section-separation)}.article-list article{display:flex;flex-direction:column;background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);overflow:hidden;transition:box-shadow .3s ease}.article-list article:hover{box-shadow:var(--shadow-l2)}.article-list article .article-image img{width:100%;height:150px;object-fit:cover}@media(min-width:768px){.article-list article .article-image img{height:200px}}@media(min-width:1280px){.article-list article .article-image img{height:250px}}.article-list article:nth-child(5n+1) .article-category a{background:#8ea885;color:#fff}.article-list article:nth-child(5n+2) .article-category a{background:#df7988;color:#fff}.article-list article:nth-child(5n+3) .article-category a{background:#0177b8;color:#fff}.article-list article:nth-child(5n+4) .article-category a{background:#ffb900;color:#fff}.article-list article:nth-child(5n+5) .article-category a{background:#6b69d6;color:#fff}.article-details{display:flex;flex-direction:column;justify-content:center;padding:var(--card-padding);gap:15px}.article-title{font-family:var(--article-font-family);font-weight:600;margin:0;color:var(--card-text-color-main);font-size:2.2rem}@media(min-width:1280px){.article-title{font-size:2.4rem}}.article-title a{color:var(--card-text-color-main)}.article-title a:hover{color:var(--card-text-color-main)}.article-subtitle{font-weight:400;color:var(--card-text-color-secondary);line-height:1.5;margin:0;font-size:1.75rem}@media(min-width:1280px){.article-subtitle{font-size:2rem}}.article-title-wrapper{display:flex;flex-direction:column;gap:8px}.article-time,.article-translations{display:flex;color:var(--card-text-color-tertiary);gap:15px}.article-time svg,.article-translations svg{vertical-align:middle;width:20px;height:20px;stroke-width:1.33;flex-shrink:0}.article-time time,.article-time a,.article-translations time,.article-translations a{font-size:1.4rem;color:var(--card-text-color-tertiary)}.article-time>div,.article-translations>div{display:inline-flex;align-items:center;gap:15px}.article-time{flex-wrap:wrap}.article-translations>div{flex-wrap:wrap}.article-category,.article-tags{display:flex;gap:10px}.article-category a,.article-tags a{color:var(--accent-color-text);background-color:var(--accent-color);padding:8px 16px;border-radius:var(--tag-border-radius);display:inline-block;font-size:1.4rem;transition:background-color .5s ease}.article-category a:hover,.article-tags a:hover{color:var(--accent-color-text);background-color:var(--accent-color-darker)}.article-list--compact{border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);background-color:var(--card-background);--image-size:50px}@media(min-width:768px){.article-list--compact{--image-size:60px}}.article-list--compact article>a{display:flex;align-items:center;padding:var(--small-card-padding);gap:15px}.article-list--compact article:not(:last-of-type){border-bottom:1.5px solid var(--card-separator-color)}.article-list--compact article .article-details{flex-grow:1;padding:0;min-height:var(--image-size);gap:10px}.article-list--compact article .article-title{margin:0;font-size:1.6rem}@media(min-width:768px){.article-list--compact article .article-title{font-size:1.8rem}}.article-list--compact article .article-image img{width:var(--image-size);height:var(--image-size);object-fit:cover}.article-list--compact article .article-time{font-size:1.4rem}.article-list--compact article .article-preview{font-size:1.4rem;color:var(--card-text-color-tertiary);margin-top:10px;line-height:1.5}.article-list--tile article{border-radius:var(--card-border-radius);overflow:hidden;position:relative;height:350px;width:250px;box-shadow:var(--shadow-l1);transition:box-shadow .3s ease;background-color:var(--card-background)}.article-list--tile article:hover{box-shadow:var(--shadow-l2)}.article-list--tile article.has-image .article-details{background-color:rgba(0,0,0,.25)}.article-list--tile article.has-image .article-title{color:#fff}.article-list--tile article .article-image{position:absolute;top:0;left:0;width:100%;height:100%}.article-list--tile article .article-image img{width:100%;height:100%;object-fit:cover}.article-list--tile article .article-details{border-radius:var(--card-border-radius);position:relative;height:100%;width:100%;display:flex;flex-direction:column;justify-content:flex-end;z-index:2;padding:15px}@media(min-width:640px){.article-list--tile article .article-details{padding:20px}}.article-list--tile article .article-title{font-size:2rem;font-weight:500;color:var(--card-text-color-main)}@media(min-width:640px){.article-list--tile article .article-title{font-size:2.2rem}}.widget{display:flex;flex-direction:column}.widget .widget-icon svg{width:32px;height:32px;stroke-width:1.6;color:var(--body-text-color)}.tagCloud .tagCloud-tags{display:flex;flex-wrap:wrap;gap:10px}.tagCloud .tagCloud-tags a{background:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--tag-border-radius);padding:8px 20px;color:var(--card-text-color-main);font-size:1.4rem;transition:box-shadow .3s ease}.tagCloud .tagCloud-tags a:hover{box-shadow:var(--shadow-l2)}.widget.archives .widget-archive--list{border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);background-color:var(--card-background)}.widget.archives .archives-year:not(:last-of-type){border-bottom:1.5px solid var(--card-separator-color)}.widget.archives .archives-year a{font-size:1.4rem;padding:18px 25px;display:flex}.widget.archives .archives-year a span.year{flex:1;color:var(--card-text-color-main);font-weight:700}.widget.archives .archives-year a span.count{color:var(--card-text-color-tertiary)}footer.site-footer{padding:20px 0 var(--section-separation);font-size:1.4rem;line-height:1.75}footer.site-footer:before{content:"";display:block;height:3px;width:50px;background:var(--body-text-color);margin-bottom:20px}footer.site-footer .copyright{color:var(--accent-color);font-weight:700;margin-bottom:5px}footer.site-footer .powerby{color:var(--body-text-color);font-weight:400;font-size:1.2rem}footer.site-footer .powerby a{color:var(--body-text-color)}.pagination{display:flex;background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);overflow:hidden;flex-wrap:wrap}.pagination .page-link{padding:16px 32px;display:inline-flex;color:var(--card-text-color-secondary)}.pagination .page-link.current{font-weight:700;background-color:var(--card-background-selected);color:var(--card-text-color-main)}@media(min-width:768px){.sidebar.sticky{position:sticky}}.left-sidebar{display:flex;flex-direction:column;flex-shrink:0;align-self:stretch;gap:var(--sidebar-element-separation);max-width:none;width:100%;position:relative;--sidebar-avatar-size:100px;--sidebar-element-separation:20px;--emoji-size:40px;--emoji-font-size:20px}@media(min-width:768px){.left-sidebar{width:auto;padding-top:var(--main-top-padding);padding-bottom:var(--main-top-padding);max-height:100vh}}@media(min-width:1536px){.left-sidebar{--sidebar-avatar-size:120px;--sidebar-element-separation:25px;--emoji-size:40px}}.left-sidebar.sticky{top:0}.left-sidebar.compact{--sidebar-avatar-size:80px;--emoji-size:30px;--emoji-font-size:15px}@media(min-width:1024px){.left-sidebar.compact header{flex-direction:row}}.left-sidebar.compact header .site-meta{gap:5px}.left-sidebar.compact header .site-name{font-size:1.4rem}@media(min-width:1536px){.left-sidebar.compact header .site-name{font-size:1.75rem}}.left-sidebar.compact header .site-description{font-size:1.4rem}.right-sidebar{width:100%;display:none;flex-direction:column;gap:var(--widget-separation)}.right-sidebar.sticky{top:0}@media(min-width:1024px){.right-sidebar{padding-top:var(--main-top-padding);padding-bottom:var(--main-top-padding)}}.sidebar header{z-index:1;transition:box-shadow .5s ease;display:flex;flex-direction:column;gap:var(--sidebar-element-separation)}@media(min-width:768px){.sidebar header{padding:0}}.sidebar header .site-avatar{position:relative;margin:0;width:var(--sidebar-avatar-size);height:var(--sidebar-avatar-size);flex-shrink:0}.sidebar header .site-avatar .site-logo{width:100%;height:100%;border-radius:100%;box-shadow:var(--shadow-l1)}.sidebar header .site-avatar .emoji{position:absolute;width:var(--emoji-size);height:var(--emoji-size);line-height:var(--emoji-size);border-radius:100%;bottom:0;right:0;text-align:center;font-size:var(--emoji-font-size);background-color:var(--card-background);box-shadow:var(--shadow-l2)}.sidebar header .site-meta{display:flex;flex-direction:column;gap:10px;justify-content:center}.sidebar header .site-name{color:var(--accent-color);margin:0;font-size:1.6rem}@media(min-width:1536px){.sidebar header .site-name{font-size:1.8rem}}.sidebar header .site-description{color:var(--body-text-color);font-weight:400;margin:0;font-size:1.4rem}@media(min-width:1536px){.sidebar header .site-description{font-size:1.6rem}}[data-scheme=dark] #dark-mode-toggle{color:var(--accent-color);font-weight:700}[data-scheme=dark] #dark-mode-toggle .icon-tabler-toggle-left{display:none}[data-scheme=dark] #dark-mode-toggle .icon-tabler-toggle-right{display:unset}#dark-mode-toggle{margin-top:auto;color:var(--body-text-color);display:flex;align-items:center;cursor:pointer;gap:var(--menu-icon-separation)}#dark-mode-toggle .icon-tabler-toggle-right{display:none}#i18n-switch{color:var(--body-text-color);display:inline-flex;align-content:center;gap:var(--menu-icon-separation)}#i18n-switch select{border:0;background-color:transparent;color:var(--body-text-color)}#i18n-switch select option{color:var(--card-text-color-main);background-color:var(--card-background)}html{font-size:62.5%;overflow-y:scroll}*{box-sizing:border-box}body{background:var(--body-background);margin:0;font-family:var(--base-font-family);font-size:1.6rem;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{scrollbar-width:auto;scrollbar-color:var(--scrollbar-thumb)transparent}::-webkit-scrollbar{height:auto}::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb)}::-webkit-scrollbar-track{background-color:transparent}.article-page.hide-sidebar-sm .left-sidebar{display:none}@media(min-width:768px){.article-page.hide-sidebar-sm .left-sidebar{display:inherit}}.article-page .main-article{background:var(--card-background);border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);overflow:hidden}.article-page .main-article .article-header .article-image img{height:auto;width:100%;max-height:50vh;object-fit:cover}.article-page .main-article .article-header .article-details{padding:var(--card-padding);padding-bottom:0}.article-page .main-article .article-content{margin:var(--card-padding)0;color:var(--card-text-color-main)}.article-page .main-article .article-content .footnotes{font-family:var(--base-font-family)}.article-page .main-article .article-content img{max-width:100%;height:auto}.article-page .main-article .article-footer{margin:var(--card-padding);margin-top:0}.article-page .main-article .article-footer section:not(:first-child){margin-top:var(--card-padding)}.article-page .main-article .article-footer section{color:var(--card-text-color-tertiary);text-transform:uppercase;display:flex;align-items:center;font-size:1.4rem;gap:15px}.article-page .main-article .article-footer section svg{width:20px;height:20px;stroke-width:1.33}.article-page .main-article .article-footer .article-tags{flex-wrap:wrap;text-transform:unset}.article-page .main-article .article-footer .article-copyright a,.article-page .main-article .article-footer .article-lastmod a{color:var(--body-text-color)}.article-page .main-article .article-footer .article-copyright a.link,.article-page .main-article .article-footer .article-lastmod a.link{box-shadow:unset}.widget--toc{background-color:var(--card-background);border-radius:var(--card-border-radius);box-shadow:var(--shadow-l1);display:flex;flex-direction:column;color:var(--card-text-color-main);overflow:hidden}.widget--toc ::-webkit-scrollbar-thumb{background-color:var(--card-separator-color)}.widget--toc #TableOfContents{overflow-x:auto;max-height:75vh}.widget--toc #TableOfContents ol,.widget--toc #TableOfContents ul{margin:0;padding:0}.widget--toc #TableOfContents ol{list-style-type:none;counter-reset:item}.widget--toc #TableOfContents ol li a:first-of-type::before{counter-increment:item;content:counters(item,".")". ";font-weight:700;margin-right:5px}.widget--toc #TableOfContents>ul{padding:0 1em}.widget--toc #TableOfContents li{margin:15px 0 15px 20px;padding:5px}.widget--toc #TableOfContents li>ol,.widget--toc #TableOfContents li>ul{margin-top:10px;padding-left:10px;margin-bottom:-5px}.widget--toc #TableOfContents li>ol>li:last-child,.widget--toc #TableOfContents li>ul>li:last-child{margin-bottom:0}.widget--toc #TableOfContents li.active-class>a{border-left:var(--heading-border-size)solid var(--accent-color);font-weight:700}.widget--toc #TableOfContents ul li.active-class>a{display:block}.widget--toc #TableOfContents>ul>li.active-class>a{margin-left:calc(-25px - 1em);padding-left:calc(25px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li.active-class>a{margin-left:calc(-9px - 1em);padding-left:calc(9px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li.active-class>a{margin-left:calc(-60px - 1em);padding-left:calc(60px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li.active-class>a{margin-left:calc(-44px - 1em);padding-left:calc(44px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-95px - 1em);padding-left:calc(95px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-79px - 1em);padding-left:calc(79px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-130px - 1em);padding-left:calc(130px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-114px - 1em);padding-left:calc(114px + 1em - var(--heading-border-size));display:block}.widget--toc #TableOfContents>ul>li>ul>li>ul>li>ul>li>ul>li.active-class>a{margin-left:calc(-165px - 1em);padding-left:calc(165px + 1em - var(--heading-border-size))}.widget--toc #TableOfContents>ol>li>ol>li>ol>li>ol>li>ol>li.active-class>a{margin-left:calc(-149px - 1em);padding-left:calc(149px + 1em - var(--heading-border-size));display:block}.related-content{overflow-x:auto;padding-bottom:15px}.related-content>.flex{float:left}.related-content article{margin-right:15px;flex-shrink:0;overflow:hidden;width:250px;height:150px}.related-content article .article-title{font-size:1.8rem;margin:0}.related-content article.has-image .article-details{padding:20px;background:linear-gradient(0deg,rgba(0,0,0,.25) 0%,rgba(0,0,0,.75) 100%)}.article-content{font-family:var(--article-font-family);font-size:var(--article-font-size);padding:0 var(--card-padding);line-height:var(--article-line-height)}.article-content>p{margin:1.5em 0}.article-content h1,.article-content h2,.article-content h3,.article-content h4,.article-content h5,.article-content h6{margin-inline-start:calc((var(--card-padding)) * -1);padding-inline-start:calc(var(--card-padding) - var(--heading-border-size));border-inline-start:var(--heading-border-size)solid var(--accent-color)}.article-content figure{text-align:center}.article-content figure figcaption{font-size:1.4rem;color:var(--card-text-color-secondary)}.article-content blockquote{position:relative;margin:1.5em 0;border-inline-start:var(--blockquote-border-size)solid var(--card-separator-color);padding:15px calc(var(--card-padding) - var(--blockquote-border-size));background-color:var(--blockquote-background-color)}.article-content blockquote .cite{display:block;text-align:right;font-size:.75em}.article-content blockquote .cite a{text-decoration:underline}.article-content hr{width:100px;margin:40px auto;background:var(--card-text-color-tertiary);height:2px;border:0;opacity:.55}.article-content code{color:var(--code-text-color);background-color:var(--code-background-color);padding:2px 4px;border-radius:var(--tag-border-radius);font-family:var(--code-font-family)}.article-content a,.article-content code{word-break:break-word}.article-content .gallery{position:relative;display:flex;flex-direction:row;justify-content:center;margin:1.5em 0;gap:10px}.article-content .gallery figure{margin:0}.article-content pre{overflow-x:auto;display:block;background-color:var(--pre-background-color);color:var(--pre-text-color);font-family:var(--code-font-family);line-height:1.428571429;word-break:break-all;padding:var(--card-padding)}[dir=rtl] .article-content pre{direction:ltr}.article-content pre code{color:unset;border:none;background:0 0;padding:0}.article-content .highlight{background-color:var(--pre-background-color);padding:var(--card-padding);position:relative}.article-content .highlight:hover .copyCodeButton{opacity:1}[dir=rtl] .article-content .highlight{direction:ltr}.article-content .highlight pre{margin:initial;padding:0;margin:0;width:auto}.article-content .copyCodeButton{position:absolute;top:calc(var(--card-padding));right:calc(var(--card-padding));background:var(--card-background);border:none;box-shadow:var(--shadow-l2);border-radius:var(--tag-border-radius);padding:8px 16px;color:var(--card-text-color-main);cursor:pointer;font-size:14px;opacity:0;transition:opacity .3s ease}.article-content .table-wrapper{padding:0 var(--card-padding);overflow-x:auto;display:block}.article-content table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:1.5em;font-size:.96em}.article-content th,.article-content td{text-align:left;padding:4px 8px 4px 10px;border:1px solid var(--table-border-color)}.article-content td{vertical-align:top}.article-content tr:nth-child(even){background-color:var(--tr-even-background-color)}.article-content .twitter-tweet{color:var(--card-text-color-main)}.article-content .video-wrapper{position:relative;width:100%;height:0;padding-bottom:56.25%;overflow:hidden}.article-content .video-wrapper>iframe,.article-content .video-wrapper>video{position:absolute;width:100%;height:100%;left:0;top:0;border:0}.article-content .gitlab-embed-snippets{margin:0!important}.article-content .gitlab-embed-snippets .file-holder.snippet-file-content{margin-block-end:0!important;margin-block-start:0!important;margin-left:calc((var(--card-padding)) * -1)!important;margin-right:calc((var(--card-padding)) * -1)!important;padding:0 var(--card-padding)!important}.article-content blockquote,.article-content figure,.article-content .highlight,.article-content pre,.article-content .gallery,.article-content .video-wrapper,.article-content .table-wrapper,.article-content .s_video_simple{margin-left:calc((var(--card-padding)) * -1);margin-right:calc((var(--card-padding)) * -1);width:calc(100% + var(--card-padding) * 2)}.article-content .katex-display>.katex{overflow-x:auto;overflow-y:hidden}.article-content kbd{border:1px solid var(--kbd-border-color);font-weight:700;font-size:.9em;line-height:1;padding:2px 4px;border-radius:4px;display:inline-block}.section-card{border-radius:var(--card-border-radius);background-color:var(--card-background);padding:var(--small-card-padding);box-shadow:var(--shadow-l1);display:flex;align-items:center;gap:20px;--separation:15px}.section-card .section-term{font-size:2.2rem;margin:0;color:var(--card-text-color-main)}.section-card .section-description{font-weight:400;color:var(--card-text-color-secondary);font-size:1.6rem;margin:0}.section-card .section-details{flex-grow:1;display:flex;flex-direction:column;gap:8px}.section-card .section-image img{width:60px;height:60px}.section-card .section-count{color:var(--card-text-color-tertiary);font-size:1.4rem;margin:0;font-weight:700;text-transform:uppercase}.subsection-list{overflow-x:auto}.subsection-list .article-list--tile{display:flex;padding-bottom:15px}.subsection-list .article-list--tile article{width:250px;height:150px;margin-right:20px;flex-shrink:0}.subsection-list .article-list--tile article .article-title{margin:0;font-size:1.8rem}.subsection-list .article-list--tile article .article-details{padding:20px}.not-found-card{background-color:var(--card-background);box-shadow:var(--shadow-l1);border-radius:var(--card-border-radius);padding:var(--card-padding)}.search-form{position:relative;--button-size:80px}.search-form.widget{--button-size:60px}.search-form.widget label{font-size:1.3rem;top:10px}.search-form.widget input{font-size:1.5rem;padding:30px 20px 15px}.search-form p{position:relative;margin:0}.search-form label{position:absolute;top:15px;inset-inline-start:20px;font-size:1.4rem;color:var(--card-text-color-tertiary)}.search-form input{padding:40px 20px 20px;border-radius:var(--card-border-radius);background-color:var(--card-background);box-shadow:var(--shadow-l1);color:var(--card-text-color-main);width:100%;border:0;-webkit-appearance:none;transition:box-shadow .3s ease;font-size:1.8rem}.search-form input:focus{outline:0;box-shadow:var(--shadow-l2)}.search-form button{position:absolute;inset-inline-end:0;top:0;height:100%;width:var(--button-size);cursor:pointer;background-color:transparent;border:0;padding:0 10px}.search-form button:focus{outline:0}.search-form button:focus svg{stroke-width:2;color:var(--accent-color)}.search-form button svg{color:var(--card-text-color-secondary);stroke-width:1.33;transition:all .3s ease;width:20px;height:20px}a{text-decoration:none;color:var(--accent-color)}a:hover{color:var(--accent-color-darker)}a.link{box-shadow:0 -2px rgba(var(--link-background-color),var(--link-background-opacity))inset;transition:all .3s ease}a.link:hover{box-shadow:0 calc(-1rem * var(--article-line-height))rgba(var(--link-background-color),var(--link-background-opacity-hover))inset}.section-title{text-transform:uppercase;margin-top:0;margin-bottom:10px;display:block;font-size:1.6rem;font-weight:700;color:var(--body-text-color)}.section-title a{color:var(--body-text-color)}:root{--sys-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", "Droid Sans", "Helvetica Neue";--zh-font-family:"PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei";--base-font-family:"Lato", var(--sys-font-family), var(--zh-font-family), sans-serif;--code-font-family:"ComicMono Nerd Font", Menlo, Monaco, Consolas, "Courier New", monospace} \ No newline at end of file diff --git a/search/index.html b/search/index.html new file mode 100644 index 0000000..4c90a4b --- /dev/null +++ b/search/index.html @@ -0,0 +1,356 @@ + + + + +Search + + + + + + + + + + + + + + + +
+ +
+
+

+ + +

+ + +
+ +
+

+
+
+ + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/search/index.json b/search/index.json new file mode 100644 index 0000000..88861dc --- /dev/null +++ b/search/index.json @@ -0,0 +1 @@ +[{"content":" # 函数的应用 # 函数模板 ","date":"2024-03-21T23:12:32+08:00","image":"https://3000ye.com/p/clear-c-ch09-function-application/assets/clearcpp_hub0854a127a6da5b9055254a34cc31419_44720_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/clear-c-ch09-function-application/","title":"Clear C++ Ch09: Function application"},{"content":" # 线程管理 在程序开发中,很多时候都是程序都是串行处理,这没有什么问题。然而,在某些重复工作较多,且性能要求较高的场景,串行处理所需时间往往过于漫长。\n因此,合理地使用线程管理有助于我们程序的更好运行。但是请注意,不是一味地使用多线程或线程池就一定是好的,适合运行场景的处理方式才是最好的。\n# 单线程 在本文中,我们考虑这样一个场景:有一个非常耗时的计算函数,其计算一次需要 time 秒。\n1 2 3 4 5 6 void waitTime(int time) { std::chrono::seconds duration(time); std::this_thread::sleep_for(duration); std::cout \u0026lt;\u0026lt; \u0026#34;wait for \u0026#34; \u0026lt;\u0026lt; time \u0026lt;\u0026lt; \u0026#34; seconds\u0026#34; \u0026lt;\u0026lt; std::endl; } 按照常规的做法,我们串行地对批量任务进行处理:\n1 2 3 4 5 6 7 8 9 int main() { std::vector\u0026lt;int\u0026gt; todoList(10, 3); for (auto time : todoList) { waitTime(time); } return 0; } 可以预见,这种处理方法会非常耗时。\n# 多线程 为了加速程序运行和处理的速度,我们可以使用多线程来并行处理。多线程的思想是:先将要进行的任务放入队列中,然后让这些任务同时运行,最终实现加速程序运行的效果。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void mulTreads(std::vector\u0026lt;int\u0026gt; todoList, const int MaxThreads) { std::vector\u0026lt;std::unique_ptr\u0026lt;std::thread\u0026gt;\u0026gt; fetchingThreads; for (int i = 0, l = todoList.size(); i \u0026lt; l; i ++) { fetchingThreads.emplace_back(std::make_unique\u0026lt;std::thread\u0026gt;(waitTime, todoList[i])); if (fetchingThreads.size() \u0026gt;= MaxThreads) { fetchingThreads.front()-\u0026gt;join(); fetchingThreads.erase(fetchingThreads.begin()); } } for (auto \u0026amp;threadPtr : fetchingThreads) { threadPtr-\u0026gt;join(); } } int main() { std::vector\u0026lt;int\u0026gt; todoList(10, 3); mulTreads(todoList, 7); return 0; } # 线程池 多线程虽好,但是频繁地创建和删除线程,同样会造成时间和空间的浪费。因此,线程池出现了,在每次任务完成之后,保留现有线程并继续处理下一个任务。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class ThreadPool { public: ThreadPool(size_t numThreads) : stop(false) { for (size_t i = 0; i \u0026lt; numThreads; ++i) { workers.emplace_back([this] { while (true) { std::function\u0026lt;void()\u0026gt; task; { std::unique_lock\u0026lt;std::mutex\u0026gt; lock(queueMutex); condition.wait(lock, [this] { return stop || !tasks.empty(); }); if (stop \u0026amp;\u0026amp; tasks.empty()) return; task = std::move(tasks.front()); tasks.pop(); } task(); } }); } } // 添加任务到线程池 template\u0026lt;class F\u0026gt; void enqueue(F\u0026amp;\u0026amp; task) { { std::unique_lock\u0026lt;std::mutex\u0026gt; lock(queueMutex); tasks.emplace(std::forward\u0026lt;F\u0026gt;(task)); } condition.notify_one(); } ~ThreadPool() { { std::unique_lock\u0026lt;std::mutex\u0026gt; lock(queueMutex); stop = true; } condition.notify_all(); for (std::thread \u0026amp;worker : workers) worker.join(); } private: std::vector\u0026lt;std::thread\u0026gt; workers; std::queue\u0026lt;std::function\u0026lt;void()\u0026gt;\u0026gt; tasks; std::mutex queueMutex; std::condition_variable condition; bool stop; }; int main() { const int numThreads = 7; ThreadPool pool(numThreads); // 创建包含7个线程的线程池 std::vector\u0026lt;int\u0026gt; todoList(10, 3); // 向线程池中添加任务 for (const int\u0026amp; time : todoList) { pool.enqueue([time] { waitTime(time); }); } return 0; } ","date":"2024-03-18T21:06:19+08:00","permalink":"https://3000ye.com/p/threads/","title":"Threads"},{"content":" # 数据结构:链表 # 线性表 线性表是具有相同数据类型的 $n(n \\ge 0)$ 个数据元素的有限序列,其中 $n$ 为表长,当 $n = 0$ 时线性表是一个空表。若用 $L$ 命名线性表,则其一般表示为:\n$$ L = (a_1, a_2, \\cdots, a_i, a_{i + 1}, \\cdots, a_n) $$\n几个概念:\n$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的位序,位序从 $1$ 开始,数组下标从 $0$ 开始。 $a_1$ 是表头元素,$a_n$ 是表尾元素。 除第一个元素外,每个元素有且仅有一个直接前驱;除最后一个元素外,每个元素有且仅有一个直接后继。\n线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。\n# 链表 链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。\n这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。\n链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。\n# 单向链表 单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。\n","date":"2024-02-21T19:14:32+08:00","image":"https://3000ye.com/p/linked-list/assets/dataStructures_hu4bc1837d9ef6ac9fd46b5492d23a7b01_166447_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/linked-list/","title":"Linked List"},{"content":" # 东华大学学术 Beamer 模板 本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:\n设计元素全部来源于东华大学 标识系统,浓浓的东华风。 Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,模板连接)。 简洁清晰的底部导航区让 Slides 更适合学术汇报使用。 前后端分离式设计,.tex 文件中只需聚焦内容,.sty 配置文件隔离存放。 # 使用 dhuBeamer 模板 # 认识 Beamer 中的元素 Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:\n# 修改 dhuBeamer 模板 ","date":"2024-02-16T23:33:47+08:00","image":"https://3000ye.com/p/dhubeamer/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/dhubeamer/","title":"dhuBeamer"},{"content":" # CppJson 第二章节:number 值解析 ","date":"2024-01-01T23:18:32+08:00","image":"https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/cppjson-ch02/","title":"CppJson Ch02"},{"content":" # 用桌面变化来总结 2023 # 3月16日,我有 5 块屏幕 这段时间是我同时使用屏幕数量最多的时候,主屏幕写代码,副屏幕看网页;surface 和工控屏做直播推流;iPad 负责预览直播画面:\n主屏幕(中间):27 寸 2k,副屏幕(最左):15.6 寸 2k surface(最右),工控屏(最小):7 寸 1080p iPad(非统一背景) 几乎每天都在宿舍,买了本《明解 C++》一边学语言一边学算法。虽然算法已经忘得差不多了,但坚实的 C++ 基础对我帮助很大,甚至我现在实习的主要内容就是 C++ 开发。\n# 5月10日,我有 2 块屏幕 学完了 C++ 语言基础后,我开始意识到数据结构对编程的重要性。同时也有为考研做准备的打算,我开始每天往返图书馆,刷王道考研的 CS408 课程。\n为了方便往返图书馆并尽可能满足我多屏幕的需求,买了一台可以 180 度开合的笔记本(ThinkBook 14)和一个立式支架,搭配 ThinkPad 的 GaN 100W 充电器和雷电线,只需 2 根线完成所有的连接。这样的好处是可以上面写代码下面看课程,同时使用外接键盘不会很突兀。\n# 6月18日,我有 2 块屏幕 春季学期结束后,我也放弃了考研的打算,开始往机器学习和 Pytorch 的方向学习。翻出以前买的西瓜书和南瓜书,对着 Github 上的开源笔记边看边学。由于宿舍桌子太窄了,放三块屏幕过于拥挤,于是将 15.6 寸的副屏收了起来。\n趁着 618 换了罗技的 master 3s 鼠标,手感确实比雷蛇的 click Pro 好很多。同时每天都坚持录视频,之前买的麦克风也派上了用场。\n# 7月19日,我有 1 块屏幕 随着 Pytorch 学习的深入,笔记本的核显已经无法满足需求。而此时矿潮已经开始逐渐褪去,随着 4090 的发布老显卡的价格开始走低,索性在咸鱼上淘了一张服务器版的 2080Ti:\n300A 核心,三星显存 纯铜涡轮散热,尾部供电 其 11G 的显存足以满足入门需求,同时后期还可以加焊 22G 显存,是一张性能和成长空间都不错的显卡。\n同时为了最大限度使用显卡,直接把机器刷成了 Ubuntu server,将之前的监控屏用来监控显卡状态。\n# 7月20日,我有 3 块屏幕 搞深度学习,真的是屏幕越多越好,有太多的资料和数据集看不过来:\n主显示器用来看资料 副显示器用来看数据集(竖直摆放) 笔记本屏幕用来写代码 # 9月6日,我有 1 块屏幕 秋季学期重回宿舍后,开始迷上了 Minecraft 和泡茶,一杯茶一个种子就是一天。这段时间是最放松的时间,每天都在搭方块。\n# 10月19日,我有 3 块屏幕 开始实习后,第一次用上了 MacOS。但尴尬的是,工作用的编译和测试环境还是 Windows,所以基本上都是在 Mac 上面通过 SSH 远程 Windows 进行开发(VsCode 大法好):\n开发环境:MacBookPro(左边)和主屏幕:28 寸 4k 编译环境:dell Windows 主机(无屏幕) 测试环境:hp | ThinkPad(右边) 同时,我也负责服务器上的容器部署等相关工作,所以我有幸同时使用 3 大主流操作系统。\n# 12月31日,我有 2 块屏幕 适应实习的节奏后,晚上回宿舍的几个小时是每天最舒服的,因为可以不用管公司的事情专注自己喜欢的项目。\n趁着放假买了一块树莓派 4B 在宿舍折腾,初步搭了 Seafile 和 Gitlab。\n","date":"2023-12-31T15:40:26+08:00","image":"https://3000ye.com/p/2023-summary/assets/wakatime_hu842e5fd4bc9aaa065f7b68e4ce13a651_73567_120x120_fill_box_smart1_3.png","permalink":"https://3000ye.com/p/2023-summary/","title":"2023 Summary"},{"content":" # 创作极客喜欢的 PRD 产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。\n产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。\n在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:\n产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。 在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。 开发完成后,项目结果远远不如预期。 因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。\n# 产品简介 # 行业概要 # 发行版本 # 排期表 # 产品设计 # 实体关系图 # 用户角色权限表 function user1 user2 func 01 get get ","date":"2023-12-28T14:18:06+08:00","image":"https://3000ye.com/p/happy-geek-prd/assets/prd_hua20af89a28f447e7715c0bfe98af11ec_79304_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/happy-geek-prd/","title":"Happy Geek PRD"},{"content":" # 为不同的 Git 服务生成不同的 SSH Key 在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:\n我有多个 Git 平台(Github, Gitlab, \u0026hellip;)的账号,每个平台的账号都需要一个 SSH Key。 在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。 不同平台的账号,可能使用相同邮箱进行注册。 这时,如何生成和管理 SSH Key 成为一个问题。\n# 设置局部 Git 信息 在不同文件夹下,都可以设置不同的局部 Git 信息:\n1 2 git config user.name \u0026#34;name\u0026#34; git config user.email \u0026#34;email\u0026#34; # 开启 SSH Agent 开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:\n1 eval \u0026#34;$(ssh-agent -s)\u0026#34; # 生成不同的 SSH Key 指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:\n1 ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_name -C \u0026#34;your_email@example.com\u0026#34; 将生成的私钥添加进代理:\n1 2 3 4 ssh-add ~/.ssh/id_rsa_name # MacOS ssh-add --apple-use-keychain ~/.ssh/id_rsa_name # 将公钥添加到 Git 平台 首先复制 SSH 公钥:\n1 cat ~/.ssh/id_rsa_name.pub 然后到 Git 平台中,添加该公钥。\n","date":"2023-12-26T16:23:26+08:00","image":"https://3000ye.com/p/ssh-key-agent/assets/gitssh_hu407acc778bebbd329487502bfd15069f_78666_120x120_fill_box_smart1_3.png","permalink":"https://3000ye.com/p/ssh-key-agent/","title":"SSH Key Agent"},{"content":" # 飞书机器人监听 Gitlab 项目 在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 push 到 Gitlab。\n使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。\n# 准备工作 实现这个功能,你需要准备:\n电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。 飞书账号:用于创建机器人监听 Gitlab 项目。 服务器(Linux):用于部署任务,并自动 push 到 Gitlab。 # 创建 Gitlab 账号并新建项目 使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/\n注册完成后新建一个新项目,并添加一个 README.md 文件。\n# 配置 SSH 秘钥 使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):\n1 2 3 4 5 6 sudo apt update \u0026amp;\u0026amp; sudo apt upgrade sudo apt install git git config --global user.name \u0026#34;name\u0026#34; git config --global user.email \u0026#34;email\u0026#34; ssh-keygen -C \u0026#34;email\u0026#34; -t rsa cat ~/.ssh/id_rsa.pub 其中 name 为 Gitlab 账号的名称,email 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。\n然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。\n# Clone 项目并配置任务 在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:\n然后在 Terminal 中找到合适的位置,Clone 项目:\n1 git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git 编写你的任务脚本(shell 或 Python 等),然后确保其能正常运行。\n# 新建飞书指令 使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:\n然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:\n点击完成后,启用机器人即可。\n# 创建定时任务 首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。\n本文推荐使用 systemctl 和 systemd 的 timer 来创建定时任务。\n# 创建定时器配置 创建一个 task.timer 文件:\n1 2 3 4 5 6 7 8 9 [Unit] Description=Your Timer Description [Timer] # OnCalendar设置定时规则,这里是每天10点 OnCalendar=*-*-* 10:00:00 [Install] WantedBy=timers.target # 创建定时器服务 创建一个 task.service 文件(默认使用 root 用户,建议指定 User 用户):\n1 2 3 4 5 6 7 8 [Unit] Description=Your Service Description [Service] Type=simple User=user # 建议使用 shell 脚本 ExecStart=/path/to/your/script.sh # 添加定时任务 1 2 3 4 5 6 7 8 9 sudo cp your_timer_name.timer /etc/systemd/system/ sudo cp your_service_name.service /etc/systemd/system/ sudo systemctl daemon-reload # 手动启用任务,测试任务是否正常运行 sudo systemctl start your_timer_name.service # 输出任务运行日志 sudo journalctl -u your_timer_name.service 任务运行成功后,使用以下命令启用定时任务:\n1 2 sudo systemctl enable your_timer_name.timer sudo systemctl list-timers ","date":"2023-12-22T13:47:39+08:00","image":"https://3000ye.com/p/feishu-robot/assets/feishuGitlab_hu24a22c832ea1b93e1019080b416a43ce_58732_120x120_fill_box_smart1_3.png","permalink":"https://3000ye.com/p/feishu-robot/","title":"Feishu Robot"},{"content":" # CppJson 第一章节:自动测试,NULL 和 bool 值解析 # JSON 是什么 JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为ECMA-404。\n虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。\n例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { \u0026#34;title\u0026#34;: \u0026#34;Design Patterns\u0026#34;, \u0026#34;subtitle\u0026#34;: \u0026#34;Elements of Reusable Object-Oriented Software\u0026#34;, \u0026#34;author\u0026#34;: [ \u0026#34;Erich Gamma\u0026#34;, \u0026#34;Richard Helm\u0026#34;, \u0026#34;Ralph Johnson\u0026#34;, \u0026#34;John Vlissides\u0026#34; ], \u0026#34;year\u0026#34;: 2009, \u0026#34;weight\u0026#34;: 1.8, \u0026#34;hardcover\u0026#34;: true, \u0026#34;publisher\u0026#34;: { \u0026#34;Company\u0026#34;: \u0026#34;Pearson Education\u0026#34;, \u0026#34;Country\u0026#34;: \u0026#34;India\u0026#34; }, \u0026#34;website\u0026#34;: null } 网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。\n从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:\nnull: 表示为 null boolean: 表示为 true 或 false number: 一般的浮点数表示方式,在下一单元详细说明 string: 表示为 \u0026ldquo;\u0026hellip;\u0026rdquo; array: 表示为 [ \u0026hellip; ] object: 表示为 { \u0026hellip; } 我们要实现的 JSON 库,主要是完成 3 个需求:\n把 JSON 文本解析为一个树状数据结构(parse)。 提供接口访问该数据结构(access)。 把数据结构转换成 JSON 文本(stringify)。 我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。\n# 搭建编译环境 我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。\n我们的 JSON 库名为 CppJson,代码文件只有 3 个:\ninclude/cppjson.hpp:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。 cppjson.cpp:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。 cppjsonTest.cpp:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。 为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 CMake。\n在 OS X 平台中,在命令行通过命令:\n1 2 3 4 mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Debug .. make 将 Debug 改成 Release 就会生成 Release 配置的 makefile。\n在 Vscode 中,可以通过配置 tasks.json 文件来进行自动 build:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 //.vscode/tasks.json { \u0026#34;version\u0026#34;: \u0026#34;2.0.0\u0026#34;, \u0026#34;tasks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;shell\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;mkdirbuild\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;mkdir\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;${fileDirname}\u0026#34; }, \u0026#34;args\u0026#34;: [\u0026#34;-p\u0026#34;, \u0026#34;build\u0026#34;] }, { \u0026#34;type\u0026#34;: \u0026#34;shell\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;cmake\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;cmake\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;-DCMAKE_BUILD_TYPE=Debug\u0026#34;, //在此处添加其它CMAKE选项 \u0026#34;..\u0026#34; ], \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;${fileDirname}/build\u0026#34; }, }, { \u0026#34;label\u0026#34;: \u0026#34;make\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;make\u0026#34;, \u0026#34;args\u0026#34;: [\u0026#34;-j16\u0026#34;,], //根据机器cpu核心数量自行调整 \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;${fileDirname}/build\u0026#34; }, }, { \u0026#34;label\u0026#34;: \u0026#34;build\u0026#34;, \u0026#34;dependsOrder\u0026#34;: \u0026#34;sequence\u0026#34;, \u0026#34;dependsOn\u0026#34;: [\u0026#34;mkdirbuild\u0026#34;, \u0026#34;cmake\u0026#34;, \u0026#34;make\u0026#34;], }, ], } 然后执行 build 生成的文件:\n1 2 3 $ ./build/cppjson_test_ch01 16/16 (100.00%) passed 若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。\n# 头文件与 API 设计 Cpp 语言有头文件的概念,需要使用 #include去引入头文件中的类型声明和函数声明。但由于头文件也可以 #include 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):\n1 #pragma once 如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):\n1 2 3 4 5 6 7 8 9 enum class cppjsonType { CPPJSON_NULL, CPPJSON_TRUE, CPPJSON_FALSE, CPPJSON_NUMBER, CPPJSON_STRING, CPPJSON_ARRAY, CPPJSON_OBJECT }; 接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 cppjson_value 结构体表示,我们会称它为一个 JSON 值(JSON value)。\n在此单元中,我们只需要实现 null, true 和 false 的解析,因此该结构体只需要存储一个 cppjsonType,之后的单元会逐步加入其他数据。\n1 2 3 typedef struct { cppjsonType type; } cppjson_value; 然后,我们现在只需要两个 API 函数,一个是解析 JSON:\n1 cppjsonParseCode cppjson_parse(cppjson_value* v, const std::string json); 传入的 JSON 文本是一个 string 字符串,由于我们不应该改动这个输入字符串,所以使用 const std::string 类型。\n返回值是以下这些枚举类中的值,无错误会返回 cppjsonParseCode::OK,其他值在下节解释。\n1 2 3 4 5 6 enum class cppjsonParseCode { OK, EXPECT_VALUE, INVALID_VALUE, ROOT_NOT_SINGULAR }; 现时我们只需要一个访问结果的函数,就是获取其类型:\n1 cppjsonType cppjson_get_type(const cppjson_value* v); # JSON 语法子集 下面是此单元的 JSON 语法子集,使用 RFC7159 中的 ABNF 表示:\n1 2 3 4 5 6 JSON-text = ws value ws ws = *(%x20 / %x09 / %x0A / %x0D) value = null / false / true null = \u0026#34;null\u0026#34; false = \u0026#34;false\u0026#34; true = \u0026#34;true\u0026#34; 当中 %xhh 表示以 16 进制表示的字符,/ 是多选一,* 是零或多个,() 用于分组。\n那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。\n第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。\n第三行是说,我们现时的值只可以是 null、false 或 true,它们分别有对应的字面值(literal)。\n我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。\n在这个 JSON 语法子集下,我们定义 3 种错误码:\n若一个 JSON 只含有空白,传回 LEPT_PARSE_EXPECT_VALUE。 若一个值之后,在空白之后还有其他字符,传回 LEPT_PARSE_ROOT_NOT_SINGULAR。 若值不是那三种字面值,传回 LEPT_PARSE_INVALID_VALUE。 # 单元测试 许多同学在做练习题时,都是以 printf/cout 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。\n常用的单元测试框架有 xUnit 系列,如 C++ 的 Google Test、C# 的 NUnit。我们为了简单起见,会编写一个极简单的单元测试方式。\n一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:\n加入一个测试。 运行所有测试,新的测试应该会失败。 编写实现代码。 运行所有测试,若有测试失败回到3。 重构代码。 回到 1。 TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。\n但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。\n回到 CppJson 项目,cppjsonTest.cpp 包含了一个极简的单元测试框架:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include \u0026#34;include/cppjson.hpp\u0026#34; #include \u0026lt;cstdio\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;typeinfo\u0026gt; static int main_ret = 0; static int test_count = 0; static int test_pass = 0; // 测试宏接口 // flag 表示测试点是否通过,如果未通过则打印异常信息 #define EXPECT_BASE(flag, expect, actual) \\ do {\\ test_count ++;\\ if (flag) test_pass ++;\\ else {\\ fprintf(stderr, \u0026#34;%s:%d, expect = %s(%d), actual = %s(%d)\\n\u0026#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast\u0026lt;int\u0026gt;(expect), typeid(actual).name(), static_cast\u0026lt;int\u0026gt;(actual));\\ main_ret = 1;\\ }\\ } while(0) #define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) // static void test_parse_null() { cppjson_value v; v.type = cppjsonType::CPPJSON_FALSE; EXPECT_TYPE(cppjsonParseCode::OK, cppjson_parse(\u0026amp;v, \u0026#34;null\u0026#34;)); EXPECT_TYPE(cppjsonType::CPPJSON_NULL, cppjson_get_type(\u0026amp;v)); } static void test_parse() { test_parse_null(); // ... } int main() { test_parse(); printf(\u0026#34;%d/%d (%3.2f%%) passed\\n\u0026#34;, test_pass, test_count, test_pass * 100.0 / test_count); return main_ret; } 现时只提供了一个 EXPECT_TYPE(expect, actual) 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。\n若按照 TDD 的步骤,我们先写一个测试,如上面的 test_parse_null(),而 cppjson_parse() 只返回 cppjsonParseCode::OK:\n1 2 /CppJson/ch01/cppjsonTest.cpp:30, expect = 16cppjsonParseCode(0), actual = 16cppjsonParseCode(2) 15/16 (93.75%) passed 为通过的测试是因为 cppjson_parse() 没有把 v.type 改成 cppjsonType::CPPJSON_NULL,造成失败。我们再实现 lept_parse() 令到它能通过测试。\n然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。\n# 实现解析器 有了 API 的设计、单元测试,终于要实现解析器了。\n首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 cppjson_context 结构体:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct { std::string json; } cppjson_context; // json 解析函数:ws1 value ws2 cppjsonParseCode cppjson_parse(cppjson_value *v, const std::string json) { cppjson_context s; s.json = json; assert(v != NULL); v-\u0026gt;type = cppjsonType::CPPJSON_NULL; // 分别解析 ws1 value ws2 cppjson_parse_whitespace(\u0026amp;s); auto ret = cppjson_parse_value(\u0026amp;s, v); return ret == cppjsonParseCode::OK ? cppjson_parse_root_not_singular(\u0026amp;s) : ret; } CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:\nn ➔ null t ➔ true f ➔ false \u0026quot; ➔ string 0-9/- ➔ number [ ➔ array { ➔ object 所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 // 删除空白符 static void cppjson_parse_whitespace(cppjson_context* s) { const std::string str = s-\u0026gt;json; int i = 0; while (str[i] == \u0026#39; \u0026#39; or str[i] == \u0026#39;\\t\u0026#39; or str[i] == \u0026#39;\\n\u0026#39; or str[i] == \u0026#39;\\r\u0026#39;) { i ++; } s-\u0026gt;json = str.substr(i, str.size()); } // 解析 ws2 之后是否还有非空值 static cppjsonParseCode cppjson_parse_root_not_singular(cppjson_context* s) { // 删除空白符 cppjson_parse_whitespace(s); // 判断是否还有非空值 if (s-\u0026gt;json[0] != \u0026#39;\\0\u0026#39;) return cppjsonParseCode::ROOT_NOT_SINGULAR; else return cppjsonParseCode::OK; } // 检测 null 值 static cppjsonParseCode cppjson_parse_null(cppjson_context* s, cppjson_value* v) { const std::string str = s-\u0026gt;json; auto head = str.substr(0, 4); if (head != \u0026#34;null\u0026#34;) return cppjsonParseCode::INVALID_VALUE; else { s-\u0026gt;json = str.substr(4, str.size()); v-\u0026gt;type = cppjsonType::CPPJSON_NULL; return cppjsonParseCode::OK; } } // 解析 value static cppjsonParseCode cppjson_parse_value(cppjson_context* s, cppjson_value* v) { const std::string str = s-\u0026gt;json; switch (str[0]) { case \u0026#39;n\u0026#39;: return cppjson_parse_null(s, v); case \u0026#39;t\u0026#39;: return cppjson_parse_true(s, v); case \u0026#39;f\u0026#39;: return cppjson_parse_false(s, v); case \u0026#39;\\0\u0026#39;: return cppjsonParseCode::EXPECT_VALUE; default: return cppjsonParseCode::INVALID_VALUE; } } ","date":"2023-12-16T23:33:44+08:00","image":"https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial_hu2efd45d35d025ca12881d6951e1b4111_45457_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/cppjson-ch01/","title":"CppJson Ch01"},{"content":" # 顺序容器 一个容器就是一些特定类型对象的集合,顺序容器(sequence container) 为程序员提供了控制元素存储和访问顺序的能力。\n# 顺序容器概述 下面列出了标准库中的顺序容器,不同容器有不同的性能折中:\n想容器添加或从容器删除元素的代价。 非顺序访问容器中元素的代价。 容器类型 介绍 vector 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 deque 双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。 list 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。 forward_list 单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。 array 固定大小数组。支持快速随机访问。不能添加或者删除元素。 string 与vector相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。 除了固定大小的 array 外,其他容器都提供高效、灵活的内存管理。 通常使用 vector 是最好的选择,除非你有很好的理由选择其他容器。 如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 list 或 forward_list。 如果程序要求随记访问元素,应使用 vector 或 deque。 如果程序要求在容器的中间插入或删除元素,应使用 list 或 forward_list。 如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 deque。 # 容器库概念 容器类型操作上形成了一种层次:\n某些操作是所有容器类型都提供的。 另一些操作仅针对顺序容器、关联容器或无序容器。 # 类型 操作 解释 iterator 此容器类型的迭代器类型 const_iterator 可以读取元素但不能修改元素的迭代器类型 size_type 无符号整数类型,足够保存此种容器类型最大可能的大小 difference_type 带符号整数类型,足够保存两个迭代器之间的距离 value_type 元素类型 reference 元素的左值类型;和value_type \u0026amp;含义相同 const_reference 元素的const左值类型,即const value_type \u0026amp; # 构造函数 操作 解释 C c; 默认构造函数,构造空容器 C c1(c2);或C c1 = c2; 构造c2的拷贝c1 C c(b, e) 构造c,将迭代器b和e指定范围内的所有元素拷贝到c C c(a, b, c...) 列表初始化c C c(n) 只支持顺序容器,且不包括array,包含n个元素,这些元素进行了值初始化 C c(n, t) 包含n个初始值为t的元素 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。 array具有固定大小。 和其他容器不同,默认构造的array是非空的。 直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。 使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。 # 赋值和swap 操作 解释 c1 = c2; 将c1中的元素替换成c2中的元素 c1 = {a, b, c...} 将c1中的元素替换成列表中的元素(不适用于array) c1.swap(c2) 交换c1和c2的元素 swap(c1, c2) 等价于c1.swap(c2) c.assign(b, e) 将c中的元素替换成迭代器b和e表示范围中的元素,b和e不能指向c中的元素 c.assign(il) 将c中的元素替换成初始化列表il中的元素 c.assign(n, r) 将c中的元素替换为n个值是t的元素 使用非成员版本的swap是一个好习惯。 assign操作不适用于关联容器和array # 大小 操作 解释 c.size() c中元素的数目(不支持forward_list) c.max_size() c中可保存的最大元素数目 c.empty() 若c中存储了元素,返回false,否则返回true # 添加元素 操作 解释 c.push_back(t) 在c尾部创建一个值为t的元素,返回void c.emplace_back(args) 同上 c.push_front(t) 在c头部创建一个值为t的元素,返回void c.emplace_front(args) 同上 c.insert(p, t) 在迭代器p指向的元素之前创建一个值是t的元素,返回指向新元素的迭代器 c.emplace(p, args) 同上 c.insert(p, n, t) 在迭代器p指向的元素之前插入n个值为t的元素,返回指向第一个新元素的迭代器;如果n是0,则返回p c.insert(p, b, e) 将迭代器b和e范围内的元素,插入到p指向的元素之前;如果范围为空,则返回p c.insert(p, il) il是一个花括号包围中的元素值列表,将其插入到p指向的元素之前;如果il是空,则返回p 因为这些操作会改变大小,因此不适用于array。 forward_list有自己专有版本的insert和emplace。 forward_list不支持push_back和emplace_back。 当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。 emplace开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。 传递给emplace的参数必须和元素类型的构造函数相匹配。 # 访问元素 操作 解释 c.back() 返回c中尾元素的引用。若c为空,函数行为未定义 c.front() 返回c中头元素的引用。若c为空,函数行为未定义 c[n] 返回c中下标是n的元素的引用,n时候一个无符号证书。若n\u0026gt;=c.size(),则函数行为未定义 c.at(n) 返回下标为n的元素引用。如果下标越界,则抛出out_of_range异常 访问成员函数返回的是引用。 at和下标操作只适用于string、vector、deque、array。 back不适用于forward_list。 如果希望下标是合法的,可以使用at函数。 # 删除元素 操作 解释 c.pop_back() 删除c中尾元素,若c为空,则函数行为未定义。函数返回void c.pop_front() 删除c中首元素,若c为空,则函数行为未定义。函数返回void c.erase(p) 删除迭代器p指向的元素,返回一个指向被删除元素之后的元素的迭代器,若p本身是尾后迭代器,则函数行为未定义 c.erase(b, e) 删除迭代器b和e范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则返回尾后迭代器 c.clear() 删除c中所有元素,返回void 会改变容器大小,不适用于array。 forward_list有特殊版本的erase forward_list不支持pop_back vector和string不支持pop_front # 特殊的 forwad_list操作 链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。 forward_list定义了before_begin,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。 操作 解释 lst.before_begin() 返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。 lst.cbefore_begin() 同上,但是返回的是常量迭代器。 lst.insert_after(p, t) 在迭代器p之后插入元素。t是一个对象 lst.insert_after(p, n, t) 在迭代器p之后插入元素。t是一个对象,n是数量。若n是0则函数行为未定义 lst.insert_after(p, b, e) 在迭代器p之后插入元素。由迭代器b和e指定范围。 lst.insert_after(p, il) 在迭代器p之后插入元素。由il指定初始化列表。 emplace_after(p, args) 使用args在p之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义。 lst.erase_after(p) 删除p指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义。 lst.erase_after(b, e) 类似上面,删除对象换成从b到e指定的范围。 # 改变容器大小 操作 解释 c.resize(n) 调整c的大小为n个元素,若n\u0026lt;c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化 c.resize(n, t) 调整c的大小为n个元素,任何新添加的元素都初始化为值t # 获取迭代器 操作 解释 c.begin(), c.end() 返回指向c的首元素和尾元素之后位置的迭代器 c.cbegin(), c.cend() 返回const_iterator 以c开头的版本是C++11新标准引入的 当不需要写访问时,应该使用cbegin和cend。 # 反向容器的额外成员 操作 解释 reverse_iterator 按逆序寻址元素的迭代器 const_reverse_iterator 不能修改元素的逆序迭代器 c.rbegin(), c.rend() 返回指向c的尾元素和首元素之前位置的迭代器 c.crbegin(), c.crend() 返回const_reverse_iterator 不支持forward_list ","date":"2023-12-10T15:58:28+08:00","image":"https://3000ye.com/p/c-primer-ch09/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch09/","title":"C++ Primer Ch09"},{"content":" # IO 库 我们的程序已经使用了很多 IO 库设施:\nistream (输入流)类型,提供输入操作。 ostream (输出流)类型,提供输出操作。 cin,一个 istream 对象,从标准输入读取数据。 cout,一个 ostream 对象,想标准输出写入数据。 cerr,一个 ostream 对象,通常用于输出程序错误信息,写入到标准错误。 \u0026gt;\u0026gt; 运算符,用来从一个 istream 对象中读取输入数据。 \u0026lt;\u0026lt; 运算符,用来向一个 ostream 对象中写入输出数据。 getline 函数,从一个给定的 istream 对象中读取一行数据,存入到一个给定的 string 对象中。 # IO 库 IO 类型和对象一般都是操纵 char 数据的,但有些使用需要对文件、string 进行操作,因此分别定义了三个头文件:\niostream 头文件:从标准流中读写数据,istream、ostream 等。 fstream 头文件:从文件中读写数据,ifstream、ofstream 等。 sstream 头文件:从字符串中读写数据,istringstream、ostringstream 等。 ","date":"2023-12-10T15:24:27+08:00","image":"https://3000ye.com/p/c-primer-ch08/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch08/","title":"C++ Primer Ch08"},{"content":" # 类 类的基本思想是 数据抽象(data abstraction) 和 封装(encapsulation)。数据抽象是一种依赖于 接口(interface) 和 实现(implementation) 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。\n封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。\n类要想实现数据抽象和封装,首先需要定义一个 抽象数据类型(abstract data type)。\n# 定义抽象数据类型 # 访问控制与封装 我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 C++ 中使用 访问说明符(access specifiers) 加强类的封装:\n定义在 public 说明符之后的成员,在整个程序内都可被访问。 定义在 private 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。 定义新的 Sales_data 类:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Sales_data { private: std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; double avg_price() const { return units_sold ? revenue / units_sold : 0; } public: Sales_data() = default; Sales_data(const std::string \u0026amp;s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p * n) {} Sales_data(const std::string \u0026amp;s): bookNo(s) {} Sales_data(std::istream\u0026amp;); std::string isbn() const { return bookNo; }; Sales_data \u0026amp;combine(const Sales_data\u0026amp;); }; ","date":"2023-12-10T14:35:42+08:00","image":"https://3000ye.com/p/c-primer-ch07/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch07/","title":"C++ Primer Ch07"},{"content":" # 函数 函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。\n# 函数基础 一个典型的 函数(function) 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 形参(parameter) 组成的列表以及函数体。\n我们通过 调用运算符(call operator) 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 实参(argument) 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。\n编写函数:例如编写一个求 n 的阶乘的函数:\n1 2 3 4 5 6 7 8 int fact(int n) { int res = 1; while (n \u0026gt; 1) { res *= n --; } return res; } 调用函数:要调用 fact 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:\n1 2 3 4 5 6 int main() { int x = fact(5); cout \u0026lt;\u0026lt; \u0026#34;5! = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; endl; return 0; } 函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,主调函数(calling funciton) 的执行暂停,被调函数(called funciton) 开始执行。\n# 局部对象 在 C++ 语言中,名字有作用域,对象有 生命周期(lifetime):\n名字的作用域是程序文本的一部分,名字在其中可见。 对象的生命周期是程序执行过程中该对象存在的一段时间。 在函数体内,形参和内部定义的变量统称为 局部变量(local variable),它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。\n局部静态对象:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 static 类型从而获得这样的对象,局部静态对象(local static object) 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。\n# 参数传递 ","date":"2023-12-08T21:13:28+08:00","image":"https://3000ye.com/p/c-primer-ch06/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch06/","title":"C++ Primer Ch06"},{"content":" # 表达式 表达式由一个或多个 运算对象(operand) 组成对表达式求值将得到一个 结果(result)。\n# 基础 # 基本概念 C++ 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。\n对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。\n在表达式求值过程中,小整数类型(bool、char、short)等通常会被 提升(promoted) 成较大的整数类型(int)。\n当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 重载运算符(overloaded operator)。\nC++ 的表达式要不然是 右值(rvalue),要不然就是 左值(lvalue),这两个名词是从 C 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。\n# 求值顺序 在大多数情况下,表达式求值的顺序是没有明确指定的:\n1 int i = f1() * f2() 我们知道 f1 和 f2 一定会在执行乘法之前被调用,但是无法知道是 f1 先被调用还是 f2 先被调用。对于没有指定调用顺序的程序来说,如果 f1 和 f2 同时修改了同一个对象,将会引发错误并产生未定义的行为。\n# 算术运算符 溢出:当计算的结果超出该类型所能表示的范围时就会产生溢出。\nbool 类型不应该参与计算。\n取余运算:m % n 的结果的符号与 m 相同。\n# 逻辑运算符 短路求值:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。\n# 赋值运算符 ","date":"2023-11-28T08:49:13+08:00","image":"https://3000ye.com/p/c-primer-ch04/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch04/","title":"C++ Primer Ch04"},{"content":" # 字符串、向量和数组 # 命名空间的 using 声明 我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用using进行声明:\n1 2 3 using std::cout; // 单独使用某个函数 using namespace std; // 批量声明 std 中所有函数 头文件中不应该包含 using 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。\n# 标准库类型 string 标准库类型 string 表示可变长的字符序列,使用 string 类型必须首先包含 string 头文件:\n1 2 3 #include \u0026lt;string\u0026gt; using std::string # 定义和初始化 string 对象 初始化 string 对象的方式:\n方式 解释 string s1 默认初始化,s1是个空字符串 string s2(s1) s2是s1的副本 string s2 = s1 等价于s2(s1),s2是s1的副本 string s3(\u0026quot;value\u0026quot;) s3是字面值“value”的副本,除了字面值最后的那个空字符外 string s3 = \u0026quot;value\u0026quot; 等价于s3(\u0026quot;value\u0026quot;),s3是字面值\u0026quot;value\u0026quot;的副本 string s4(n, 'c') 把s4初始化为由连续n个字符c组成的串 拷贝初始化(copy initialization):使用 = 将一个已有的对象拷贝到正在创建的对象。\n直接初始化(direct initialization):通过括号给对象赋值。\n# string 对象的操作 string的操作:\n操作 解释 os \u0026lt;\u0026lt; s 将s写到输出流os当中,返回os is \u0026gt;\u0026gt; s 从is中读取字符串赋给s,字符串以空白分割,返回is getline(is, s) 从is中读取一行赋给s,返回is s.empty() s为空返回true,否则返回false s.size() 返回s中字符的个数 s[n] 返回s中第n个字符的引用,位置n从0计起 s1+s2 返回s1和s2连接后的结果 s1=s2 用s2的副本代替s1中原来的字符 s1==s2 如果s1和s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感 s1!=s2 同上 \u0026lt;, \u0026lt;=, \u0026gt;, \u0026gt;= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较) 读取 string 对象:\n使用 IO 操作符 \u0026gt;\u0026gt; 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。 使用 getline() 函数读取:将一整行读取为 string 对象,包括空白。 s.size() 返回 string::size_type 类型,是 无符号 类型的值,不能和 int 混用。\ns1 + s2 使用时,必须保证至少其中一个为 string 类型。例如:string s = \u0026quot;hello\u0026quot; + \u0026quot;world\u0026quot; 错误,其 + 两边都为字符串字面值。\n字符串字面值 和 string 是不同的类型。\n# 处理 string 对象中的字符 C++ 修改了 c 的标准库 ctype.h 为 cctype,其中定义了一组标准函数:\n函数 解释 isalnum(c) 当c是字母或数字时为真 isalpha(c) 当c是字母时为真 iscntrl(c) 当c是控制字符时为真 isdigit(c) 当c是数字时为真 isgraph(c) 当c不是空格但可以打印时为真 islower(c) 当c是小写字母时为真 isprint(c) 当c是可打印字符时为真 ispunct(c) 当c是标点符号时为真 isspace(c) 当c是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符) isupper(c) 当c是大写字母时为真 isxdigit(c) 当c是十六进制数字时为真 tolower(c) 当c是大写字母,输出对应的小写字母;否则原样输出c toupper(c) 当c是小写字母,输出对应的大写字母;否则原样输出c 遍历字符串:\n1 2 3 for (auto c : str) { ... } str[idx] 中的 idx 为 string::size_type 类型,如果使用 int 会隐式转换为该类型。\n# 标准库类型 vector 标准库类型 vector 表示对象的集合,其中给所有对象的类型都相同。因为 vector 容纳着其他对象,所以称其为 容器(container),使用 vector 必须包含其头文件:\n1 2 3 #include \u0026lt;vector\u0026gt; using std::vector vector 同时也是 类模板(class template),模板本身不是类或函数,但可以使用模板创建类,这个过程称为 实例化(instantiation)。\n当使用模板时,需要指出编译器应把类或函数实例化成何种类型:\n1 2 vector\u0026lt;int\u0026gt; ls; // ls 保存 int 类型的对象 vector\u0026lt;vector\u0026lt;string\u0026gt;\u0026gt; files; // 该向量中的元素是 vector 对象 vector 是模板,vector\u0026lt;int\u0026gt; 是类型。\n# 定义和初始化 vector 对象 初始化vector对象的方法:\n方法 解释 vector\u0026lt;T\u0026gt; v1 v1是一个空vector,它潜在的元素是T类型的,执行默认初始化 vector\u0026lt;T\u0026gt; v2(v1) v2中包含有v1所有元素的副本 vector\u0026lt;T\u0026gt; v2 = v1 等价于v2(v1),v2中包含v1所有元素的副本 vector\u0026lt;T\u0026gt; v3(n, val) v3包含了n个重复的元素,每个元素的值都是val vector\u0026lt;T\u0026gt; v4(n) v4包含了n个重复地执行了值初始化的对象 vector\u0026lt;T\u0026gt; v5{a, b, c...} v5包含了初始值个数的元素,每个元素被赋予相应的初始值 vector\u0026lt;T\u0026gt; v5={a, b, c...} 等价于v5{a, b, c...} # vector 对象的操作: vector支持的操作:\n操作 解释 v.emtpy() 如果v不含有任何元素,返回真;否则返回假 v.size() 返回v中元素的个数 v.push_back(t) 向v的尾端添加一个值为t的元素 v[n] 返回v中第n个位置上元素的引用 v1 = v2 用v2中的元素拷贝替换v1中的元素 v1 = {a,b,c...} 用列表中元素的拷贝替换v1中的元素 v1 == v2 v1和v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同 v1 != v2 同上 \u0026lt;,\u0026lt;=,\u0026gt;, \u0026gt;= 以字典顺序进行比较 # 迭代器介绍 除了下标运算符外,迭代器(iterator) 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。\n# 使用迭代器 拥有迭代器的类型都具有 begin 和 end 成员,其中 begin 成员返回指向第一个元素的迭代器:\n1 2 vector\u0026lt;int\u0026gt; ls{1, 2, 3}; auto b = v.begin(), e = v.end(); // b 和 e 类型相同 end 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 end 指向容器的 尾后(off the end) 元素。这样的迭代器通常没有意义,只是作为标记,被称为 尾后迭代器(off-the-end iterator) 或 尾迭代器(end iterator)。\n若容器为空,则 begin 和 end 都返回尾后迭代器。\n标准容器迭代器的运算符:\n运算符 解释 *iter 返回迭代器iter所指向的元素的引用 iter-\u0026gt;mem 等价于(*iter).mem ++iter 令iter指示容器中的下一个元素 --iter 令iter指示容器中的上一个元素 iter1 == iter2 判断两个迭代器是否相等 泛型编程:尽量使用 != 来对迭代器进行判断\n迭代器也拥有自己的类型:\n1 2 vector\u0026lt;int\u0026gt;::iterator it; // it 是 vector\u0026lt;int\u0026gt; 类型的迭代器,可以读写元素 vector\u0026lt;int\u0026gt;::const_iterator it2; // it2 只能读,不能写 如果容器中的值为常量,则 begin 和 end 返回 const_iterator,否则返回 iterator。\n解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:\n1 2 3 4 5 6 vector\u0026lt;string\u0026gt; ls{\u0026#34;str1\u0026#34;, \u0026#34;str2\u0026#34;}; auto it = ls.begin(); string s = *it; // s 为 \u0026#34;str1\u0026#34; bool flag = (*it).empty(); // 解引用访问 string 成员 bool flag = it-\u0026gt;empty(); // 作用同上 # 迭代器运算 string 和 vector 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 迭代器运算(iterator arithmetic):\n运算符 解释 iter + n 迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。 iter - n 迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。 iter1 += n 迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1 iter1 -= n 迭代器减法的复合赋值语句,将iter2减n的加过赋给iter1 iter1 - iter2 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。 \u0026gt;、\u0026gt;=、\u0026lt;、\u0026lt;= 迭代器的关系运算符,如果某迭代器 当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 difference_type 类型,是带符号整数型。\n# 数组 数组可以看做 vector 的低配版,其 长度固定。\n# 定义和初始化内置数组 数组的声明和定义形如 a[d],其中 a 是数组的名字,d 是数组的维度(大于 0):\n1 2 3 4 5 6 7 8 int cnt = 42; // 非常量 const int cnt2 = 42; // 常量 int arr[10]; // 含有 10 个整数的数组 int *arr2[10]; // 含有 10 个整型指针的数组 int arr3[cnt]; // 报错,cnt 非常量 int arr4[cnt2]; // 含有 42 和整数的数组 int arr5[] = {1, 2, 3}; // 自动计算长度的数组 字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:\n1 2 char arr[] = \u0026#34;hello\u0026#34;; char arr2[5] = \u0026#34;hello\u0026#34;; // 报错,长度不够 不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:\n1 2 3 int a[] = {1, 2, 3}; int a2[] = a; // 报错,不能用数组来初始化数组 a2 = a; // 报错,不能用数组进行赋值 # 访问数组元素 数组的下标为 size_t 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。\n下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。\n# 指针和数组 使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:\n1 2 3 int ls[] = {1, 2, 3} int *p = \u0026amp;ls[0]; // 数组的元素的指针 int *p2 = ls; // 等价于 *p2 = \u0026amp;ls[0] # C 风格字符串 字符串字面值是一种通用结构的实例,这种结构是 C++ 由 C 继承而来的 C 风格字符串(C-style character string) 。 按此习惯书写的字符串存放在字符数组中并以 空字符结束(null terminated)。\n在 C++ 程序中尽量不要使用 C 风格字符串,容易引起安全漏洞且不方便。\nC标准库String函数,定义在\u0026lt;cstring\u0026gt; 中:\n函数 介绍 strlen(p) 返回p的长度,空字符不计算在内 strcmp(p1, p2) 比较p1和p2的相等性。如果p1==p2,返回0;如果p1\u0026gt;p2,返回一个正值;如果p1\u0026lt;p2,返回一个负值。 strcat(p1, p2) 将p2附加到p1之后,返回p1 strcpy(p1, p2) 将p2拷贝给p1,返回p1 # 多维数组 严格来说,C++ 语言中没有多维数组,所谓的多维数组实际是数组的数组。\n1 int arr[10][20] = {0}; // 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 # 使用范围 for 语句处理多维数组 1 2 3 4 5 6 7 size_t cnt = 0; for (auto \u0026amp;row: arr) { for (auto \u0026amp;col: row) { col = cnt; cnt ++; } } ","date":"2023-11-14T22:14:23+08:00","image":"https://3000ye.com/p/c-primer-ch03/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch03/","title":"C++ Primer Ch03"},{"content":" # 变量和基本类型 变量提供一个具名的、可供程序操作的存储空间。C++中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。\n# 变量的声明和定义 为了允许把程序拆分成多个逻辑部分来编写,C++语言支持分离式编译(separate complication) 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。\n为了支持分离式编译,C++语言将声明和定义区分开来。声明(declaration) 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 定义(definition) 负责创建与名字关联的实体。\n变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。\n1 2 3 4 extern int i; // 声明 int i; // 定义 int i = 3; // 定义并赋值 extern int i = 3; // 定义而非声明,extern失效 注意: 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。\n# 标识符 C++的 标识符(identifier) 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。\n# 作用域 作用域(scope) 是程序的一部分,在其中名字有特定的含义,C++语言中大多数作用域都以花括号分隔。\n作用域能彼此包含,被包含(嵌套)的作用域称为 内层作用域(inner scope),包含着别的作用域的作用域称为 外层作用域(outer scope)。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include \u0026#34;iostream\u0026#34; int outer = 42; // 全局作用域 int main() { int inner = 12; // 内层作用域 // 使用全局变量输出 std::cout \u0026lt;\u0026lt; outer \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; inner \u0026lt;\u0026lt; std::endl; int outer = 0; // 局部重新定义,覆盖全局变量 // 使用局部变量输出 std::cout \u0026lt;\u0026lt; outer \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; inner \u0026lt;\u0026lt; std::endl; // 显式访问全局变量 std::cout \u0026lt;\u0026lt; ::outer \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; inner \u0026lt;\u0026lt; std::endl; return 0; } # 复合类型 复合类型(compound type) 是指基于其他类型定义的类型,主要介绍引用和指针。\n# 引用 引用(reference) 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 绑定(bind) 在一起,而不是将初始者拷贝给引用。\n引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。\n1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026#34;iostream\u0026#34; int main() { int x = 3; // 定义 int \u0026amp;y = x; // 引用 int z = x; // 定义 y = 3; // 修改 y 的值,实际是修改 x 的值 std::cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; y \u0026lt;\u0026lt; std::endl; return 0; } # 指针 指针(pointer) 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:\n指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。 指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。 1 2 int *ip1, *ip2; // ip1 和 ip2 都是指向 int 类型对象的指针 double dp, *dp2; // dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 指针存放某个对象的地址,想获取该地址,需要使用 取地址符(\u0026amp;):\n1 2 int val = 42; int *p = \u0026amp;val; // p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。\n指针的值(即地址)应属于下列 4 种状态之一:\n指向一个对象。 指向紧邻对象所占空间的下一个位置。 空指针,意味着指针没有指向任何对象。 无效指针,也就是上述情况之外的其他值。 如果指针指向了一个对象,则允许使用 解引用符(*) 来访问该对象:\n1 2 3 4 int val = 42; int *p = \u0026amp;val; std::cout \u0026lt;\u0026lt; *p \u0026lt;\u0026lt; std::endl; // 由符号 * 得到指针 p 所指的对象,输出 42 解引用操作仅适用于有效指针,无效指针无法解引用。\n空指针(null pointer) 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。\n生成空指针:\n1 2 int *p1 = nullptr; // 等价于 int *p1 = 0 int *p2 = 0; 指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。\n指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。\n1 2 3 4 5 6 7 int i = 42; int *pi = 0; // pi 为空指针 int *pi2 = \u0026amp;i; // pi2 存放 i 的地址 int *pi3; // pi3 的值无法确定 pi3 = pi2; // pi3 和 pi2 指向同一个对象 i pi2 = 0; // pi2 变为空指针 # const 限定符 有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字const对变量的类型加以限定:\n1 const int bufferSize = 512; // 限定缓冲区大小为 512 默认情况下,const对象被设定为仅在单个文件内有效。当多个文件中出现了同名的const变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上extern:\n1 2 3 4 5 // 定义文件 xxx.cpp extern const int bufferSize = 512; // 声明文件 xxx.hpp extern const int bufferSize; # 指针和 const 与引用一样,也可以令指针指向常量与非常量。指向常量的指针(pointer to const) 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:\n1 2 3 4 5 const int val = 42; // val 为常量对像,其值不能改变 int *pi = \u0026amp;val; // 报错,pi 为普通指针,不能存放常量对象地址 const int *pi2 = \u0026amp;val; // 使用指向常量的指针存放常量对象地址 *pi2 = 20; // 报错,不能给指向常量的指针赋值 # 处理类型 随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。\n# 类型别名 类型别名(type alias) 是一个名字,它是某种类型的同义词。\n使用关键字typedef定义类型别名:\n1 typedef long long ll; // ll 是 long long 的同义词 使用 别名声明(alias declaration) 定义类型别名:\n1 using ll = long long; // ll 是 long long 的同义词 # auto 类型说明符 编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此C++11引入了auto类型说明符,它能自动分析表达式结果的类型。\n1 auto item = val1 + val2; // item 初始化为 val1 和 val2 相加的结果 使用auto定义或声明多个变量时,所有变量的类型必须一致:\n1 2 auto x = 3, y = 5; auto x = 3, y = 4.2; // 报错,同一行的变量类型必须一致 # decltype 类型指示符 有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。\n因此C++11引入了decltype类型指示符,它的作用是返回操作数的数据类型:\n1 2 int x = 5, y = 7; decltype(x + y) z = 6; // z 为 int 类型 # 自定义数据结构 内置的数据类型并不能满足所有的需求,因此c++提供了自定义数据类型的方式:\n1 2 3 4 5 6 7 8 9 10 11 struct student { std::string name; std::string sex; int gender; }; // 定义 student jack{\u0026#34;jack\u0026#34;, \u0026#34;m\u0026#34;, 18}; // 先声明后赋值 student castor; castor.name = \u0026#34;castor\u0026#34;, castor.sex = \u0026#34;m\u0026#34;, castor.gender = 18; # 自定义数据结构使用别名 和内置数据类型一样,自定义数据结构也能使用别名:\n1 2 3 4 5 6 7 8 using stu = studeng; // 或直接在定义时使用别名 using stu = struct student { std::string name; std::string sex; int gender; }; # 编写自己的头文件 当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:\n1 2 3 4 5 6 7 8 9 10 11 12 13 // studeng.h #ifndef STUDENT_H #define STUDENT_H #include \u0026#34;string\u0026#34; struct student { std::string name; std::string sex; int gender; }; #endif ","date":"2023-11-06T22:34:55+08:00","image":"https://3000ye.com/p/c-primer-ch02/assets/c++primer_hu7c71e19f9023f7a97b706cc8104fd581_19508_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/c-primer-ch02/","title":"C++ Primer Ch02"},{"content":" # 使用 $\\LaTeX$ 绘制 Slides ","date":"2023-10-20T10:04:55+08:00","image":"https://3000ye.com/p/slides/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/slides/","title":"Slides"},{"content":" # 东华大学学士毕业论文 LaTeX 模板使用手册 # 导入模板 # 使用 Git 克隆仓库 将仓库克隆到你需要的位置:\n1 2 3 git clone git@github.com:3000ye/dhuBachelor.git # 或 git clone https://github.com/3000ye/dhuBachelor.git # 下载 Zip 文件 如果你不会使用Git,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载Zip文件:\n# 微信公众号获取 如果你没有科学代理,无法进入Github,请扫码关注公众号:3000ye Blog然后回复latex模板获取百度网盘分享链接。\n# 开始使用 # 安装字体 在使用模板之前,请先找到并打开fonts文件夹,安装里面的所有字体。\n# 配置 TeX 环境 如果你是 $\\LaTeX$ 小白,请先行阅读:使用 LaTeX 优雅地完成创作,在这个教程里你可以学会如何安装并配置适合你的 $\\TeX$ 环境。\n# 尝试编译 使用你喜欢的编辑器,打开dhuBachelor.tex文件,选择xelatex命令编译文件。如果没有出现报错,同目录下会生成一个dhuBachelor.pdf文件,这就是我们的论文。\n# 各个组件说明 看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。\n本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。\n# 论文题目 根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:\n1 2 \\reTitle{中文题目} \\reTitleEN{英文题目} # 摘要 根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:\n1 2 \\reAbstract % 中文摘要 \\reAbstractEN % 英文摘要 摘要内容直接写在摘要下方,首行缩进两字符。\n# 关键词 中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。\n英文关键词:Times New Roman(标题加黑)。\n1 2 \\reKeyword{关键词1,关键词2,关键词3,关键词4} % 中文关键词 \\reKeywordEN{Keyword1, Keyword2, Keyword3} % 英文关键词 # 目录 目录标题需居中显示,添加代码:\n1 2 3 \\begin{center} \\tableofcontents \\end{center} # 各级标题 规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。\n1 2 3 \\reSection{一级标题} \\subsection{二级标题} \\subsubsection{三级标题} # 有序列表 规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:\n1 2 3 4 5 6 7 \\orderedList{ % 使用 (i) 排序,缩进 2 字符 \\item 有序列表标题 \\par % \\par的作用是将内容换行 这是有序列表的内容 \\item 有序列表标题 \\par 这是有序列表的内容 } # 数学公式 行内公式:$f(x) = x + 1$\n跨行公式:跨行公式请使用equation环境,默认按照章节自动编号。\n1 2 3 4 5 \\begin{equation} x = a_0 + \\cfrac{1}{a_1 + \\cfrac{1}{a_2 + \\cfrac{1}{a_3 + \\cfrac{1}{a_4} } } } \\end{equation} # 插入图片 图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改width大小和图片路径,并根据实际更改图例和索引。\n注意:图片需要保存在assets/目录中才能被正确插入,读者可以自己新建其他目录实现插入。\n1 2 3 4 5 6 7 \\begin{figure}[H] % 图片位于文字下方 \\centering % 居中 % 设置图片占页面宽度的比例(默认0.8) \\includegraphics[width=0.8\\textwidth]{assets/dataStructures.jpg} \\caption{图片标题} % 图例,按章节编号 \\label{fig: 数据结构2} % 图片索引 \\end{figure} 有时可能需要多图并排,模板使用minipage来实现并排展示,使用时可以修改子图所占比例:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \\begin{figure}[H] \\centering \\begin{minipage}[c]{0.40\\textwidth} %minipage使之保持同一行 \\centering \\includegraphics[width=0.8\\textwidth]{assets/dataStructures.jpg}\\\\ \\caption{图片1标题} \\end{minipage} \\hspace{1em} \\begin{minipage}[c]{0.40\\textwidth} %minipage使之保持同一行 \\centering \\includegraphics[width=0.8\\textwidth]{assets/dataStructures.jpg}\\\\ \\caption{图片2标题} \\end{minipage} \\end{figure} # 插入表格 使用 excel2latex 工具生成表格代码后,需要手动添加分割线(\\toprule, \\midrule, \\bottomrule),以达到三线表的格式要求。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \\begin{table}[H] \\centering \\caption{表格标题} \\begin{tabular}{c||l} \\toprule parameter \u0026amp; Description \\\\ \\midrule $I$ \u0026amp; Land area collection \\\\ $J$ \u0026amp; Flower pollination demand set \\\\ $D_j$ \u0026amp; Number of pollinating bees required for flower pollination \\\\ $T_k$ \u0026amp; Honeycomb size grade, $k = 1, 2, \\cdots$ \\\\ $B$ \u0026amp; Maximum number of hive \\\\ $R_{ik}$ \u0026amp; Maximum influence radius of a single honeycomb \\\\ \\bottomrule \\end{tabular}% \\label{tab: 一个表}% \\end{table}% 对于需要多表并排的情况,和图片的方式类似,使用minipage来实现:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 \\begin{minipage}[c]{0.45\\textwidth} \\centering \\begin{table}[H] \\centering \\caption{表格1标题} \\begin{tabular}{c||lc} \\toprule Symbol \u0026amp; Description \u0026amp; Unit \\\\ \\midrule $t$ \u0026amp; $t_{th}$ year \u0026amp; $\\sim$ \\\\ $e_k$ \u0026amp; the error term \u0026amp; $\\sim$ \\\\ $X_{ij}$ \u0026amp; Raw data matrix \u0026amp; $\\sim$ \\\\ $Y_{ij}$ \u0026amp; Positive matrix \u0026amp; $\\sim$ \\\\ \\bottomrule \\end{tabular}% \\label{tab: 表格1标题}% \\end{table}% \\end{minipage} \\begin{minipage}[c]{0.45\\textwidth} \\centering \\begin{table}[H] \\centering \\caption{表格2标题} \\begin{tabular}{c||lc} \\toprule Symbol \u0026amp; Description \u0026amp; Unit \\\\ \\midrule $t$ \u0026amp; $t_{th}$ year \u0026amp; $\\sim$ \\\\ $e_k$ \u0026amp; the error term \u0026amp; $\\sim$ \\\\ $X_{ij}$ \u0026amp; Raw data matrix \u0026amp; $\\sim$ \\\\ $Y_{ij}$ \u0026amp; Positive matrix \u0026amp; $\\sim$ \\\\ \\bottomrule \\end{tabular}% \\label{tab: 表格2标题}% \\end{table}% \\end{minipage} # 插入代码 可以直接在.tex文件中编写代码,并指定语言和标题:\n1 2 3 4 5 6 7 8 9 10 11 \\begin{lstlisting}[language=c++,title={code.cpp}] #include \u0026#34;bits/stdc++.h\u0026#34; using namespace std; int main() { cout \u0026lt;\u0026lt; \u0026#34;3000ye 的 LaTeX 模板!\u0026#34; \u0026lt;\u0026lt; endl; return 0; } \\end{lstlisting} 另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在assets/目录下:\n1 \\lstinputlisting[language=c++, title=code.cpp]{code/code.cpp} # 插入伪代码 使用宏包algorithm, algorithmic来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:\n1 2 3 4 5 6 7 8 9 10 11 \\begin{algorithm} \\caption{Example Pseudocode} \\begin{algorithmic} \\STATE $x\\gets0$ \\IF {$x\\leq 0$} \\STATE $x\\gets x+1$ \\ELSE \\STATE $x\\gets x-1$ \\ENDIF \\end{algorithmic} \\end{algorithm} # 参考文献 参考文献使用\\bibitem来添加,添加时需要手动更改{RNi}索引(i是你文献的序号)。\n1 2 3 4 \\reference{ \\bibitem{RN1} 参考文献1 \\bibitem{RN2} 参考文献2 } # 致谢 1 2 3 \\reThanks{ 致谢,3000ye 的 \\LaTeX 模板! } ","date":"2023-10-12T16:41:55+08:00","image":"https://3000ye.com/p/dhubachelor/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/dhubachelor/","title":"dhuBachelor"},{"content":" # 使用 $\\LaTeX$ 优雅地完成创作 $\\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\\LaTeX$ 使用 $\\TeX$ 作为它的排版引擎,学习 $\\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。\n# 从安装 $\\TeX$ 引擎开始 $\\TeX$ 引擎类似于 gcc/g++ 或 Python,用于编译 $\\LaTeX$ 文档。\n不同平台中 $\\TeX$ 的安装方法不尽相同,本文提供:Windows11、Linux(Ubuntu 22.04)、MacOs(12.7)、Windows11-wsl2(Ubuntu22.04)的安装方法。\n如果你只是想简单体验 $\\LaTeX$,可以使用 overleaf 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。\n# Windows11 进入网站tug.org for windows,点击install-tl-windows.exe下载 $\\TeX$ 安装器,然后运行安装即可。\n不过,这种方法需要一直联网安装,网速不好的环境可以直接下载iso镜像进行本地安装。本文给出清华源镜像地址:mirrors.tuna,下载后缀为.iso的文件(只用下载一个)。\n下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击install-tl-windows.bat文件,使用管理员打开,然后按照指引安装即可。\n最新版本的安装器会自动添加环境变量,安装完成后打开cmd然后输入:\n1 tex --version 若能输出 $\\TeX$ 版本信息则安装成功:\n1 2 3 TeX 3.141592653 (TeX Live 2023/W32TeX) kpathsea version 6.3.5 Copyright 2023 D.E. Knuth. # Linux(Ubuntu22.04) 打开终端,然后执行安装命令:\n1 2 3 sudo apt update sudo apt upgrade sudo apt install texlive-full 等待安装完成即可,安装完成后执行命令:\n1 tex --version 若能输出 $\\TeX$ 版本信息则安装成功:\n1 2 3 TeX 3.141592653 (TeX Live 2023/Debian) kpathsea version 6.3.5 Copyright 2023 D.E. Knuth. # MacOs(12.7) 打开终端,然后执行安装命令(推荐安装无窗体版本):\n1 brew install mactex-no-gui 等待安装完成即可,安装完成后执行命令:\n1 tex --version 若能输出 $\\TeX$ 版本信息则安装成功:\n1 2 3 TeX 3.141592653 (TeX Live 2023) kpathsea version 6.3.5 Copyright 2023 D.E. Knuth. # Windows11-wsl2(Ubuntu22.04) 在wsl2中安装方式与在Linux中一样。\n# 找到属于你的编辑器 市面上有很多 $\\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:\n全平台通用: Vs Code:作为地表最强编辑器,Vs Code拥有非常丰富的 $\\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。 Jetbrains: 与Vs Code相对应的是Jetbrains系列, 其虽然也有 $\\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。 Neovim:如果说Vs Code是编辑器中的王后,那么nvim就是国王。nvim可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。 sublime text:nvim固然强大,但其难以上手的特点使得很多人对其望而却步。sublime打破了这个束缚,其界面优雅程度不亚于nvim,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。 TexStudio:texlive默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。 Windows独占: Winedt 11:如果不考虑跨平台,那么Winedt 11就是Windows上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。 综上所述,笔者最推荐Vs Code,但如果你只有Windows平台的使用需求并不介意一点费用的话,请果断购买Winedt 11。同样的,MacOs也拥有独占编辑器,但笔者没用过,在此不做评价。\n关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。\n# 一本教程入门 $\\LaTeX$ 语法 对于所有初学者来说,Ishort-zh-cn都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。\n如果你已经完成了所有的案例,相信你已经对 $\\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。\n# 打印中文 $\\LaTeX$ 默认只打印英文,如果没有合理的设置,.tex文件中的中文将无法正确打印。\n从下图可以看出,目前支持全平台通用的方案只有XeLaTeX和LuaTeX。因此,主流方案是一般使用xelatex+ctex编译方案,底层调用xeCJK字符集来实现中文打印。\n使用时只需在导言区加入,编译器会使用默认字体进行编译:\n1 2 \\usepackage[UTF8]{ctex} \\usepackage{fontspec} % 设置字体 如果要指定字体,则需分别设置font-family:\n1 2 3 4 5 \\usepackage[UTF8, fontset=none]{ctex} % 清除默认字体 \\usepackage{fontspec} % 设置字体 \\setCJKmainfont{SimSun}[AutoFakeBold=true, BoldFont={SimHei}, ItalicFont={KaiTi}] % 正文字体(宋体,黑体,楷体) \\setCJKsansfont[AutoFakeBold=3]{KaiTi} % 无衬线字体 \\setCJKmonofont[AutoFakeBold=3]{SimHei} % 等宽字体 同样的,英文也可以自定义字体:\n1 \\setmainfont{Times New Roman} % 设置英文字体为新罗马体 详细配置可以参考:LaTex 中文字体配置指南\n# 打印数学公式 你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\\LaTeX$,这也是 $\\LaTeX$ 为什么被奉为珍宝的原因之一。\n但是笔者并不建议读者专门花时间来学习如何编写 $\\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。\n在线数学公式生成平台:latexlive可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。 MathType:如果你是Windows用户,那么强烈建议使用MathType来生成数学公式的 $\\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。 mathpix:这是一款专为 $\\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。 # 绘制表格 你一定使用过Excel来绘制表格,但是在 $\\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。\n但不用担心,本文为你介绍开源项目 excel2latex。这是一款Excel插件,可以将你在Excel中绘制的表格自动转译为 $\\LaTeX$ 代码。\n但是这个插件并不是万能的,比如绘制三线表,即使是在Excel中也较为繁琐。因此,最好的处理方式是使用excel2latex插件生成表格主体,然后再自己添加分隔格式。\n# 插入图片 绘制表格和插入图片并称为 $\\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。\n# 学会使用代码片段 阅读到这里,相信你已经能够使用 $\\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写.sty和.cls文件),但这对初学者来说太难了。\n因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。\n代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。\n# Vs Code配置代码片段 点击左下角的设置按钮,然后点击设置用户代码片段:\n在弹出的窗口中输入latex然后选中即可跳转到latex.json文件,我们可以在这里设置我们的代码片段。\n比如这段设置,保存文件后我们只需要在.tex后缀的文件中输入insertImg然后回车就会自动填充以下代码,并且使用tab来依次输入参数。\n1 2 3 4 5 6 7 8 9 10 11 12 \u0026#34;insertImg\u0026#34;: { \u0026#34;prefix\u0026#34;: \u0026#34;insertImg\u0026#34;, // 代码片段别名 \u0026#34;body\u0026#34;: [ // 代码片段主体 \u0026#34;\\\\begin{figure}[H]\u0026#34;, \u0026#34; \\\\centering\u0026#34;, \u0026#34; \\\\includegraphics[width=0.8\\\\textwidth]{$1}\u0026#34;, // 参数1:图片路径 \u0026#34; \\\\caption{$2}\u0026#34;, // 参数2:图片标题 \u0026#34; \\\\label{$3}\u0026#34;, // 参数3:图片索引 \u0026#34;\\\\end{figure}$0\u0026#34;, ], \u0026#34;description\u0026#34;: \u0026#34;insert one img with 0.8 width\u0026#34; }, # Winedt 设置代码片段 依次点击Option -\u0026gt; Options Interface -\u0026gt; Menus and Toolbar -\u0026gt; Main Menu,修改或添加配置:\n1 2 3 4 5 6 ITEM=\u0026#34;Figure\u0026#34; CAPTION=\u0026#34;\u0026amp;Figure\u0026#34; IMAGE=\u0026#34;Figure\u0026#34; MACRO=\u0026#34;Exe(\u0026#39;%b\\Menus\\Insert\\Image.edt\u0026#39;);\u0026#34; SHORTCUT=\u0026#34;49222::Ctrl+Alt+F\u0026#34; REQ_DOCUMENT=1 然后就可以使用快捷键Ctrl+Alt+F填充插入图片的代码片段。\n# 使用模板来专注内容 使用 $\\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\\LaTeX$ 的初衷。\n因此,常见期刊都会提供对应的 $\\LaTeX$ 风格模板和示例,其中主要文件通常为:\n.sty:$\\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。 .cls:$\\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。 .tex:示例文件,通常会包括论文中会用到的所有样式的示例代码。 阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。\n","date":"2023-10-09T17:18:55+08:00","image":"https://3000ye.com/p/elegant-latex/assets/latex_hu1e4838b57430da79d946f8d6b8ca0bae_30212_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/elegant-latex/","title":"Elegant LaTeX"},{"content":" # 由浅到深理解相机标定 # 何为相机标定 在图像测量过程以及机器视觉应用中,为确定空间无哦表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。\n在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像头标定)。\n相机标定涉及的知识面很广:成像几何、镜头畸变、单应矩阵、非线性优化等。\n相机标定有自标定(找图像中特征点)、标定板标定(特征点易求,稳定性好),一般采用标定板标定。\n相机标定按照相机是否静止,可分为静态相机标定(标定板动,相机静止),动态相机标定(标定板静止,相机运动)。\n# 为什么需要标定 任何理论物理模型都是在特定假设上对真实事物的近似,然而在实际应用中存在误差,普通相机的成像模型也不例外(透视投影)。\n实际中,普通相机成像误差的主要来源有两部分:\n第一是sensor(传感器)制造产生的误差,比如sensor成像单元不是正方形,sensor歪斜。 第二是镜头制造和安装产生的误差,镜头一般存在非线性的径向畸变;镜头与相机sensor安装不平行,还会产生切向畸变。 # 相机标定的目的和意义 我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。\n相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。\n这个逼近的过程就是「相机标定」,我们用简单的数学模型来表达复杂的成像过程,并且求出成像的反过程。标定之后的相机,可以进行三维场景的重建,即深度的感知。\n# 相关术语 焦点:在几何光学中有时也称为像点,是源头的光线经过物镜后汇聚的点。\n焦距:也称为焦长,是光学系统中衡量光的聚集或发散的度量方式,指从透镜中心到光聚集之焦点的距离。亦是照相机中,从镜片光学中心到底片、CCD或CMOS等成像平面的距离。\n正透镜、负透镜、凹面镜和凸面镜的焦点F和焦距f:\n镜头(Lenses):是将拍摄景物在传感器上成像的器件,它通常由几片透镜、光圈叶片、对焦马达等光学元件组成。\n传感器(Sensor):是摄像头组成的核心,其作用是作为相机的感光元件。摄像头传感器主要有两种,一种是CCD传感器,一种是CMOS传感器,两者区别在于:CCD的优势在于成像质量好,但是由于制造工艺复杂,成本居高不下,特别是大型CCD,价格非常高昂。在相同分辨率下,CMOS价格比CCD便宜,但是CMOS器件产生的图像质量相比CCD来说要低一些。\n光心:凸透镜近轴光线中,入射线和与其对应且相平行的出射线构成共轭光线,其入射点跟出射点的连线与主光轴的交点,称为凸透镜的焦点,位于透镜中央的点叫光心。\n从图中可知,O为光心,F为焦点。每个透镜主轴上都有一个特殊点,凡是通过该点的光,其传播方向不变,这个点叫光心。经过光心的光线的传播方向不会发生改变。\n# 相机标定原理模型 # 针孔相机模型 我们通常将相机看成如下所示的透镜模型:\n在实际分析时,通常将其简化为针孔模型(小孔成像):\n一般为了分析简单,将成像平面画在对称位置,这样图像不再颠倒:\n# 四个坐标系 世界坐标系:用户定义的三维世界的坐标系,用于描述目标物体在真实世界里的位置。单位通常为米(m)。该坐标系作用于三维空间。\n相机坐标系:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位通常为米(m)。相机坐标系的原点在光心,其 $X_c、Y_c$ 轴分别与像面的两边平行,其 $Z_c$ 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点(实际情况中可能存在主点偏移),相机坐标系与图像坐标系之间的距离为焦距 $f$。该坐标系作用于三维空间。\n图像坐标系:为了描述成像过程中物体从相机坐标系到图像坐标系的投影投射关系而引入,方便进一步得到像素坐标系下的坐标。其原点是相机光轴与像面的交点(称为主点),即图像的中心点。其 $x, y$ 轴和像素坐标系的 $u, v$ 轴平行,故图像坐标系和像素坐标系实际是平移关系。单位通常为毫米(mm)。该坐标系作用于二维空间。\n像素坐标系:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为像素。像素坐标平面和图像坐标系平面重合,但像素坐标系原点位于图像左上角。该坐标系作用于二维空间。\n# 相机外参 将世界坐标系中的点映射到相机坐标系:相机坐标系是世界坐标系通过刚体变换得到的。\n刚体变换能够保持物体中各点的距离和角度,常见的刚体变换有:平移、旋转和镜像。\n我们先只考虑旋转,假设将坐标系以 $X$ 轴为中心进行旋转,即 $X$ 不变,旋转 $Y - Z$ 平面。\n假设旋转角度为 $\\theta$,即 $\\angle Y\u0026rsquo; O Y = \\angle Z\u0026rsquo; O Z = \\theta$。旋转前的坐标系为 $X - Y - Z$,旋转后的坐标系为 $X\u0026rsquo; - Y\u0026rsquo; - Z\u0026rsquo;$。假设点 $P$ 在 $X - Y - Z$ 中的坐标为($X_w, Y_w, Z_w$),旋转后,其在 $X\u0026rsquo; - Y\u0026rsquo; - Z\u0026rsquo;$ 中的坐标为($X_c, Y_c, Z_c$): $$ X_C = X_w $$\n$$ \\begin{array}{l} Y_c \u0026amp; = OC + CD = OA \\cdot \\sin \\theta + BP \\\\ \u0026amp; = Z_w \\cdot \\sin \\theta + AP \\cdot \\cos \\theta \\\\ \u0026amp; = Z_w \\sin \\theta + Y_w \\cos \\theta \\end{array} $$\n$$ \\begin{array}{l} Z_c \u0026amp; = PD = AC - AB \\\\ \u0026amp; = AO \\cdot \\cos \\theta - AP \\cdot \\cos \\theta \\\\ \u0026amp; = Z_w \\cos \\theta + Y_w \\cos \\theta \\end{array} $$\n写成矩阵形式: $$\\displaystyle \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\end{bmatrix} = \\mathbf{R_{cw}} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\end{bmatrix} or \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\end{bmatrix} = \\mathbf{R_{wc}} \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\end{bmatrix} $$ 推广到每个方向,可得到 $\\mathbf{R_{cw}}, \\mathbf{R_{wc}}$ 为: $$ \\mathbf{R_{cw}} (X_A, \\theta) = \\begin{bmatrix} 1 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; \\cos \\theta \u0026amp; \\sin \\theta \\\\ 0 \u0026amp; - \\sin \\theta \u0026amp; \\cos \\theta \\end{bmatrix} , \\mathbf{R_{wc}} (X_A, \\theta) = \\begin{bmatrix} 1 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; \\cos \\theta \u0026amp; - \\sin \\theta \\\\ 0 \u0026amp; \\sin \\theta \u0026amp; \\cos \\theta \\end{bmatrix} $$ $$ \\mathbf{R_{cw}} (Y_A, \\theta) = \\begin{bmatrix} \\cos \\theta \u0026amp; 0 \u0026amp; \\sin \\theta \\\\ 0 \u0026amp; 1 \u0026amp; 0 \\\\ - \\sin \\theta \u0026amp; 0 \u0026amp; \\cos \\theta \\end{bmatrix} , \\mathbf{R_{wc}} (Y_A, \\theta) = \\begin{bmatrix} \\cos \\theta \u0026amp; 0 \u0026amp; - \\sin \\theta \\\\ 0 \u0026amp; 1 \u0026amp; 0 \\\\ \\sin \\theta \u0026amp; 0 \u0026amp; \\cos \\theta \\end{bmatrix} $$ $$ \\mathbf{R_{cw}} (Z_A, \\theta) = \\begin{bmatrix} \\cos \\theta \u0026amp; \\sin \\theta \u0026amp; 0 \\\\ - \\sin \\theta \u0026amp; \\cos \\theta \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} , \\mathbf{R_{wc}} (Z_A, \\theta) = \\begin{bmatrix} \\cos \\theta \u0026amp; - \\sin \\theta \u0026amp; 0 \\\\ \\sin \\theta \u0026amp; \\cos \\theta \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} $$\n这里我们使用右手笛卡尔三维坐标系:\n旋转可分为主动旋转与被动旋转。主动旋转是指将向量逆时针围绕旋转轴所做出的旋转。被动旋转是对坐标轴本身进行的逆时针旋转,它相当于主动旋转的逆操作。关于右手笛卡尔坐标系的 $X, Y, Z$ 轴的旋转分别叫做roll,pitch和yaw旋转:\n因为逆时针和顺时针旋转会得到不一样的旋转矩阵,所以我们统一如下:\n绕 $X$ 轴的主动旋转定义为($\\theta_x$ 是roll角 ): $$ R(X_A, \\theta_x) = \\begin{bmatrix} 1 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; \\cos \\theta_x \u0026amp; - \\sin \\theta_x \\\\ 0 \u0026amp; \\sin \\theta_x \u0026amp; \\cos \\theta_x \\end{bmatrix} = \\exp \\left ( \\theta_x \\begin{bmatrix} 0 \u0026amp; 0 \u0026amp; 0\\\\ 0 \u0026amp; 0 \u0026amp; -1\\\\ 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\right ) $$ 绕 $Y$ 轴的主动旋转定义为($\\theta_y$ 是pitch角): $$ R(Y_A, \\theta_y) = \\begin{bmatrix} \\cos \\theta_y \u0026amp; 0 \u0026amp; \\sin \\theta_y \\\\ 0 \u0026amp; 1 \u0026amp; 0 \\\\ - \\sin \\theta_y \u0026amp; 0 \u0026amp; \\cos \\theta_y \\end{bmatrix} = \\exp \\left ( \\theta_y \\begin{bmatrix} 0 \u0026amp; 0 \u0026amp; 1\\\\ 0 \u0026amp; 0 \u0026amp; 0\\\\ -1 \u0026amp; 0 \u0026amp; 0 \\end{bmatrix} \\right ) $$ 绕 $Z$ 轴的主动旋转定义为($\\theta_z$ 是yaw角): $$ R(Z_A, \\theta_z) = \\begin{bmatrix} \\cos \\theta_z \u0026amp; - \\sin \\theta_z \u0026amp; 0 \\\\ \\sin \\theta_z \u0026amp; \\cos \\theta_z \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} = \\exp \\left ( \\theta_y \\begin{bmatrix} 0 \u0026amp; -1 \u0026amp; 0\\\\ 1 \u0026amp; 0 \u0026amp; 0\\\\ 0 \u0026amp; 0 \u0026amp; 0 \\end{bmatrix} \\right ) $$ 将上述三个旋转矩阵结合起来,最终的旋转矩阵(设绕 $X, Y, Z$ 轴旋转的角度分别为 $\\alpha, \\beta, \\gamma$): $$ \\begin{array}{ll} M(\\alpha, \\beta, \\gamma) \u0026amp; = R_x(\\alpha) R_y(\\beta) R_z(\\gamma) \\\\ \u0026amp; = \\begin{bmatrix} 1 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; \\cos \\alpha \u0026amp; - \\sin \\alpha \\\\ 0 \u0026amp; \\sin \\alpha \u0026amp; \\cos \\alpha \\end{bmatrix} \\begin{bmatrix} \\cos \\beta \u0026amp; 0 \u0026amp; \\sin \\beta \\\\ 0 \u0026amp; 1 \u0026amp; 0 \\\\ - \\sin \\beta \u0026amp; 0 \u0026amp; \\cos \\beta \\end{bmatrix} \\begin{bmatrix} \\cos \\gamma \u0026amp; -\\sin \\gamma \u0026amp; 0 \\\\ \\sin \\gamma \u0026amp; \\cos \\gamma \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} \\\\ \u0026amp; = \\begin{bmatrix} \\cos \\gamma \\cos \\beta \u0026amp; - \\sin \\gamma \\cos \\alpha + \\cos \\gamma \\sin \\beta \\sin \\alpha \u0026amp; \\sin \\gamma \\sin \\alpha + \\cos \\gamma \\sin \\beta \\cos \\alpha \\\\ \\sin \\gamma \\cos \\beta \u0026amp; \\cos \\gamma \\cos \\alpha + \\sin \\gamma \\sin \\beta \\sin \\alpha \u0026amp; - \\cos \\gamma \\sin \\alpha + \\sin \\gamma \\sin \\beta \\cos \\alpha \\\\ - \\sin \\beta \u0026amp; \\cos \\beta \\sin \\alpha \u0026amp; \\cos \\beta \\cos \\alpha \\end{bmatrix} \\end{array} $$\n此时我们再加上平移向量 $T$ 便可完成从世界坐标系到相机坐标系的这个刚体变换了:\n$$ \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\end{bmatrix} = \\begin{bmatrix} r_{11} \u0026amp; r_{12} \u0026amp; r_{13} \\\\ r_{21} \u0026amp; r_{22} \u0026amp; r_{23} \\\\ r_{31} \u0026amp; r_{32} \u0026amp; r_{33} \\end{bmatrix} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\end{bmatrix} + \\begin{bmatrix} t_x \\\\ t_y \\\\ t_z \\end{bmatrix} = \\mathbf{R} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\end{bmatrix} + T $$\n可进一步写成如下形式:\n$$ \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} \\mathbf{R} \u0026amp; \\mathbf{T} \\\\ 0_3^T \u0026amp; 1 \\end{bmatrix} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\\\ 1 \\end{bmatrix} $$\n其中,$\\mathbf{R}$ 和 $\\mathbf{T}$ 便是相机外参。\n# 相机内参 首先考虑图像坐标系($xy$)和像素坐标系($uv$)之间的转换:\n$$ \\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix}= \\begin{bmatrix} \\displaystyle \\frac{1}{dx} \u0026amp; 0 \u0026amp; u_0 \\\\ 0 \u0026amp; \\displaystyle \\frac{1}{dy} \u0026amp; v_0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix}= \\begin{bmatrix} x \\\\ y \\\\ 1 \\end{bmatrix} $$\n$dx$ 表示一个像素点在 $x$ 方向的长度是多少毫米,$dy$ 表示一个像素点在 $y$ 方向的长度是多少毫米;$(u_0, v_0)$ 为图像的中心点。\n然后考虑相机坐标系和图像坐标系之间的转换:\n$$ \\Delta ABO_c \\sim \\Delta oCO_c, \\Delta PBO_c \\sim \\Delta pCO_c $$ $$ \\displaystyle \\frac{AB}{oC} = \\frac{AO_c}{oO_c} = \\frac{PB}{p C} = \\frac{X_c}{x} = \\frac{Z_c}{f} = \\frac{Y_c}{y} $$ $$ x = f \\displaystyle \\frac{X_c}{Z_c}, y = f \\frac{Y_c}{Z_c} $$\n$$ Z_c \\begin{bmatrix} x \\\\ y \\\\ 1 \\end{bmatrix}= \\lambda \\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix}= \\begin{bmatrix} f \u0026amp; 0 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; f \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\\\ 1 \\end{bmatrix} $$\n其中,$f$ 是焦距,结合外参我们最终可以得到世界坐标系和像素坐标系之间的映射关系:\n$$ \\begin{array}{l} \\lambda \\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix} \u0026amp; = \\begin{bmatrix} \\displaystyle \\frac{1}{dx} \u0026amp; 0 \u0026amp; u_0 \\\\ 0 \u0026amp; \\displaystyle \\frac{1}{dy} \u0026amp; v_0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} \\begin{bmatrix} f \u0026amp; 0 \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; f \u0026amp; 0 \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\begin{bmatrix} \\mathbf{R} \u0026amp; \\mathbf{T} \\\\ 0 \u0026amp; 1 \\end{bmatrix} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\\\ 1 \\end{bmatrix}\\\\ \u0026amp; = \\begin{bmatrix} fx \u0026amp; 0 \u0026amp; u_0 \u0026amp; 0 \\\\ 0 \u0026amp; fy \u0026amp; v_0 \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\begin{bmatrix} \\mathbf{R} \u0026amp; \\mathbf{T} \\\\ 0 \u0026amp; 1 \\end{bmatrix} \\begin{bmatrix} X_w \\\\ Y_w \\\\ Z_w \\\\ 1 \\end{bmatrix} \\end{array} $$ 其中,相机内参为(不考虑图像传感器的特性): $$ \\begin{bmatrix} fx \u0026amp; 0 \u0026amp; u_0 \u0026amp; 0 \\\\ 0 \u0026amp; fy \u0026amp; v_0 \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} $$\n其中,$f_x, f_y$ 即为焦距的物理距离在像素坐标系中的长度,相机内参标定主要是标定相机的焦距、主点、歪斜等内部参数。\n# 可能存在的影响 # 主点偏移 主点是光轴和相机成像平面的交点,在理想情况下,图像坐标系和相机坐标系原点重合,不存在坐标系偏移。但在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动:\n考虑主点偏移后,图像坐标和3D在相机坐标系的关系为:\n$$ \\begin{matrix} u = f \\frac{X}{Z} + O_x \\\\ v = f \\frac{X}{Z} + O_y \\end{matrix} $$ 此时,透视投影模型(像素坐标系和相机坐标系)的关系为: $$ \\lambda \\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} f \u0026amp; 0 \u0026amp; O_x \u0026amp; 0 \\\\ 0 \u0026amp; f \u0026amp; O_x \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\\\ 1 \\end{bmatrix} $$\n仔细观察就会发现,该关系与上面提到的关系是等价的,只不过上面使用坐标 $(u_0, v_0)$ 来代表偏移量 $(O_x, O_y)$。\n# 图像传感器特征 图像传感器像原尺寸在制造过程可能不是正方形,同时可能存在歪斜(skewed),因此需要考虑这些影响因素,传感器歪斜和不是正方形主要对相机 $x$ 和 $y$ 方向的焦距产生影响。\n此时,透视投影模型(像素坐标系和相机坐标系)的关系为: $$ \\lambda \\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} f \u0026amp; s \u0026amp; O_x \u0026amp; 0 \\\\ 0 \u0026amp; \\eta f \u0026amp; O_x \u0026amp; 0 \\\\ 0 \u0026amp; 0 \u0026amp; 1 \u0026amp; 0 \\end{bmatrix} \\begin{bmatrix} X_c \\\\ Y_c \\\\ Z_c \\\\ 1 \\end{bmatrix} = [K, 0_3] P $$ 其中,$K$ 矩阵即为最终的内参矩阵。\n# 镜头畸变 小孔成像模型虽然充分考虑了相机内部参数对成像的影响,但没有考虑成像系统另一个重要的部分,镜头。镜头常用的有普通镜头、广角镜头、鱼眼镜头等,在无人驾驶和视觉slam领域,鱼眼镜头和广角镜头用的很多,主要是视角很大,可以观测到更多的信息。任何镜头都存在不同程度的畸变,不同类型的镜头用到的畸变模型也不相同。\n在几何光学和阴极射线管(CRT)显示中,畸变(distortion)是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括径向畸变(radial distortion)和切向畸变(tangential distortion)。\n径向畸变来自于透镜形状,主要是由于透镜不同部位放大倍率不同造成的。切向畸变来自于整个相机的组装过程,主要是由于透镜安装与成像平面不平行造成的。\n# 径向畸变 透过镜头边缘的光线很容易产生径向畸变,这种现象来源于“筒形”或“鱼眼”的影响。光线离镜头中心越远,畸变越大。\n从图像可以看出,径向畸变以某一个中心往外延伸,且越往外,畸变越大;显然畸变与距离成一种非线性的变换关系,参考众多文献,可以用多项式来近似: $$ \\begin{matrix} x_{rcrt} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\\\\\\\ y_{rcrt} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\end{matrix} $$ 其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$k_1, k_2, k_3$ 是径向畸变系数,$r^2 = x^2 + y^2$。\n# 切向畸变 切向畸变主要发生在相机sensor和镜头不平行的情况下;因为有夹角,所以光透过镜头传到图像传感器上时,成像位置发生了变化。\n$$ \\begin{matrix} x_{tcrt} = x + [2p_1 xy + p_2 (r^2 + 2 x^2)] \\\\\\\\ y_{tcrt} = y + [2p_2 xy + p_1 (r^2 + 2 y^2)] \\end{matrix} $$ 其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$p_1, p_2$ 是切向畸变系数,$r^2 = x^2 + y^2$。\n# 消除镜头畸变 考虑镜头畸变前,我们可以将相机标定简单描述为以下过程:像素坐标 $(u_{ccd}, v_{ccd})$ $\\to$ 图像坐标 $(x, y)$ $\\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\\to$ 世界坐标 $(X_w, Y_w, Z_w)$。\n此时我们考虑加入镜头畸变: $$ \\begin{matrix} x_{crt} = x_{rcrt} + x_{tcrt} \\\\\\\\ y_{crt} = y_{rcrt} + y_{tcrt} \\end{matrix} $$ 得到消除镜头畸变的相机标定流程:像素坐标 $(u_{ccd - crt}, v_{ccd - crt})$ $\\to$ 图像坐标 $(x_{crt}, y_{crt})$ $\\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\\to$ 世界坐标 $(X_w, Y_w, Z_w)$。\n# 标定板的作用 # 相机标定中的参数 针孔相机模型中,只要确定这9个参数就可以唯一的确定针孔相机模型:\n$$ f_x,f_y,O_x,O_y,k_1,k_2,k_3,p_1,p_2 $$\n这个过程就称为「相机标定」,其中前4个我们称为内参数,后5个称为畸变参数,畸变参数是为了补充内参的。所以一旦相机结构固定,包括镜头结构固定,对焦距离固定,我们就可以用这9个的参数去近似这个相机。这里说的「镜头结构固定」,按我个人的理解,除了焦距固定之外,也应当包含光圈固定,因为改变光圈的大小,除了景深之外,是有可能改变针孔相机模型中的光心位置,但是影响并不是很大。这意味着标定好的相机如果改变光圈大小,会使得标定误差变大但应该不会大到难以接受的地步。\n对于针孔相机本身需要拟合的方程如下:\n$$ \\begin{bmatrix} u_{ccd - crt} * Z\\\\ v_{ccd - crt} * Z\\\\ Z \\end{bmatrix} = J(k_1, k_2, k_3, p_1, p_2) \\begin{bmatrix} f_x \u0026amp; 0 \u0026amp; O_x \\\\ 0 \u0026amp; f_y \u0026amp; O_y \\\\ 0 \u0026amp; 0 \u0026amp; 1 \\end{bmatrix} \\begin{bmatrix} X \\\\ Y \\\\ X \\end{bmatrix} $$\n因此,我们现在的任务就是找出一大堆具有对应关系的像点 ${(u_{ccd - crt}, v_{ccd - crt}) ^T }$ 和物点 ${ (X, Y, Z)^T }$ 的点作为样本,来训练出模型的参数。这里就引发了两个问题:\n这么多像点和物点如何匹配? 即便现在知道物点的位置,如何用相机坐标系来表达物点的位置 $(X, Y, Z)$? 为了解决上述问题,标定板应运而生。标定板的一大作用,确定物点和像点的对应性。这里用到的原理主要是「透视不变性」,打个比方,你近看一个人和远看一个人,虽然他的鼻子大小变了,你看鼻子的视角也变了,但是拓扑结构肯定是不变的,你也不可能把鼻子看成是嘴巴。\n所以在标定板中,印刷了拓扑结构,广泛应用的是棋盘格和圆点格,这两种之所以成为主流,不仅是因为它们的拓扑结构明确且均匀,更重要的是检测其拓扑结构的算法简单且有效。棋盘格检测的是角点,只要对拍摄到的棋盘格图像横纵两个方向计算梯度就可获得;而圆点格的检测只需要对拍摄到的圆点格图样计算质心即可。假如你开发了一套非常完美的检测人脸全部特征的算法,你完全可以用你的照片当作标定板。\n按照我的经验,圆点格的效果应该是好于棋盘格,因为圆点质心的「透视不变性」要比棋盘格的角点稳定的多。下图是同样尺寸、同样比例棋盘格和圆点在最大重投影误差处的误差对比,红色十字是提取的角点/质心,绿色圆圈是针孔相机模型计算出来认为的角点/质心位置。\n但是圆点格的检测似乎是Halcon的专利(存疑),因此OpenCV和Matlab标定工具箱用的是棋盘格,要用圆点格得要自己写算法。下文中提到的标定板说的都是棋盘格。\n标定板的第二大作用是把标定板中的角点变换到相机坐标系下的坐标 $(X, Y, Z)$。对于标定的初学者来说,很容易忽略的一点是标定板是具有标定板坐标系的。换句话说,标定板中的每个角点,在标定板坐标系下的位置是确定并且是已知的。\n而标定板坐标系变换到相机坐标系的变换矩阵,我们称它的元素为外参数。\n# 如何使用标定板 如果用OpenCV或Matlab标定工具箱进行标定,需要给出棋盘格的物理尺寸,这其实就是在建立标定板坐标系,从测量的角度讲,标定板的精度是相机标定精度的基准,是误差传递链上的第一个环节。所以为了使针孔相机模型更逼近真实相机,对标定板的质量有以下要求(按重要性顺序):\n标定板的平面度高,棋盘格是直角。 标定板每个格子尺寸的高一致性。 真实尺寸与标称尺寸的差异小。 ","date":"2023-10-08T14:30:49Z","image":"https://3000ye.com/p/camera-calibration/board_hu0a6050398035f85c674f0491317f5983_14125_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/camera-calibration/","title":"Camera Calibration"},{"content":" # Test page # picture # math $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$\n# code 1 2 3 4 5 6 7 8 9 #include \u0026#34;bits/stdc++.h\u0026#34; using namespace std; int main() { cout \u0026lt;\u0026lt; \u0026#34;Hello World!\u0026#34; \u0026lt;\u0026lt; endl; return 0; } # Test $\\LaTeX$ # $\\frac{2}{3}$ ","date":"2023-10-08T14:30:49Z","image":"https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg","permalink":"https://3000ye.com/p/test/","title":"Test"}] \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..ec4bfc6 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,154 @@ + + + + https://3000ye.com/ + 2024-03-21T23:12:32+08:00 + + https://3000ye.com/p/clear-c-ch09-function-application/ + 2024-03-21T23:12:32+08:00 + + https://3000ye.com/post/ + 2024-03-21T23:12:32+08:00 + + https://3000ye.com/tags/cpp/ + 2024-03-18T21:06:19+08:00 + + https://3000ye.com/tags/ + 2024-03-18T21:06:19+08:00 + + https://3000ye.com/p/threads/ + 2024-03-18T21:06:19+08:00 + + https://3000ye.com/categories/ + 2024-02-21T19:14:32+08:00 + + https://3000ye.com/tags/data-structure/ + 2024-02-21T19:14:32+08:00 + + https://3000ye.com/categories/data-structure/ + 2024-02-21T19:14:32+08:00 + + https://3000ye.com/p/linked-list/ + 2024-02-21T19:14:32+08:00 + + https://3000ye.com/tags/beamer/ + 2024-02-16T23:33:47+08:00 + + https://3000ye.com/tags/dhu/ + 2024-02-16T23:33:47+08:00 + + https://3000ye.com/p/dhubeamer/ + 2024-02-16T23:33:47+08:00 + + https://3000ye.com/tags/latex/ + 2024-02-16T23:33:47+08:00 + + https://3000ye.com/categories/note-tools/ + 2024-02-16T23:33:47+08:00 + + https://3000ye.com/tags/cppjson/ + 2024-01-01T23:18:32+08:00 + + https://3000ye.com/categories/cppjson/ + 2024-01-01T23:18:32+08:00 + + https://3000ye.com/p/cppjson-ch02/ + 2024-01-01T23:18:32+08:00 + + https://3000ye.com/p/2023-summary/ + 2023-12-31T15:40:26+08:00 + + https://3000ye.com/p/happy-geek-prd/ + 2023-12-28T14:18:06+08:00 + + https://3000ye.com/tags/pm/ + 2023-12-28T14:18:06+08:00 + + https://3000ye.com/tags/prd/ + 2023-12-28T14:18:06+08:00 + + https://3000ye.com/tags/git/ + 2023-12-26T16:23:26+08:00 + + https://3000ye.com/tags/ssh/ + 2023-12-26T16:23:26+08:00 + + https://3000ye.com/p/ssh-key-agent/ + 2023-12-26T16:23:26+08:00 + + https://3000ye.com/tags/feishu/ + 2023-12-22T13:47:39+08:00 + + https://3000ye.com/p/feishu-robot/ + 2023-12-22T13:47:39+08:00 + + https://3000ye.com/p/cppjson-ch01/ + 2023-12-16T23:33:44+08:00 + + https://3000ye.com/tags/c++-primer/ + 2023-12-10T15:58:28+08:00 + + https://3000ye.com/categories/c++-primer/ + 2023-12-10T15:58:28+08:00 + + https://3000ye.com/p/c-primer-ch09/ + 2023-12-10T15:58:28+08:00 + + https://3000ye.com/p/c-primer-ch08/ + 2023-12-10T15:24:27+08:00 + + https://3000ye.com/p/c-primer-ch07/ + 2023-12-10T14:35:42+08:00 + + https://3000ye.com/p/c-primer-ch06/ + 2023-12-08T21:13:28+08:00 + + https://3000ye.com/p/c-primer-ch04/ + 2023-11-28T08:49:13+08:00 + + https://3000ye.com/p/c-primer-ch03/ + 2023-11-14T22:14:23+08:00 + + https://3000ye.com/p/c-primer-ch02/ + 2023-11-06T22:34:55+08:00 + + https://3000ye.com/p/slides/ + 2023-10-20T10:04:55+08:00 + + https://3000ye.com/p/dhubachelor/ + 2023-10-12T16:41:55+08:00 + + https://3000ye.com/p/elegant-latex/ + 2023-10-09T17:18:55+08:00 + + https://3000ye.com/tags/camera/ + 2023-10-08T14:30:49+00:00 + + https://3000ye.com/p/camera-calibration/ + 2023-10-08T14:30:49+00:00 + + https://3000ye.com/p/test/ + 2023-10-08T14:30:49+00:00 + + https://3000ye.com/categories/test-category/ + 2023-10-08T14:30:49+00:00 + + https://3000ye.com/tags/test-tag/ + 2023-10-08T14:30:49+00:00 + + https://3000ye.com/archives/ + 2022-03-06T00:00:00+00:00 + + https://3000ye.com/page/ + 2022-03-06T00:00:00+00:00 + + https://3000ye.com/about/ + + https://3000ye.com/friends/ + + https://3000ye.com/search/ + + https://3000ye.com/stars/ + + diff --git a/stars/index.html b/stars/index.html new file mode 100644 index 0000000..7319b00 --- /dev/null +++ b/stars/index.html @@ -0,0 +1,581 @@ + + + + +Stars + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + +
+

+ Stars +

+ + +
+ + + + + +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ +
+ + + +
+ + +
+ + + +
+ + + + + + + + + Licensed under CC BY-NC-SA 4.0 +
+
+ + + + + +
+ + +
+ + + + + + + +
+ + + + + + + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + + + + +
+
+ + + + + diff --git a/tags/beamer/index.html b/tags/beamer/index.html new file mode 100644 index 0000000..d05222a --- /dev/null +++ b/tags/beamer/index.html @@ -0,0 +1,536 @@ + + + + +Tag: Beamer - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Beamer

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/beamer/index.xml b/tags/beamer/index.xml new file mode 100644 index 0000000..6aaa8b9 --- /dev/null +++ b/tags/beamer/index.xml @@ -0,0 +1,44 @@ + + + + Beamer on 3000ye's Blog + https://3000ye.com/tags/beamer/ + Recent content in Beamer on 3000ye's Blog + Hugo -- gohugo.io + en-us + Fri, 16 Feb 2024 23:33:47 +0800 + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + + diff --git a/tags/beamer/page/1/index.html b/tags/beamer/page/1/index.html new file mode 100644 index 0000000..a3bec92 --- /dev/null +++ b/tags/beamer/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/beamer/ + + + + + + diff --git a/tags/c++-primer/index.html b/tags/c++-primer/index.html new file mode 100644 index 0000000..43b5f64 --- /dev/null +++ b/tags/c++-primer/index.html @@ -0,0 +1,656 @@ + + + + +Tag: C++ Primer - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

7 pages

+

C++ Primer

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/c++-primer/index.xml b/tags/c++-primer/index.xml new file mode 100644 index 0000000..55b4428 --- /dev/null +++ b/tags/c++-primer/index.xml @@ -0,0 +1,1766 @@ + + + + C++ Primer on 3000ye's Blog + https://3000ye.com/tags/c++-primer/ + Recent content in C++ Primer on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 10 Dec 2023 15:58:28 +0800 + C++ Primer Ch09 + https://3000ye.com/p/c-primer-ch09/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/p/c-primer-ch09/ + <img src="https://3000ye.com/p/c-primer-ch09/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch09" /><h1 id="顺序容器"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8">#</a> + 顺序容器 +</h1><p>一个容器就是一些特定类型对象的集合,<strong>顺序容器(sequence container)</strong> 为程序员提供了控制元素存储和访问顺序的能力。</p> +<h2 id="顺序容器概述"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8%e6%a6%82%e8%bf%b0">#</a> + 顺序容器概述 +</h2><p>下面列出了标准库中的顺序容器,不同容器有不同的性能折中:</p> +<ul> +<li>想容器添加或从容器删除元素的代价。</li> +<li>非顺序访问容器中元素的代价。</li> +</ul> +<table> +<thead> +<tr> +<th>容器类型</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector</code></td> +<td>可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。</td> +</tr> +<tr> +<td><code>deque</code></td> +<td>双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。</td> +</tr> +<tr> +<td><code>list</code></td> +<td>双向链表。只支持双向顺序访问。在<code>list</code>中任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>forward_list</code></td> +<td>单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>array</code></td> +<td>固定大小数组。支持快速随机访问。不能添加或者删除元素。</td> +</tr> +<tr> +<td><code>string</code></td> +<td>与<code>vector</code>相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。</td> +</tr> +</tbody> +</table> +<ul> +<li>除了固定大小的 <code>array</code> 外,其他容器都提供高效、灵活的内存管理。</li> +<li>通常使用 <code>vector</code> 是最好的选择,除非你有很好的理由选择其他容器。</li> +<li>如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序要求随记访问元素,应使用 <code>vector</code> 或 <code>deque</code>。</li> +<li>如果程序要求在容器的中间插入或删除元素,应使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 <code>deque</code>。</li> +</ul> +<h2 id="容器库概念"> + <a href="#%e5%ae%b9%e5%99%a8%e5%ba%93%e6%a6%82%e5%bf%b5">#</a> + 容器库概念 +</h2><p>容器类型操作上形成了一种层次:</p> +<ul> +<li>某些操作是所有容器类型都提供的。</li> +<li>另一些操作仅针对顺序容器、关联容器或无序容器。</li> +</ul> +<h3 id="类型"> + <a href="#%e7%b1%bb%e5%9e%8b">#</a> + 类型 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iterator</code></td> +<td>此容器类型的迭代器类型</td> +</tr> +<tr> +<td><code>const_iterator</code></td> +<td>可以读取元素但不能修改元素的迭代器类型</td> +</tr> +<tr> +<td><code>size_type</code></td> +<td>无符号整数类型,足够保存此种容器类型最大可能的大小</td> +</tr> +<tr> +<td><code>difference_type</code></td> +<td>带符号整数类型,足够保存两个迭代器之间的距离</td> +</tr> +<tr> +<td><code>value_type</code></td> +<td>元素类型</td> +</tr> +<tr> +<td><code>reference</code></td> +<td>元素的左值类型;和<code>value_type &amp;</code>含义相同</td> +</tr> +<tr> +<td><code>const_reference</code></td> +<td>元素的<code>const</code>左值类型,即<code>const value_type &amp;</code></td> +</tr> +</tbody> +</table> +<h3 id="构造函数"> + <a href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0">#</a> + 构造函数 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>C c;</code></td> +<td>默认构造函数,构造空容器</td> +</tr> +<tr> +<td><code>C c1(c2);</code>或<code>C c1 = c2;</code></td> +<td>构造<code>c2</code>的拷贝<code>c1</code></td> +</tr> +<tr> +<td><code>C c(b, e)</code></td> +<td>构造<code>c</code>,将迭代器<code>b</code>和<code>e</code>指定范围内的所有元素拷贝到<code>c</code></td> +</tr> +<tr> +<td><code>C c(a, b, c...)</code></td> +<td>列表初始化<code>c</code></td> +</tr> +<tr> +<td><code>C c(n)</code></td> +<td>只支持顺序容器,且不包括<code>array</code>,包含<code>n</code>个元素,这些元素进行了值初始化</td> +</tr> +<tr> +<td><code>C c(n, t)</code></td> +<td>包含<code>n</code>个初始值为<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>只有顺序容器的构造函数才接受大小参数,关联容器并不支持。</li> +<li><code>array</code>具有固定大小。</li> +<li>和其他容器不同,默认构造的<code>array</code>是非空的。</li> +<li>直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。</li> +<li>使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。</li> +</ul> +<h3 id="赋值和swap"> + <a href="#%e8%b5%8b%e5%80%bc%e5%92%8cswap">#</a> + 赋值和<code>swap</code> +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c1 = c2;</code></td> +<td>将<code>c1</code>中的元素替换成<code>c2</code>中的元素</td> +</tr> +<tr> +<td><code>c1 = {a, b, c...}</code></td> +<td>将<code>c1</code>中的元素替换成列表中的元素(不适用于<code>array</code>)</td> +</tr> +<tr> +<td><code>c1.swap(c2)</code></td> +<td>交换<code>c1</code>和<code>c2</code>的元素</td> +</tr> +<tr> +<td><code>swap(c1, c2)</code></td> +<td>等价于<code>c1.swap(c2)</code></td> +</tr> +<tr> +<td><code>c.assign(b, e)</code></td> +<td>将<code>c</code>中的元素替换成迭代器<code>b</code>和<code>e</code>表示范围中的元素,<code>b</code>和<code>e</code>不能指向<code>c</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(il)</code></td> +<td>将<code>c</code>中的元素替换成初始化列表<code>il</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(n, r)</code></td> +<td>将<code>c</code>中的元素替换为<code>n</code>个值是<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>使用非成员版本的<code>swap</code>是一个好习惯。</li> +<li><code>assign</code>操作不适用于关联容器和<code>array</code></li> +</ul> +<h3 id="大小"> + <a href="#%e5%a4%a7%e5%b0%8f">#</a> + 大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.size()</code></td> +<td><code>c</code>中元素的数目(不支持<code>forward_list</code>)</td> +</tr> +<tr> +<td><code>c.max_size()</code></td> +<td><code>c</code>中可保存的最大元素数目</td> +</tr> +<tr> +<td><code>c.empty()</code></td> +<td>若<code>c</code>中存储了元素,返回<code>false</code>,否则返回<code>true</code></td> +</tr> +</tbody> +</table> +<h3 id="添加元素"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%85%83%e7%b4%a0">#</a> + 添加元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.push_back(t)</code></td> +<td>在<code>c</code>尾部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_back(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.push_front(t)</code></td> +<td>在<code>c</code>头部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_front(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前创建一个值是<code>t</code>的元素,返回指向新元素的迭代器</td> +</tr> +<tr> +<td><code>c.emplace(p, args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, n, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前插入<code>n</code>个值为<code>t</code>的元素,返回指向第一个新元素的迭代器;如果<code>n</code>是0,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, b, e)</code></td> +<td>将迭代器<code>b</code>和<code>e</code>范围内的元素,插入到<code>p</code>指向的元素之前;如果范围为空,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, il)</code></td> +<td><code>il</code>是一个花括号包围中的元素值列表,将其插入到<code>p</code>指向的元素之前;如果<code>il</code>是空,则返回<code>p</code></td> +</tr> +</tbody> +</table> +<ul> +<li>因为这些操作会改变大小,因此不适用于<code>array</code>。</li> +<li><code>forward_list</code>有自己专有版本的<code>insert</code>和<code>emplace</code>。</li> +<li><code>forward_list</code>不支持<code>push_back</code>和<code>emplace_back</code>。</li> +<li>当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。</li> +<li><code>emplace</code>开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。</li> +<li>传递给<code>emplace</code>的参数必须和元素类型的构造函数相匹配。</li> +</ul> +<h3 id="访问元素"> + <a href="#%e8%ae%bf%e9%97%ae%e5%85%83%e7%b4%a0">#</a> + 访问元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.back()</code></td> +<td>返回<code>c</code>中尾元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c.front()</code></td> +<td>返回<code>c</code>中头元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c[n]</code></td> +<td>返回<code>c</code>中下标是<code>n</code>的元素的引用,<code>n</code>时候一个无符号证书。若<code>n&gt;=c.size()</code>,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.at(n)</code></td> +<td>返回下标为<code>n</code>的元素引用。如果下标越界,则抛出<code>out_of_range</code>异常</td> +</tr> +</tbody> +</table> +<ul> +<li>访问成员函数返回的是引用。</li> +<li><code>at</code>和下标操作只适用于<code>string</code>、<code>vector</code>、<code>deque</code>、<code>array</code>。</li> +<li><code>back</code>不适用于<code>forward_list</code>。</li> +<li>如果希望下标是合法的,可以使用<code>at</code>函数。</li> +</ul> +<h3 id="删除元素"> + <a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0">#</a> + 删除元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.pop_back()</code></td> +<td>删除<code>c</code>中尾元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.pop_front()</code></td> +<td>删除<code>c</code>中首元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.erase(p)</code></td> +<td>删除迭代器<code>p</code>指向的元素,返回一个指向被删除元素之后的元素的迭代器,若<code>p</code>本身是尾后迭代器,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.erase(b, e)</code></td> +<td>删除迭代器<code>b</code>和<code>e</code>范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若<code>e</code>本身就是尾后迭代器,则返回尾后迭代器</td> +</tr> +<tr> +<td><code>c.clear()</code></td> +<td>删除<code>c</code>中所有元素,返回<code>void</code></td> +</tr> +</tbody> +</table> +<ul> +<li>会改变容器大小,不适用于<code>array</code>。</li> +<li><code>forward_list</code>有特殊版本的<code>erase</code></li> +<li><code>forward_list</code>不支持<code>pop_back</code></li> +<li><code>vector</code>和<code>string</code>不支持<code>pop_front</code></li> +</ul> +<h3 id="特殊的-forwad_list操作"> + <a href="#%e7%89%b9%e6%ae%8a%e7%9a%84-forwad_list%e6%93%8d%e4%bd%9c">#</a> + 特殊的 <code>forwad_list</code>操作 +</h3><ul> +<li>链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。</li> +<li><code>forward_list</code>定义了<code>before_begin</code>,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。</li> +</ul> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>lst.before_begin()</code></td> +<td>返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。</td> +</tr> +<tr> +<td><code>lst.cbefore_begin()</code></td> +<td>同上,但是返回的是常量迭代器。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象</td> +</tr> +<tr> +<td><code>lst.insert_after(p, n, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象,<code>n</code>是数量。若<code>n</code>是0则函数行为未定义</td> +</tr> +<tr> +<td><code>lst.insert_after(p, b, e)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由迭代器<code>b</code>和<code>e</code>指定范围。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, il)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由<code>il</code>指定初始化列表。</td> +</tr> +<tr> +<td><code>emplace_after(p, args)</code></td> +<td>使用<code>args</code>在<code>p</code>之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若<code>p</code>为尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(p)</code></td> +<td>删除<code>p</code>指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若<code>p</code>指向<code>lst</code>的尾元素或者是一个尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(b, e)</code></td> +<td>类似上面,删除对象换成从<code>b</code>到<code>e</code>指定的范围。</td> +</tr> +</tbody> +</table> +<h3 id="改变容器大小"> + <a href="#%e6%94%b9%e5%8f%98%e5%ae%b9%e5%99%a8%e5%a4%a7%e5%b0%8f">#</a> + 改变容器大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.resize(n)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,若<code>n&lt;c.size()</code>,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化</td> +</tr> +<tr> +<td><code>c.resize(n, t)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,任何新添加的元素都初始化为值<code>t</code></td> +</tr> +</tbody> +</table> +<h3 id="获取迭代器"> + <a href="#%e8%8e%b7%e5%8f%96%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 获取迭代器 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.begin()</code>, <code>c.end()</code></td> +<td>返回指向<code>c</code>的首元素和尾元素之后位置的迭代器</td> +</tr> +<tr> +<td><code>c.cbegin()</code>, <code>c.cend()</code></td> +<td>返回<code>const_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>以<code>c</code>开头的版本是C++11新标准引入的</li> +<li>当不需要写访问时,应该使用<code>cbegin</code>和<code>cend</code>。</li> +</ul> +<h3 id="反向容器的额外成员"> + <a href="#%e5%8f%8d%e5%90%91%e5%ae%b9%e5%99%a8%e7%9a%84%e9%a2%9d%e5%a4%96%e6%88%90%e5%91%98">#</a> + 反向容器的额外成员 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>reverse_iterator</code></td> +<td>按逆序寻址元素的迭代器</td> +</tr> +<tr> +<td><code>const_reverse_iterator</code></td> +<td>不能修改元素的逆序迭代器</td> +</tr> +<tr> +<td><code>c.rbegin()</code>, <code>c.rend()</code></td> +<td>返回指向<code>c</code>的尾元素和首元素之前位置的迭代器</td> +</tr> +<tr> +<td><code>c.crbegin()</code>, <code>c.crend()</code></td> +<td>返回<code>const_reverse_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>不支持<code>forward_list</code></li> +</ul> + + + + C++ Primer Ch08 + https://3000ye.com/p/c-primer-ch08/ + Sun, 10 Dec 2023 15:24:27 +0800 + + https://3000ye.com/p/c-primer-ch08/ + <img src="https://3000ye.com/p/c-primer-ch08/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch08" /><h1 id="io-库"> + <a href="#io-%e5%ba%93">#</a> + IO 库 +</h1><p>我们的程序已经使用了很多 IO 库设施:</p> +<ul> +<li><code>istream</code> (输入流)类型,提供输入操作。</li> +<li><code>ostream</code> (输出流)类型,提供输出操作。</li> +<li><code>cin</code>,一个 <code>istream</code> 对象,从标准输入读取数据。</li> +<li><code>cout</code>,一个 <code>ostream</code> 对象,想标准输出写入数据。</li> +<li><code>cerr</code>,一个 <code>ostream</code> 对象,通常用于输出程序错误信息,写入到标准错误。</li> +<li><code>&gt;&gt;</code> 运算符,用来从一个 <code>istream</code> 对象中读取输入数据。</li> +<li><code>&lt;&lt;</code> 运算符,用来向一个 <code>ostream</code> 对象中写入输出数据。</li> +<li><code>getline</code> 函数,从一个给定的 <code>istream</code> 对象中读取一行数据,存入到一个给定的 <code>string</code> 对象中。</li> +</ul> +<h2 id="io-库-1"> + <a href="#io-%e5%ba%93-1">#</a> + IO 库 +</h2><p>IO 类型和对象一般都是操纵 <code>char</code> 数据的,但有些使用需要对文件、<code>string</code> 进行操作,因此分别定义了三个头文件:</p> +<ul> +<li><code>iostream</code> 头文件:从标准流中读写数据,<code>istream</code>、<code>ostream</code> 等。</li> +<li><code>fstream</code> 头文件:从文件中读写数据,<code>ifstream</code>、<code>ofstream</code> 等。</li> +<li><code>sstream</code> 头文件:从字符串中读写数据,<code>istringstream</code>、<code>ostringstream</code> 等。</li> +</ul> + + + + C++ Primer Ch07 + https://3000ye.com/p/c-primer-ch07/ + Sun, 10 Dec 2023 14:35:42 +0800 + + https://3000ye.com/p/c-primer-ch07/ + <img src="https://3000ye.com/p/c-primer-ch07/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch07" /><h1 id="类"> + <a href="#%e7%b1%bb">#</a> + 类 +</h1><p>类的基本思想是 <strong>数据抽象(data abstraction)</strong> 和 <strong>封装(encapsulation)</strong>。数据抽象是一种依赖于 <strong>接口(interface)</strong> 和 <strong>实现(implementation)</strong> 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。</p> +<p>封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。</p> +<p>类要想实现数据抽象和封装,首先需要定义一个 <strong>抽象数据类型(abstract data type)</strong>。</p> +<h2 id="定义抽象数据类型"> + <a href="#%e5%ae%9a%e4%b9%89%e6%8a%bd%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#</a> + 定义抽象数据类型 +</h2><h2 id="访问控制与封装"> + <a href="#%e8%ae%bf%e9%97%ae%e6%8e%a7%e5%88%b6%e4%b8%8e%e5%b0%81%e8%a3%85">#</a> + 访问控制与封装 +</h2><p>我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 <code>C++</code> 中使用 <strong>访问说明符(access specifiers)</strong> 加强类的封装:</p> +<ul> +<li>定义在 <code>public</code> 说明符之后的成员,在整个程序内都可被访问。</li> +<li>定义在 <code>private</code> 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。</li> +</ul> +<p>定义新的 <code>Sales_data</code> 类:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sales_data</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bookNo</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="n">units_sold</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">revenue</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="nf">avg_price</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">units_sold</span> <span class="o">?</span> <span class="n">revenue</span> <span class="o">/</span> <span class="nl">units_sold</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">p</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="n">units_sold</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">revenue</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">istream</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">isbn</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">bookNo</span><span class="p">;</span> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span> <span class="o">&amp;</span><span class="n">combine</span><span class="p">(</span><span class="k">const</span> <span class="n">Sales_data</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch06 + https://3000ye.com/p/c-primer-ch06/ + Fri, 08 Dec 2023 21:13:28 +0800 + + https://3000ye.com/p/c-primer-ch06/ + <img src="https://3000ye.com/p/c-primer-ch06/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch06" /><h1 id="函数"> + <a href="#%e5%87%bd%e6%95%b0">#</a> + 函数 +</h1><p>函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。</p> +<h2 id="函数基础"> + <a href="#%e5%87%bd%e6%95%b0%e5%9f%ba%e7%a1%80">#</a> + 函数基础 +</h2><p>一个典型的 <strong>函数(function)</strong> 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 <strong>形参(parameter)</strong> 组成的列表以及函数体。</p> +<p>我们通过 <strong>调用运算符(call operator)</strong> 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 <strong>实参(argument)</strong> 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。</p> +<p><strong>编写函数</strong>:例如编写一个求 <code>n</code> 的阶乘的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fact</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">*=</span> <span class="n">n</span> <span class="o">--</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><strong>调用函数</strong>:要调用 <code>fact</code> 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;5! = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,<strong>主调函数(calling funciton)</strong> 的执行暂停,<strong>被调函数(called funciton)</strong> 开始执行。</p> +<h3 id="局部对象"> + <a href="#%e5%b1%80%e9%83%a8%e5%af%b9%e8%b1%a1">#</a> + 局部对象 +</h3><p>在 <code>C++</code> 语言中,名字有作用域,对象有 <strong>生命周期(lifetime)</strong>:</p> +<ul> +<li>名字的作用域是程序文本的一部分,名字在其中可见。</li> +<li>对象的生命周期是程序执行过程中该对象存在的一段时间。</li> +</ul> +<p>在函数体内,形参和内部定义的变量统称为 <strong>局部变量(local variable)</strong>,它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。</p> +<p><strong>局部静态对象</strong>:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 <code>static</code> 类型从而获得这样的对象,<strong>局部静态对象(local static object)</strong> 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。</p> +<h2 id="参数传递"> + <a href="#%e5%8f%82%e6%95%b0%e4%bc%a0%e9%80%92">#</a> + 参数传递 +</h2> + + + C++ Primer Ch04 + https://3000ye.com/p/c-primer-ch04/ + Tue, 28 Nov 2023 08:49:13 +0800 + + https://3000ye.com/p/c-primer-ch04/ + <img src="https://3000ye.com/p/c-primer-ch04/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch04" /><h1 id="表达式"> + <a href="#%e8%a1%a8%e8%be%be%e5%bc%8f">#</a> + 表达式 +</h1><p>表达式由一个或多个 <strong>运算对象(operand)</strong> 组成对表达式求值将得到一个 <strong>结果(result)</strong>。</p> +<h2 id="基础"> + <a href="#%e5%9f%ba%e7%a1%80">#</a> + 基础 +</h2><h3 id="基本概念"> + <a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a> + 基本概念 +</h3><p><code>C++</code> 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。</p> +<p>对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。</p> +<p>在表达式求值过程中,小整数类型(<code>bool</code>、<code>char</code>、<code>short</code>)等通常会被 <strong>提升(promoted)</strong> 成较大的整数类型(<code>int</code>)。</p> +<p>当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 <strong>重载运算符(overloaded operator)</strong>。</p> +<p><code>C++</code> 的表达式要不然是 <strong>右值(rvalue)</strong>,要不然就是 <strong>左值(lvalue)</strong>,这两个名词是从 <code>C</code> 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p> +<h3 id="求值顺序"> + <a href="#%e6%b1%82%e5%80%bc%e9%a1%ba%e5%ba%8f">#</a> + 求值顺序 +</h3><p>在大多数情况下,表达式求值的顺序是没有明确指定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">f1</span><span class="p">()</span> <span class="o">*</span> <span class="n">f2</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们知道 <code>f1</code> 和 <code>f2</code> 一定会在执行乘法之前被调用,但是无法知道是 <code>f1</code> 先被调用还是 <code>f2</code> 先被调用。对于没有指定调用顺序的程序来说,如果 <code>f1</code> 和 <code>f2</code> 同时修改了同一个对象,将会引发错误并产生未定义的行为。</p> +<h2 id="算术运算符"> + <a href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 算术运算符 +</h2><p><strong>溢出</strong>:当计算的结果超出该类型所能表示的范围时就会产生溢出。</p> +<p><strong><code>bool</code> 类型不应该参与计算</strong>。</p> +<p><strong>取余运算</strong>:<code>m % n</code> 的结果的符号与 <code>m</code> 相同。</p> +<h2 id="逻辑运算符"> + <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 逻辑运算符 +</h2><p><strong>短路求值</strong>:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。</p> +<h2 id="赋值运算符"> + <a href="#%e8%b5%8b%e5%80%bc%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 赋值运算符 +</h2> + + + C++ Primer Ch03 + https://3000ye.com/p/c-primer-ch03/ + Tue, 14 Nov 2023 22:14:23 +0800 + + https://3000ye.com/p/c-primer-ch03/ + <img src="https://3000ye.com/p/c-primer-ch03/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch03" /><h1 id="字符串向量和数组"> + <a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%90%91%e9%87%8f%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 字符串、向量和数组 +</h1><h2 id="命名空间的-using-声明"> + <a href="#%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e7%9a%84-using-%e5%a3%b0%e6%98%8e">#</a> + 命名空间的 <code>using</code> 声明 +</h2><p>我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用<code>using</code>进行声明:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">;</span> <span class="c1">// 单独使用某个函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 批量声明 std 中所有函数 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>头文件中不应该包含 <code>using</code> 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。</p> +</blockquote> +<h2 id="标准库类型-string"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-string">#</a> + 标准库类型 <code>string</code> +</h2><p>标准库类型 <code>string</code> 表示可变长的字符序列,使用 <code>string</code> 类型必须首先包含 <code>string</code> 头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="定义和初始化-string-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-string-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>string</code> 对象 +</h3><p>初始化 <code>string</code> 对象的方式:</p> +<table> +<thead> +<tr> +<th>方式</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>string s1</code></td> +<td>默认初始化,<code>s1</code>是个空字符串</td> +</tr> +<tr> +<td><code>string s2(s1)</code></td> +<td><code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s2 = s1</code></td> +<td>等价于<code>s2(s1)</code>,<code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s3(&quot;value&quot;)</code></td> +<td><code>s3</code>是字面值“value”的副本,除了字面值最后的那个空字符外</td> +</tr> +<tr> +<td><code>string s3 = &quot;value&quot;</code></td> +<td>等价于<code>s3(&quot;value&quot;)</code>,<code>s3</code>是字面值&quot;value&quot;的副本</td> +</tr> +<tr> +<td><code>string s4(n, 'c')</code></td> +<td>把<code>s4</code>初始化为由连续<code>n</code>个字符<code>c</code>组成的串</td> +</tr> +</tbody> +</table> +<p>拷贝初始化(copy initialization):使用 <code>=</code> 将一个已有的对象拷贝到正在创建的对象。</p> +<p>直接初始化(direct initialization):通过括号给对象赋值。</p> +<h3 id="string-对象的操作"> + <a href="#string-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>string</code> 对象的操作 +</h3><p><code>string</code>的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>os &lt;&lt; s</code></td> +<td>将<code>s</code>写到输出流<code>os</code>当中,返回<code>os</code></td> +</tr> +<tr> +<td><code>is &gt;&gt; s</code></td> +<td>从<code>is</code>中读取字符串赋给<code>s</code>,字符串以空白分割,返回<code>is</code></td> +</tr> +<tr> +<td><code>getline(is, s)</code></td> +<td>从<code>is</code>中读取一行赋给<code>s</code>,返回<code>is</code></td> +</tr> +<tr> +<td><code>s.empty()</code></td> +<td><code>s</code>为空返回<code>true</code>,否则返回<code>false</code></td> +</tr> +<tr> +<td><code>s.size()</code></td> +<td>返回<code>s</code>中字符的个数</td> +</tr> +<tr> +<td><code>s[n]</code></td> +<td>返回<code>s</code>中第<code>n</code>个字符的引用,位置<code>n</code>从0计起</td> +</tr> +<tr> +<td><code>s1+s2</code></td> +<td>返回<code>s1</code>和<code>s2</code>连接后的结果</td> +</tr> +<tr> +<td><code>s1=s2</code></td> +<td>用<code>s2</code>的副本代替<code>s1</code>中原来的字符</td> +</tr> +<tr> +<td><code>s1==s2</code></td> +<td>如果<code>s1</code>和<code>s2</code>中所含的字符完全一样,则它们相等;<code>string</code>对象的相等性判断对字母的大小写敏感</td> +</tr> +<tr> +<td><code>s1!=s2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td> +<td>利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)</td> +</tr> +</tbody> +</table> +<p>读取 <code>string</code> 对象:</p> +<ul> +<li>使用 <code>IO</code> 操作符 <code>&gt;&gt;</code> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。</li> +<li>使用 <code>getline()</code> 函数读取:将一整行读取为 <code>string</code> 对象,包括空白。</li> +</ul> +<p><code>s.size()</code> 返回 <code>string::size_type</code> 类型,是 <strong>无符号</strong> 类型的值,不能和 <code>int</code> 混用。</p> +<p><code>s1 + s2</code> 使用时,必须保证至少其中一个为 <code>string</code> 类型。例如:<code>string s = &quot;hello&quot; + &quot;world&quot;</code> 错误,其 <code>+</code> 两边都为字符串字面值。</p> +<p><strong>字符串字面值</strong> 和 <code>string</code> 是不同的类型。</p> +<h3 id="处理-string-对象中的字符"> + <a href="#%e5%a4%84%e7%90%86-string-%e5%af%b9%e8%b1%a1%e4%b8%ad%e7%9a%84%e5%ad%97%e7%ac%a6">#</a> + 处理 <code>string</code> 对象中的字符 +</h3><p><code>C++</code> 修改了 <code>c</code> 的标准库 <code>ctype.h</code> 为 <code>cctype</code>,其中定义了一组标准函数:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>isalnum(c)</code></td> +<td>当<code>c</code>是字母或数字时为真</td> +</tr> +<tr> +<td><code>isalpha(c)</code></td> +<td>当<code>c</code>是字母时为真</td> +</tr> +<tr> +<td><code>iscntrl(c)</code></td> +<td>当<code>c</code>是控制字符时为真</td> +</tr> +<tr> +<td><code>isdigit(c)</code></td> +<td>当<code>c</code>是数字时为真</td> +</tr> +<tr> +<td><code>isgraph(c)</code></td> +<td>当<code>c</code>不是空格但可以打印时为真</td> +</tr> +<tr> +<td><code>islower(c)</code></td> +<td>当<code>c</code>是小写字母时为真</td> +</tr> +<tr> +<td><code>isprint(c)</code></td> +<td>当<code>c</code>是可打印字符时为真</td> +</tr> +<tr> +<td><code>ispunct(c)</code></td> +<td>当<code>c</code>是标点符号时为真</td> +</tr> +<tr> +<td><code>isspace(c)</code></td> +<td>当<code>c</code>是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)</td> +</tr> +<tr> +<td><code>isupper(c)</code></td> +<td>当<code>c</code>是大写字母时为真</td> +</tr> +<tr> +<td><code>isxdigit(c)</code></td> +<td>当<code>c</code>是十六进制数字时为真</td> +</tr> +<tr> +<td><code>tolower(c)</code></td> +<td>当<code>c</code>是大写字母,输出对应的小写字母;否则原样输出<code>c</code></td> +</tr> +<tr> +<td><code>toupper(c)</code></td> +<td>当<code>c</code>是小写字母,输出对应的大写字母;否则原样输出<code>c</code></td> +</tr> +</tbody> +</table> +<p>遍历字符串:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">c</span> <span class="p">:</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>str[idx]</code> 中的 <code>idx</code> 为 <code>string::size_type</code> 类型,如果使用 <code>int</code> 会隐式转换为该类型。</p> +<h2 id="标准库类型-vector"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-vector">#</a> + 标准库类型 <code>vector</code> +</h2><p>标准库类型 <code>vector</code> 表示对象的集合,其中给所有对象的类型都相同。因为 <code>vector</code> 容纳着其他对象,所以称其为 <strong>容器(container)</strong>,使用 <code>vector</code> 必须包含其头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>vector</code> 同时也是 <strong>类模板(class template)</strong>,模板本身不是类或函数,但可以使用模板创建类,这个过程称为 <strong>实例化(instantiation)</strong>。</p> +<p>当使用模板时,需要指出编译器应把类或函数实例化成何种类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// ls 保存 int 类型的对象 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">files</span><span class="p">;</span> <span class="c1">// 该向量中的元素是 vector 对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p><code>vector</code> 是模板,<code>vector&lt;int&gt;</code> 是类型。</p> +</blockquote> +<h3 id="定义和初始化-vector-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-vector-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>vector</code> 对象 +</h3><p>初始化<code>vector</code>对象的方法:</p> +<table> +<thead> +<tr> +<th>方法</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector&lt;T&gt; v1</code></td> +<td><code>v1</code>是一个空<code>vector</code>,它潜在的元素是<code>T</code>类型的,执行默认初始化</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2(v1)</code></td> +<td><code>v2</code>中包含有<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2 = v1</code></td> +<td>等价于<code>v2(v1)</code>,<code>v2</code>中包含<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v3(n, val)</code></td> +<td><code>v3</code>包含了n个重复的元素,每个元素的值都是<code>val</code></td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v4(n)</code></td> +<td><code>v4</code>包含了n个重复地执行了值初始化的对象</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5{a, b, c...}</code></td> +<td><code>v5</code>包含了初始值个数的元素,每个元素被赋予相应的初始值</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5={a, b, c...}</code></td> +<td>等价于<code>v5{a, b, c...}</code></td> +</tr> +</tbody> +</table> +<h3 id="vector-对象的操作"> + <a href="#vector-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>vector</code> 对象的操作: +</h3><p><code>vector</code>支持的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>v.emtpy()</code></td> +<td>如果<code>v</code>不含有任何元素,返回真;否则返回假</td> +</tr> +<tr> +<td><code>v.size()</code></td> +<td>返回<code>v</code>中元素的个数</td> +</tr> +<tr> +<td><code>v.push_back(t)</code></td> +<td>向<code>v</code>的尾端添加一个值为<code>t</code>的元素</td> +</tr> +<tr> +<td><code>v[n]</code></td> +<td>返回<code>v</code>中第<code>n</code>个位置上元素的<strong>引用</strong></td> +</tr> +<tr> +<td><code>v1 = v2</code></td> +<td>用<code>v2</code>中的元素拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 = {a,b,c...}</code></td> +<td>用列表中元素的拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 == v2</code></td> +<td><code>v1</code>和<code>v2</code>相等当且仅当它们的元素数量相同且对应位置的元素值都相同</td> +</tr> +<tr> +<td><code>v1 != v2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>,<code>&lt;=</code>,<code>&gt;</code>, <code>&gt;=</code></td> +<td>以字典顺序进行比较</td> +</tr> +</tbody> +</table> +<h2 id="迭代器介绍"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e4%bb%8b%e7%bb%8d">#</a> + 迭代器介绍 +</h2><p>除了下标运算符外,<strong>迭代器(iterator)</strong> 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。</p> +<h3 id="使用迭代器"> + <a href="#%e4%bd%bf%e7%94%a8%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 使用迭代器 +</h3><p>拥有迭代器的类型都具有 <code>begin</code> 和 <code>end</code> 成员,其中 <code>begin</code> 成员返回指向第一个元素的迭代器:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">e</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="c1">// b 和 e 类型相同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><code>end</code> 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 <code>end</code> 指向容器的 <strong>尾后(off the end)</strong> 元素。这样的迭代器通常没有意义,只是作为标记,被称为 <strong>尾后迭代器(off-the-end iterator)</strong> 或 <strong>尾迭代器(end iterator)</strong>。</p> +<blockquote> +<p>若容器为空,则 <code>begin</code> 和 <code>end</code> 都返回尾后迭代器。</p> +</blockquote> +<p>标准容器迭代器的运算符:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>*iter</code></td> +<td>返回迭代器<code>iter</code>所指向的<strong>元素的引用</strong></td> +</tr> +<tr> +<td><code>iter-&gt;mem</code></td> +<td>等价于<code>(*iter).mem</code></td> +</tr> +<tr> +<td><code>++iter</code></td> +<td>令<code>iter</code>指示容器中的下一个元素</td> +</tr> +<tr> +<td><code>--iter</code></td> +<td>令<code>iter</code>指示容器中的上一个元素</td> +</tr> +<tr> +<td><code>iter1 == iter2</code></td> +<td>判断两个迭代器是否相等</td> +</tr> +</tbody> +</table> +<blockquote> +<p>泛型编程:尽量使用 <code>!=</code> 来对迭代器进行判断</p> +</blockquote> +<p>迭代器也拥有自己的类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="p">;</span> <span class="c1">// it 是 vector&lt;int&gt; 类型的迭代器,可以读写元素 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">const_iterator</span> <span class="n">it2</span><span class="p">;</span> <span class="c1">// it2 只能读,不能写 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果容器中的值为常量,则 <code>begin</code> 和 <code>end</code> 返回 <code>const_iterator</code>,否则返回 <code>iterator</code>。</p> +<p>解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="s">&#34;str1&#34;</span><span class="p">,</span> <span class="s">&#34;str2&#34;</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">ls</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> <span class="c1">// s 为 &#34;str1&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">).</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 解引用访问 string 成员 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 作用同上 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="迭代器运算"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e8%bf%90%e7%ae%97">#</a> + 迭代器运算 +</h3><p><code>string</code> 和 <code>vector</code> 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 <strong>迭代器运算(iterator arithmetic)</strong>:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iter + n</code></td> +<td>迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter - n</code></td> +<td>迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter1 += n</code></td> +<td>迭代器加法的复合赋值语句,将<code>iter1</code>加n的结果赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 -= n</code></td> +<td>迭代器减法的复合赋值语句,将<code>iter2</code>减n的加过赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 - iter2</code></td> +<td>两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。</td> +</tr> +<tr> +<td><code>&gt;</code>、<code>&gt;=</code>、<code>&lt;</code>、<code>&lt;=</code></td> +<td>迭代器的关系运算符,如果某迭代器</td> +</tr> +</tbody> +</table> +<p>当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 <code>difference_type</code> 类型,是带符号整数型。</p> +<h2 id="数组"> + <a href="#%e6%95%b0%e7%bb%84">#</a> + 数组 +</h2><p>数组可以看做 <code>vector</code> 的低配版,其 <strong>长度固定</strong>。</p> +<h3 id="定义和初始化内置数组"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96%e5%86%85%e7%bd%ae%e6%95%b0%e7%bb%84">#</a> + 定义和初始化内置数组 +</h3><p>数组的声明和定义形如 <code>a[d]</code>,其中 <code>a</code> 是数组的名字,<code>d</code> 是数组的维度(大于 0):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="n">cnt2</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">arr2</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整型指针的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr3</span><span class="p">[</span><span class="n">cnt</span><span class="p">];</span> <span class="c1">// 报错,cnt 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr4</span><span class="p">[</span><span class="n">cnt2</span><span class="p">];</span> <span class="c1">// 含有 42 和整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> <span class="c1">// 自动计算长度的数组 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> <span class="c1">// 报错,长度不够 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a2</span><span class="p">[]</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组来初始化数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组进行赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="访问数组元素"> + <a href="#%e8%ae%bf%e9%97%ae%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0">#</a> + 访问数组元素 +</h3><p>数组的下标为 <code>size_t</code> 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。</p> +<blockquote> +<p>下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。</p> +</blockquote> +<h3 id="指针和数组"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 指针和数组 +</h3><p>使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">ls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ls</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// 数组的元素的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// 等价于 *p2 = &amp;ls[0] +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="c-风格字符串"> + <a href="#c-%e9%a3%8e%e6%a0%bc%e5%ad%97%e7%ac%a6%e4%b8%b2">#</a> + <code>C</code> 风格字符串 +</h3><p>字符串字面值是一种通用结构的实例,这种结构是 <code>C++</code> 由 <code>C</code> 继承而来的 <strong><code>C</code> 风格字符串(C-style character string)</strong> 。 +按此习惯书写的字符串存放在字符数组中并以 <strong>空字符结束(null terminated)</strong>。</p> +<blockquote> +<p>在 <code>C++</code> 程序中尽量不要使用 <code>C</code> 风格字符串,容易引起安全漏洞且不方便。</p> +</blockquote> +<p>C标准库String函数,定义在<code>&lt;cstring&gt;</code> 中:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>strlen(p)</code></td> +<td>返回<code>p</code>的长度,空字符不计算在内</td> +</tr> +<tr> +<td><code>strcmp(p1, p2)</code></td> +<td>比较<code>p1</code>和<code>p2</code>的相等性。如果<code>p1==p2</code>,返回0;如果<code>p1&gt;p2</code>,返回一个正值;如果<code>p1&lt;p2</code>,返回一个负值。</td> +</tr> +<tr> +<td><code>strcat(p1, p2)</code></td> +<td>将<code>p2</code>附加到<code>p1</code>之后,返回<code>p1</code></td> +</tr> +<tr> +<td><code>strcpy(p1, p2)</code></td> +<td>将<code>p2</code>拷贝给<code>p1</code>,返回<code>p1</code></td> +</tr> +</tbody> +</table> +<h2 id="多维数组"> + <a href="#%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 多维数组 +</h2><p>严格来说,<code>C++</code> 语言中没有多维数组,所谓的多维数组实际是数组的数组。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用范围-for-语句处理多维数组"> + <a href="#%e4%bd%bf%e7%94%a8%e8%8c%83%e5%9b%b4-for-%e8%af%ad%e5%8f%a5%e5%a4%84%e7%90%86%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 使用范围 <code>for</code> 语句处理多维数组 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">row</span><span class="p">:</span> <span class="n">arr</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">col</span><span class="p">:</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">col</span> <span class="o">=</span> <span class="n">cnt</span><span class="p">;</span> <span class="n">cnt</span> <span class="o">++</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch02 + https://3000ye.com/p/c-primer-ch02/ + Mon, 06 Nov 2023 22:34:55 +0800 + + https://3000ye.com/p/c-primer-ch02/ + <img src="https://3000ye.com/p/c-primer-ch02/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch02" /><h1 id="变量和基本类型"> + <a href="#%e5%8f%98%e9%87%8f%e5%92%8c%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b">#</a> + 变量和基本类型 +</h1><p>变量提供一个具名的、可供程序操作的存储空间。<code>C++</code>中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。</p> +<h2 id="变量的声明和定义"> + <a href="#%e5%8f%98%e9%87%8f%e7%9a%84%e5%a3%b0%e6%98%8e%e5%92%8c%e5%ae%9a%e4%b9%89">#</a> + 变量的声明和定义 +</h2><p>为了允许把程序拆分成多个逻辑部分来编写,<code>C++</code>语言支持<strong>分离式编译(separate complication)</strong> 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。</p> +<p>为了支持分离式编译,<code>C++</code>语言将声明和定义区分开来。<strong>声明(declaration)</strong> 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 <strong>定义(definition)</strong> 负责创建与名字关联的实体。</p> +<p>变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 声明 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义并赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义而非声明,extern失效 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><strong>注意:</strong> 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。</p> +<h3 id="标识符"> + <a href="#%e6%a0%87%e8%af%86%e7%ac%a6">#</a> + 标识符 +</h3><p><code>C++</code>的 <strong>标识符(identifier)</strong> 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。</p> +<h3 id="作用域"> + <a href="#%e4%bd%9c%e7%94%a8%e5%9f%9f">#</a> + 作用域 +</h3><p><strong>作用域(scope)</strong> 是程序的一部分,在其中名字有特定的含义,<code>C++</code>语言中大多数作用域都以花括号分隔。</p> +<p>作用域能彼此包含,被包含(嵌套)的作用域称为 <strong>内层作用域(inner scope)</strong>,包含着别的作用域的作用域称为 <strong>外层作用域(outer scope)</strong>。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 全局作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">inner</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span> <span class="c1">// 内层作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用全局变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 局部重新定义,覆盖全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用局部变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 显式访问全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">::</span><span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="复合类型"> + <a href="#%e5%a4%8d%e5%90%88%e7%b1%bb%e5%9e%8b">#</a> + 复合类型 +</h2><p><strong>复合类型(compound type)</strong> 是指基于其他类型定义的类型,主要介绍引用和指针。</p> +<h3 id="引用"> + <a href="#%e5%bc%95%e7%94%a8">#</a> + 引用 +</h3><p><strong>引用(reference)</strong> 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 <strong>绑定(bind)</strong> 在一起,而不是将初始者拷贝给引用。</p> +<blockquote> +<p>引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。</p> +</blockquote> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 引用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 修改 y 的值,实际是修改 x 的值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针"> + <a href="#%e6%8c%87%e9%92%88">#</a> + 指针 +</h3><p><strong>指针(pointer)</strong> 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:</p> +<ul> +<li>指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。</li> +<li>指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ip1</span><span class="p">,</span> <span class="o">*</span><span class="n">ip2</span><span class="p">;</span> <span class="c1">// ip1 和 ip2 都是指向 int 类型对象的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">dp</span><span class="p">,</span> <span class="o">*</span><span class="n">dp2</span><span class="p">;</span> <span class="c1">// dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>指针存放某个对象的地址,想获取该地址,需要使用 <strong>取地址符(<code>&amp;</code>)</strong>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。</p> +</blockquote> +<p>指针的值(即地址)应属于下列 4 种状态之一:</p> +<ul> +<li>指向一个对象。</li> +<li>指向紧邻对象所占空间的下一个位置。</li> +<li>空指针,意味着指针没有指向任何对象。</li> +<li>无效指针,也就是上述情况之外的其他值。</li> +</ul> +<p>如果指针指向了一个对象,则允许使用 <strong>解引用符(<code>*</code>)</strong> 来访问该对象:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">p</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 由符号 * 得到指针 p 所指的对象,输出 42 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>解引用操作仅适用于有效指针,无效指针无法解引用。</p> +</blockquote> +<p><strong>空指针(null pointer)</strong> 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。</p> +<p>生成空指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> <span class="c1">// 等价于 int *p1 = 0 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。</p> +<p>指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi 为空指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">;</span> <span class="c1">// pi2 存放 i 的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi3</span><span class="p">;</span> <span class="c1">// pi3 的值无法确定 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="n">pi3</span> <span class="o">=</span> <span class="n">pi2</span><span class="p">;</span> <span class="c1">// pi3 和 pi2 指向同一个对象 i +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi2 变为空指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="const-限定符"> + <a href="#const-%e9%99%90%e5%ae%9a%e7%ac%a6">#</a> + const 限定符 +</h2><p>有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字<code>const</code>对变量的类型加以限定:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> <span class="c1">// 限定缓冲区大小为 512 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>默认情况下,<code>const</code>对象被设定为仅在单个文件内有效。当多个文件中出现了同名的<code>const</code>变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上<code>extern</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 定义文件 xxx.cpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 声明文件 xxx.hpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针和-const"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c-const">#</a> + 指针和 const +</h3><p>与引用一样,也可以令指针指向常量与非常量。<strong>指向常量的指针(pointer to const)</strong> 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// val 为常量对像,其值不能改变 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 报错,pi 为普通指针,不能存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 使用指向常量的指针存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// 报错,不能给指向常量的指针赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="处理类型"> + <a href="#%e5%a4%84%e7%90%86%e7%b1%bb%e5%9e%8b">#</a> + 处理类型 +</h2><p>随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。</p> +<h3 id="类型别名"> + <a href="#%e7%b1%bb%e5%9e%8b%e5%88%ab%e5%90%8d">#</a> + 类型别名 +</h3><p><strong>类型别名(type alias)</strong> 是一个名字,它是某种类型的同义词。</p> +<p>使用关键字<code>typedef</code>定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用 <strong>别名声明(alias declaration)</strong> 定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="auto-类型说明符"> + <a href="#auto-%e7%b1%bb%e5%9e%8b%e8%af%b4%e6%98%8e%e7%ac%a6">#</a> + auto 类型说明符 +</h3><p>编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此<code>C++11</code>引入了<code>auto</code>类型说明符,它能自动分析表达式结果的类型。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">item</span> <span class="o">=</span> <span class="n">val1</span> <span class="o">+</span> <span class="n">val2</span><span class="p">;</span> <span class="c1">// item 初始化为 val1 和 val2 相加的结果 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>auto</code>定义或声明多个变量时,所有变量的类型必须一致:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mf">4.2</span><span class="p">;</span> <span class="c1">// 报错,同一行的变量类型必须一致 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="decltype-类型指示符"> + <a href="#decltype-%e7%b1%bb%e5%9e%8b%e6%8c%87%e7%a4%ba%e7%ac%a6">#</a> + decltype 类型指示符 +</h3><p>有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。</p> +<p>因此<code>C++11</code>引入了<code>decltype</code>类型指示符,它的作用是返回操作数的数据类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">decltype</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// z 为 int 类型 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="自定义数据结构"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84">#</a> + 自定义数据结构 +</h2><p>内置的数据类型并不能满足所有的需求,因此<code>c++</code>提供了自定义数据类型的方式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">jack</span><span class="p">{</span><span class="s">&#34;jack&#34;</span><span class="p">,</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 先声明后赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">castor</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="n">castor</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;castor&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">sex</span> <span class="o">=</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">gender</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义数据结构使用别名"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%bd%bf%e7%94%a8%e5%88%ab%e5%90%8d">#</a> + 自定义数据结构使用别名 +</h3><p>和内置数据类型一样,自定义数据结构也能使用别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="n">studeng</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 或直接在定义时使用别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编写自己的头文件"> + <a href="#%e7%bc%96%e5%86%99%e8%87%aa%e5%b7%b1%e7%9a%84%e5%a4%b4%e6%96%87%e4%bb%b6">#</a> + 编写自己的头文件 +</h3><p>当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// studeng.h +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp">#define STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;string&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="cp">#endif +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/c++-primer/page/1/index.html b/tags/c++-primer/page/1/index.html new file mode 100644 index 0000000..2da727b --- /dev/null +++ b/tags/c++-primer/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/c++-primer/ + + + + + + diff --git a/tags/camera/index.html b/tags/camera/index.html new file mode 100644 index 0000000..9e63862 --- /dev/null +++ b/tags/camera/index.html @@ -0,0 +1,536 @@ + + + + +Tag: Camera - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Camera

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/camera/index.xml b/tags/camera/index.xml new file mode 100644 index 0000000..cf26231 --- /dev/null +++ b/tags/camera/index.xml @@ -0,0 +1,712 @@ + + + + Camera on 3000ye's Blog + https://3000ye.com/tags/camera/ + Recent content in Camera on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 08 Oct 2023 14:30:49 +0000 + Camera Calibration + https://3000ye.com/p/camera-calibration/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/camera-calibration/ + <img src="https://3000ye.com/p/camera-calibration/board.jpg" alt="Featured image of post Camera Calibration" /><h1 id="由浅到深理解相机标定"> + <a href="#%e7%94%b1%e6%b5%85%e5%88%b0%e6%b7%b1%e7%90%86%e8%a7%a3%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 由浅到深理解相机标定 +</h1><h2 id="何为相机标定"> + <a href="#%e4%bd%95%e4%b8%ba%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a">#</a> + 何为相机标定 +</h2><p>在图像测量过程以及机器视觉应用中,为确定空间无哦表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。</p> +<p>在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为<strong>相机标定(或摄像头标定)</strong>。</p> +<p><strong>相机标定</strong>涉及的知识面很广:成像几何、镜头畸变、单应矩阵、非线性优化等。</p> +<p><strong>相机标定</strong>有自标定(找图像中特征点)、标定板标定(特征点易求,稳定性好),一般采用标定板标定。</p> +<p><strong>相机标定</strong>按照相机是否静止,可分为静态相机标定(标定板动,相机静止),动态相机标定(标定板静止,相机运动)。</p> +<h3 id="为什么需要标定"> + <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e6%a0%87%e5%ae%9a">#</a> + 为什么需要标定 +</h3><p>任何理论物理模型都是在特定假设上对真实事物的近似,然而在实际应用中存在误差,普通相机的成像模型也不例外(透视投影)。</p> +<p>实际中,普通相机成像误差的主要来源有两部分:</p> +<ul> +<li>第一是sensor(传感器)制造产生的误差,比如sensor成像单元不是正方形,sensor歪斜。</li> +<li>第二是镜头制造和安装产生的误差,镜头一般存在非线性的径向畸变;镜头与相机sensor安装不平行,还会产生切向畸变。</li> +</ul> +<h2 id="相机标定的目的和意义"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e7%9a%84%e7%9b%ae%e7%9a%84%e5%92%8c%e6%84%8f%e4%b9%89">#</a> + 相机标定的目的和意义 +</h2><p>我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。</p> +<p>相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w.webp" + width="898" + height="615" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_480x0_resize_q75_h2_box_2.webp 480w, https://3000ye.com/p/camera-calibration/assets/v2-afff3b4901966569a5203751afb5e50f_1440w_hu494e263fab3b797d2a93ce028aa90121_24808_1024x0_resize_q75_h2_box_2.webp 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="146" + data-flex-basis="350px" + +></p> +<p>这个逼近的过程就是「相机标定」,我们用简单的数学模型来表达复杂的成像过程,并且求出成像的反过程。标定之后的相机,可以进行三维场景的重建,即深度的感知。</p> +<h2 id="相关术语"> + <a href="#%e7%9b%b8%e5%85%b3%e6%9c%af%e8%af%ad">#</a> + 相关术语 +</h2><p><strong>焦点:<strong>在几何光学中有时也称为</strong>像点</strong>,是源头的光线经过物镜后汇聚的点。</p> +<p><strong>焦距:<strong>也称为</strong>焦长</strong>,是光学系统中衡量光的聚集或发散的度量方式,指从透镜中心到光聚集之焦点的距离。亦是照相机中,从镜片光学中心到底片、CCD或CMOS等成像平面的距离。</p> +<p>正透镜、负透镜、凹面镜和凸面镜的焦点<code>F</code>和焦距<code>f</code>:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/1.png" alt="img" style="zoom:50%;" /> +</div> +<p><strong>镜头(Lenses)</strong>:是将拍摄景物在传感器上成像的器件,它通常由几片透镜、光圈叶片、对焦马达等光学元件组成。</p> +<p><strong>传感器(Sensor)</strong>:是摄像头组成的核心,其作用是作为相机的感光元件。摄像头传感器主要有两种,一种是CCD传感器,一种是CMOS传感器,两者区别在于:CCD的优势在于成像质量好,但是由于制造工艺复杂,成本居高不下,特别是大型CCD,价格非常高昂。在相同分辨率下,CMOS价格比CCD便宜,但是CMOS器件产生的图像质量相比CCD来说要低一些。</p> +<p><strong>光心</strong>:凸透镜近轴光线中,入射线和与其对应且相平行的出射线构成共轭光线,其入射点跟出射点的连线与主光轴的交点,称为凸透镜的焦点,位于透镜中央的点叫光心。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/3.png" alt="img" style="zoom:50%;" /> +</div> +<p>从图中可知,<code>O</code>为光心,<code>F</code>为焦点。每个透镜主轴上都有一个特殊点,凡是通过该点的光,其传播方向不变,这个点叫光心。经过光心的光线的传播方向不会发生改变。</p> +<h2 id="相机标定原理模型"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e5%8e%9f%e7%90%86%e6%a8%a1%e5%9e%8b">#</a> + 相机标定原理模型 +</h2><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定.drawio" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<h3 id="针孔相机模型"> + <a href="#%e9%92%88%e5%ad%94%e7%9b%b8%e6%9c%ba%e6%a8%a1%e5%9e%8b">#</a> + 针孔相机模型 +</h3><p>我们通常将相机看成如下所示的透镜模型:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/2.png" alt="img" style="zoom:50%;" /> +</div> +<p>在实际分析时,通常将其简化为针孔模型(小孔成像):</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/4.png" alt="img" style="zoom:50%;" /> +</div> +<p>一般为了分析简单,将成像平面画在对称位置,这样图像不再颠倒:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/5.png" alt="img" style="zoom:50%;" /> +</div> +<h3 id="四个坐标系"> + <a href="#%e5%9b%9b%e4%b8%aa%e5%9d%90%e6%a0%87%e7%b3%bb">#</a> + 四个坐标系 +</h3><p><strong>世界坐标系</strong>:用户定义的三维世界的坐标系,用于描述目标物体在真实世界里的位置。单位通常为米(m)。该坐标系作用于三维空间。</p> +<p><strong>相机坐标系</strong>:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位通常为米(m)。相机坐标系的原点在光心,其 $X_c、Y_c$ 轴分别与像面的两边平行,其 $Z_c$ 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点(实际情况中可能存在<strong>主点偏移</strong>),相机坐标系与图像坐标系之间的距离为焦距 $f$。该坐标系作用于三维空间。</p> +<p><strong>图像坐标系</strong>:为了描述成像过程中物体从相机坐标系到图像坐标系的投影投射关系而引入,方便进一步得到像素坐标系下的坐标。其原点是相机光轴与像面的交点(称为主点),即<strong>图像的中心点</strong>。其 $x, y$ 轴和像素坐标系的 $u, v$ 轴平行,故图像坐标系和像素坐标系实际是平移关系。单位通常为毫米(mm)。该坐标系作用于二维空间。</p> +<p><strong>像素坐标系</strong>:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为像素。像素坐标平面和图像坐标系平面重合,但像素坐标系原点位于图像左上角。该坐标系作用于二维空间。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/6.png" + width="1248" + height="841" + srcset="https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/6_hu50e7c67d19d22e879bfaa71a393e05b1_178123_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="148" + data-flex-basis="356px" + +></p> +<h3 id="相机外参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%a4%96%e5%8f%82">#</a> + 相机外参 +</h3><p>将世界坐标系中的点映射到相机坐标系:相机坐标系是世界坐标系通过<strong>刚体变换</strong>得到的。</p> +<blockquote> +<p>刚体变换能够保持物体中各点的距离和角度,常见的刚体变换有:平移、旋转和镜像。</p> +</blockquote> +<p>我们先只考虑旋转,假设将坐标系以 $X$ 轴为中心进行旋转,即 $X$ 不变,旋转 $Y - Z$ 平面。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/7.png" alt="img" style="zoom: 33%;" /> +</div> +<p>假设旋转角度为 $\theta$,即 $\angle Y&rsquo; O Y = \angle Z&rsquo; O Z = \theta$。旋转前的坐标系为 $X - Y - Z$,旋转后的坐标系为 $X&rsquo; - Y&rsquo; - Z&rsquo;$。假设点 $P$ 在 $X - Y - Z$ 中的坐标为($X_w, Y_w, Z_w$),旋转后,其在 $X&rsquo; - Y&rsquo; - Z&rsquo;$ 中的坐标为($X_c, Y_c, Z_c$): +$$ +X_C = X_w +$$</p> +<p>$$ +\begin{array}{l} +Y_c &amp; = OC + CD = OA \cdot \sin \theta + BP \\ +&amp; = Z_w \cdot \sin \theta + AP \cdot \cos \theta \\ +&amp; = Z_w \sin \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>$$ +\begin{array}{l} +Z_c &amp; = PD = AC - AB \\ +&amp; = AO \cdot \cos \theta - AP \cdot \cos \theta \\ +&amp; = Z_w \cos \theta + Y_w \cos \theta +\end{array} +$$</p> +<p>写成矩阵形式: +$$\displaystyle \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} = \mathbf{R_{cw}} \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} or \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix} = \mathbf{R_{wc}} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} $$ +推广到每个方向,可得到 $\mathbf{R_{cw}}, \mathbf{R_{wc}}$ 为: +$$ +\mathbf{R_{cw}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; \sin \theta \\ +0 &amp; - \sin \theta &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (X_A, \theta) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta &amp; - \sin \theta \\ +0 &amp; \sin \theta &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; \sin \theta \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +, +\mathbf{R_{wc}} (Y_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; 0 &amp; - \sin \theta \\ +0 &amp; 1 &amp; 0 \\ +\sin \theta &amp; 0 &amp; \cos \theta +\end{bmatrix} +$$ +$$ +\mathbf{R_{cw}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; \sin \theta &amp; 0 \\ - \sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +, +\mathbf{R_{wc}} (Z_A, \theta) = +\begin{bmatrix} +\cos \theta &amp; - \sin \theta &amp; 0 \\ +\sin \theta &amp; \cos \theta &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +$$</p> +<p>这里我们使用右手笛卡尔三维坐标系:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/14.png" alt="img" style="zoom: 25%;" /> +</div> +<p>旋转可分为<strong>主动旋转</strong>与<strong>被动旋转</strong>。<strong>主动旋转</strong>是指将向量逆时针围绕旋转轴所做出的旋转。<strong>被动旋转</strong>是对坐标轴本身进行的逆时针旋转,它相当于主动旋转的逆操作。关于右手笛卡尔坐标系的 $X, Y, Z$ 轴的旋转分别叫做<code>roll</code>,<code>pitch</code>和<code>yaw</code>旋转:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/15.png" alt="img" style="zoom: 20%;" /> +</div> +<p>因为逆时针和顺时针旋转会得到不一样的旋转矩阵,所以我们统一如下:</p> +<p>绕 $X$ 轴的主动旋转定义为($\theta_x$ 是<code>roll</code>角 ): +$$ +R(X_A, \theta_x) = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \theta_x &amp; - \sin \theta_x \\ +0 &amp; \sin \theta_x &amp; \cos \theta_x +\end{bmatrix} = +\exp \left ( \theta_x +\begin{bmatrix} +0 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; -1\\ +0 &amp; 1 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Y$ 轴的主动旋转定义为($\theta_y$ 是<code>pitch</code>角): +$$ +R(Y_A, \theta_y) = +\begin{bmatrix} +\cos \theta_y &amp; 0 &amp; \sin \theta_y \\ +0 &amp; 1 &amp; 0 \\ - \sin \theta_y &amp; 0 &amp; \cos \theta_y +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; 0 &amp; 1\\ +0 &amp; 0 &amp; 0\\ -1 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +绕 $Z$ 轴的主动旋转定义为($\theta_z$ 是<code>yaw</code>角): +$$ +R(Z_A, \theta_z) = +\begin{bmatrix} +\cos \theta_z &amp; - \sin \theta_z &amp; 0 \\ +\sin \theta_z &amp; \cos \theta_z &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} = +\exp \left ( \theta_y +\begin{bmatrix} +0 &amp; -1 &amp; 0\\ +1 &amp; 0 &amp; 0\\ +0 &amp; 0 &amp; 0 +\end{bmatrix} +\right ) +$$ +将上述三个旋转矩阵结合起来,最终的旋转矩阵(设绕 $X, Y, Z$ 轴旋转的角度分别为 $\alpha, \beta, \gamma$): +$$ +\begin{array}{ll} +M(\alpha, \beta, \gamma) &amp; = R_x(\alpha) R_y(\beta) R_z(\gamma) \\ +&amp; = +\begin{bmatrix} +1 &amp; 0 &amp; 0 \\ +0 &amp; \cos \alpha &amp; - \sin \alpha \\ +0 &amp; \sin \alpha &amp; \cos \alpha +\end{bmatrix} +\begin{bmatrix} +\cos \beta &amp; 0 &amp; \sin \beta \\ +0 &amp; 1 &amp; 0 \\ - \sin \beta &amp; 0 &amp; \cos \beta +\end{bmatrix} +\begin{bmatrix} +\cos \gamma &amp; -\sin \gamma &amp; 0 \\ +\sin \gamma &amp; \cos \gamma &amp; 0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} \\ +&amp; = \begin{bmatrix} +\cos \gamma \cos \beta &amp; - \sin \gamma \cos \alpha + \cos \gamma \sin \beta \sin \alpha &amp; \sin \gamma \sin \alpha + \cos \gamma \sin \beta \cos \alpha \\ +\sin \gamma \cos \beta &amp; \cos \gamma \cos \alpha + \sin \gamma \sin \beta \sin \alpha &amp; - \cos \gamma \sin \alpha + \sin \gamma \sin \beta \cos \alpha \\ - \sin \beta &amp; \cos \beta \sin \alpha &amp; \cos \beta \cos \alpha +\end{bmatrix} +\end{array} +$$</p> +<p>此时我们再加上平移向量 $T$ 便可完成从世界坐标系到相机坐标系的这个刚体变换了:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c +\end{bmatrix} = +\begin{bmatrix} +r_{11} &amp; r_{12} &amp; r_{13} \\ +r_{21} &amp; r_{22} &amp; r_{23} \\ +r_{31} &amp; r_{32} &amp; r_{33} +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + +\begin{bmatrix} +t_x \\ +t_y \\ +t_z +\end{bmatrix} = +\mathbf{R} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w +\end{bmatrix} + T +$$</p> +<p>可进一步写成如下形式:</p> +<p>$$ +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0_3^T &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$\mathbf{R}$ 和 $\mathbf{T}$ 便是相机外参。</p> +<h3 id="相机内参"> + <a href="#%e7%9b%b8%e6%9c%ba%e5%86%85%e5%8f%82">#</a> + 相机内参 +</h3><p>首先考虑图像坐标系($xy$)和像素坐标系($uv$)之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/8.png" + width="369" + height="245" + srcset="https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/8_hu3053578c3bc439278c7e5faeaf7da032_3687_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="150" + data-flex-basis="361px" + +></p> +<p>$$ +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix}= +\begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix} +$$</p> +<p>$dx$ 表示一个像素点在 $x$ 方向的长度是多少毫米,$dy$ 表示一个像素点在 $y$ 方向的长度是多少毫米;$(u_0, v_0)$ 为图像的中心点。</p> +<p>然后考虑相机坐标系和图像坐标系之间的转换:</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/9.png" + width="353" + height="346" + srcset="https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/9_hu7dba7a60413e6fda49ead6d74037b32e_12375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="102" + data-flex-basis="244px" + +></p> +<p>$$ +\Delta ABO_c \sim \Delta oCO_c, \Delta PBO_c \sim \Delta pCO_c +$$ +$$ +\displaystyle \frac{AB}{oC} = \frac{AO_c}{oO_c} = \frac{PB}{p C} = \frac{X_c}{x} = \frac{Z_c}{f} = \frac{Y_c}{y} +$$ +$$ +x = f \displaystyle \frac{X_c}{Z_c}, y = f \frac{Y_c}{Z_c} +$$</p> +<p>$$ +Z_c \begin{bmatrix} +x \\ +y \\ +1 +\end{bmatrix}= +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix}= +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>其中,$f$ 是焦距,结合外参我们最终可以得到世界坐标系和像素坐标系之间的映射关系:</p> +<p>$$ +\begin{array}{l} +\lambda \begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} &amp; = +\begin{bmatrix} +\displaystyle \frac{1}{dx} &amp; 0 &amp; u_0 \\ +0 &amp; \displaystyle \frac{1}{dy} &amp; v_0 \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +f &amp; 0 &amp; 0 &amp; 0 \\ +0 &amp; f &amp; 0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix}\\ +&amp; = +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +\mathbf{R} &amp; \mathbf{T} \\ +0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X_w \\ +Y_w \\ +Z_w \\ +1 +\end{bmatrix} +\end{array} +$$ +其中,相机内参为(不考虑图像传感器的特性): +$$ +\begin{bmatrix} +fx &amp; 0 &amp; u_0 &amp; 0 \\ +0 &amp; fy &amp; v_0 &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +$$</p> +<p>其中,$f_x, f_y$ 即为焦距的物理距离在像素坐标系中的长度,相机内参标定主要是标定相机的焦距、主点、歪斜等内部参数。</p> +<h2 id="可能存在的影响"> + <a href="#%e5%8f%af%e8%83%bd%e5%ad%98%e5%9c%a8%e7%9a%84%e5%bd%b1%e5%93%8d">#</a> + 可能存在的影响 +</h2><h3 id="主点偏移"> + <a href="#%e4%b8%bb%e7%82%b9%e5%81%8f%e7%a7%bb">#</a> + 主点偏移 +</h3><p>主点是光轴和相机成像平面的交点,在理想情况下,图像坐标系和相机坐标系原点重合,不存在坐标系偏移。但在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动:</p> +<div style="display: flex; justify-content: center;"> + <img src="assets/v2-d05b86c51be4aec5c412e2ca74afaf22_1440w.png" alt="img" style="zoom: 67%;" /> +</div> +<p>考虑主点偏移后,图像坐标和3D在相机坐标系的关系为:</p> +<p>$$ +\begin{matrix} +u = f \frac{X}{Z} + O_x \\ +v = f \frac{X}{Z} + O_y +\end{matrix} +$$ +此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; 0 &amp; O_x &amp; 0 \\ +0 &amp; f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} +$$</p> +<p>仔细观察就会发现,该关系与上面提到的关系是等价的,只不过上面使用坐标 $(u_0, v_0)$ 来代表偏移量 $(O_x, O_y)$。</p> +<h3 id="图像传感器特征"> + <a href="#%e5%9b%be%e5%83%8f%e4%bc%a0%e6%84%9f%e5%99%a8%e7%89%b9%e5%be%81">#</a> + 图像传感器特征 +</h3><p>图像传感器像原尺寸在制造过程可能不是正方形,同时可能存在歪斜(skewed),因此需要考虑这些影响因素,传感器歪斜和不是正方形主要对相机 $x$ 和 $y$ 方向的焦距产生影响。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/v2-a812da34b739588fa9142c46839ad281_1440w.png" alt="img" style="zoom: 50%;" /> +</div> +<p>此时,透视投影模型(像素坐标系和相机坐标系)的关系为: +$$ +\lambda +\begin{bmatrix} +u \\ +v \\ +1 +\end{bmatrix} = +\begin{bmatrix} +f &amp; s &amp; O_x &amp; 0 \\ +0 &amp; \eta f &amp; O_x &amp; 0 \\ +0 &amp; 0 &amp; 1 &amp; 0 +\end{bmatrix} +\begin{bmatrix} +X_c \\ +Y_c \\ +Z_c \\ +1 +\end{bmatrix} = [K, 0_3] P +$$ +其中,$K$ 矩阵即为最终的内参矩阵。</p> +<h3 id="镜头畸变"> + <a href="#%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 镜头畸变 +</h3><p>小孔成像模型虽然充分考虑了相机内部参数对成像的影响,但没有考虑成像系统另一个重要的部分,镜头。镜头常用的有普通镜头、广角镜头、鱼眼镜头等,在无人驾驶和视觉slam领域,鱼眼镜头和广角镜头用的很多,主要是视角很大,可以观测到更多的信息。任何镜头都存在不同程度的畸变,不同类型的镜头用到的畸变模型也不相同。</p> +<p>在几何光学和阴极射线管(CRT)显示中,畸变(distortion)是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括<strong>径向畸变(radial distortion)<strong>和</strong>切向畸变(tangential distortion)</strong>。</p> +<p>径向畸变来自于透镜形状,主要是由于透镜不同部位放大倍率不同造成的。切向畸变来自于整个相机的组装过程,主要是由于透镜安装与成像平面不平行造成的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/10.png" + width="456" + height="300" + srcset="https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/10_hu520d01a4277650b3ace23061f7842e97_16805_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="152" + data-flex-basis="364px" + +></p> +<h4 id="径向畸变"> + <a href="#%e5%be%84%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 径向畸变 +</h4><p>透过镜头边缘的光线很容易产生径向畸变,这种现象来源于“筒形”或“鱼眼”的影响。光线离镜头中心越远,畸变越大。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w.png" + width="765" + height="248" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-d76fefc82f8dfa9361518b34d4e0e911_1440w_hud968eea2866ecad196275151a52e05a5_25077_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="308" + data-flex-basis="740px" + +></p> +<p>从图像可以看出,径向畸变以某一个中心往外延伸,且越往外,畸变越大;显然畸变与距离成一种非线性的变换关系,参考众多文献,可以用多项式来近似: +$$ +\begin{matrix} +x_{rcrt} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\\\ +y_{rcrt} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$k_1, k_2, k_3$ 是径向畸变系数,$r^2 = x^2 + y^2$。</p> +<h4 id="切向畸变"> + <a href="#%e5%88%87%e5%90%91%e7%95%b8%e5%8f%98">#</a> + 切向畸变 +</h4><p>切向畸变主要发生在相机sensor和镜头不平行的情况下;因为有夹角,所以光透过镜头传到图像传感器上时,成像位置发生了变化。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/11.png" + width="926" + height="385" + srcset="https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/11_hu67182665c8f044b7049524c5ee04a0b9_14335_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="240" + data-flex-basis="577px" + +> +$$ +\begin{matrix} +x_{tcrt} = x + [2p_1 xy + p_2 (r^2 + 2 x^2)] \\\\ +y_{tcrt} = y + [2p_2 xy + p_1 (r^2 + 2 y^2)] +\end{matrix} +$$ +其中,$x, y$ 是归一化的图像坐标,即坐标原点已经移动到主点,并且像素坐标除以焦距。$p_1, p_2$ 是切向畸变系数,$r^2 = x^2 + y^2$。</p> +<h3 id="消除镜头畸变"> + <a href="#%e6%b6%88%e9%99%a4%e9%95%9c%e5%a4%b4%e7%95%b8%e5%8f%98">#</a> + 消除镜头畸变 +</h3><p>考虑镜头畸变前,我们可以将相机标定简单描述为以下过程:像素坐标 $(u_{ccd}, v_{ccd})$ $\to$ 图像坐标 $(x, y)$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<p>此时我们考虑加入镜头畸变: +$$ +\begin{matrix} +x_{crt} = x_{rcrt} + x_{tcrt} \\\\ +y_{crt} = y_{rcrt} + y_{tcrt} +\end{matrix} +$$ +得到消除镜头畸变的相机标定流程:像素坐标 $(u_{ccd - crt}, v_{ccd - crt})$ $\to$ 图像坐标 $(x_{crt}, y_{crt})$ $\to$ 相机坐标 $(X_c, Y_c, Z_c)$ $\to$ 世界坐标 $(X_w, Y_w, Z_w)$。</p> +<h2 id="标定板的作用"> + <a href="#%e6%a0%87%e5%ae%9a%e6%9d%bf%e7%9a%84%e4%bd%9c%e7%94%a8">#</a> + 标定板的作用 +</h2><h3 id="相机标定中的参数"> + <a href="#%e7%9b%b8%e6%9c%ba%e6%a0%87%e5%ae%9a%e4%b8%ad%e7%9a%84%e5%8f%82%e6%95%b0">#</a> + 相机标定中的参数 +</h3><p><img src="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio.png" + width="1568" + height="716" + srcset="https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/%E7%9B%B8%E6%9C%BA%E6%A0%87%E5%AE%9A.drawio_hu442cf25e47db863ae4d720005871112f_99053_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="相机标定" + + + class="gallery-image" + data-flex-grow="218" + data-flex-basis="525px" + +></p> +<p>针孔相机模型中,只要确定这9个参数就可以唯一的确定针孔相机模型:</p> +<p>$$ +f_x,f_y,O_x,O_y,k_1,k_2,k_3,p_1,p_2 +$$</p> +<p>这个过程就称为「相机标定」,其中前4个我们称为内参数,后5个称为畸变参数,畸变参数是为了补充内参的。所以一旦相机结构固定,包括镜头结构固定,对焦距离固定,我们就可以用这9个的参数去近似这个相机。这里说的「镜头结构固定」,按我个人的理解,除了焦距固定之外,也应当包含光圈固定,因为改变光圈的大小,除了景深之外,是有可能改变针孔相机模型中的光心位置,但是影响并不是很大。这意味着标定好的相机如果改变光圈大小,会使得标定误差变大但应该不会大到难以接受的地步。</p> +<p>对于针孔相机本身需要拟合的方程如下:</p> +<p>$$ +\begin{bmatrix} +u_{ccd - crt} * Z\\ +v_{ccd - crt} * Z\\ +Z +\end{bmatrix} = +J(k_1, k_2, k_3, p_1, p_2) +\begin{bmatrix} +f_x &amp; 0 &amp; O_x \\ +0 &amp; f_y &amp; O_y \\ +0 &amp; 0 &amp; 1 +\end{bmatrix} +\begin{bmatrix} +X \\ +Y \\ +X +\end{bmatrix} +$$</p> +<p>因此,我们现在的任务就是找出一大堆具有对应关系的像点 ${(u_{ccd - crt}, v_{ccd - crt}) ^T }$ 和物点 ${ (X, Y, Z)^T }$ 的点作为样本,来训练出模型的参数。这里就引发了两个问题:</p> +<ul> +<li>这么多像点和物点如何匹配?</li> +<li>即便现在知道物点的位置,如何用相机坐标系来表达物点的位置 $(X, Y, Z)$?</li> +</ul> +<p>为了解决上述问题,标定板应运而生。标定板的一大作用,确定物点和像点的对应性。这里用到的原理主要是「透视不变性」,打个比方,你近看一个人和远看一个人,虽然他的鼻子大小变了,你看鼻子的视角也变了,但是拓扑结构肯定是不变的,你也不可能把鼻子看成是嘴巴。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w.png" + width="803" + height="363" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-c6f1ebb7bfbef57c665546683b283de1_1440w_hu0a6050398035f85c674f0491317f5983_14125_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="221" + data-flex-basis="530px" + +></p> +<p>所以在标定板中,印刷了拓扑结构,广泛应用的是棋盘格和圆点格,这两种之所以成为主流,不仅是因为它们的拓扑结构明确且均匀,更重要的是检测其拓扑结构的算法简单且有效。棋盘格检测的是角点,只要对拍摄到的棋盘格图像横纵两个方向计算梯度就可获得;而圆点格的检测只需要对拍摄到的圆点格图样计算质心即可。假如你开发了一套非常完美的检测人脸全部特征的算法,你完全可以用你的照片当作标定板。</p> +<p>按照我的经验,圆点格的效果应该是好于棋盘格,因为圆点质心的「透视不变性」要比棋盘格的角点稳定的多。下图是同样尺寸、同样比例棋盘格和圆点在最大重投影误差处的误差对比,红色十字是提取的角点/质心,绿色圆圈是针孔相机模型计算出来认为的角点/质心位置。</p> +<p>但是圆点格的检测似乎是Halcon的专利(存疑),因此OpenCV和Matlab标定工具箱用的是棋盘格,要用圆点格得要自己写算法。下文中提到的标定板说的都是棋盘格。</p> +<p>标定板的第二大作用是把标定板中的角点变换到相机坐标系下的坐标 $(X, Y, Z)$。对于标定的初学者来说,很容易忽略的一点是标定板是具有标定板坐标系的。换句话说,标定板中的每个角点,在标定板坐标系下的位置是确定并且是已知的。</p> +<p><img src="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w.png" + width="447" + height="404" + srcset="https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_480x0_resize_box_3.png 480w, https://3000ye.com/p/camera-calibration/assets/v2-67b8737534163cb960564df54e4361cb_1440w_hu8ebb7eb1d15f75db39d4e9022464a37f_9666_1024x0_resize_box_3.png 1024w" + loading="lazy" + + alt="img" + + + class="gallery-image" + data-flex-grow="110" + data-flex-basis="265px" + +></p> +<p>而标定板坐标系变换到相机坐标系的变换矩阵,我们称它的元素为外参数。</p> +<h3 id="如何使用标定板"> + <a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%a0%87%e5%ae%9a%e6%9d%bf">#</a> + 如何使用标定板 +</h3><p>如果用OpenCV或Matlab标定工具箱进行标定,需要给出棋盘格的物理尺寸,这其实就是在建立标定板坐标系,从测量的角度讲,标定板的精度是相机标定精度的基准,是误差传递链上的第一个环节。所以为了使针孔相机模型更逼近真实相机,对标定板的质量有以下要求(按重要性顺序):</p> +<ul> +<li>标定板的平面度高,棋盘格是直角。</li> +<li>标定板每个格子尺寸的高一致性。</li> +<li>真实尺寸与标称尺寸的差异小。</li> +</ul> + + + + + diff --git a/tags/camera/page/1/index.html b/tags/camera/page/1/index.html new file mode 100644 index 0000000..8dda877 --- /dev/null +++ b/tags/camera/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/camera/ + + + + + + diff --git a/tags/cpp/index.html b/tags/cpp/index.html new file mode 100644 index 0000000..c30a9e1 --- /dev/null +++ b/tags/cpp/index.html @@ -0,0 +1,662 @@ + + + + +Tag: CPP - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

11 pages

+

CPP

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/cpp/index.xml b/tags/cpp/index.xml new file mode 100644 index 0000000..40ed542 --- /dev/null +++ b/tags/cpp/index.xml @@ -0,0 +1,2695 @@ + + + + CPP on 3000ye's Blog + https://3000ye.com/tags/cpp/ + Recent content in CPP on 3000ye's Blog + Hugo -- gohugo.io + en-us + Mon, 18 Mar 2024 21:06:19 +0800 + Threads + https://3000ye.com/p/threads/ + Mon, 18 Mar 2024 21:06:19 +0800 + + https://3000ye.com/p/threads/ + <h1 id="线程管理"> + <a href="#%e7%ba%bf%e7%a8%8b%e7%ae%a1%e7%90%86">#</a> + 线程管理 +</h1><p>在程序开发中,很多时候都是程序都是串行处理,这没有什么问题。然而,在某些重复工作较多,且性能要求较高的场景,串行处理所需时间往往过于漫长。</p> +<p>因此,合理地使用线程管理有助于我们程序的更好运行。但是请注意,不是一味地使用多线程或线程池就一定是好的,适合运行场景的处理方式才是最好的。</p> +<h2 id="单线程"> + <a href="#%e5%8d%95%e7%ba%bf%e7%a8%8b">#</a> + 单线程 +</h2><p>在本文中,我们考虑这样一个场景:有一个非常耗时的计算函数,其计算一次需要 <code>time</code> 秒。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">waitTime</span><span class="p">(</span><span class="kt">int</span> <span class="n">time</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">seconds</span> <span class="n">duration</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">duration</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;wait for &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">time</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; seconds&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>按照常规的做法,我们串行地对批量任务进行处理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>可以预见,这种处理方法会非常耗时。</p> +<h2 id="多线程"> + <a href="#%e5%a4%9a%e7%ba%bf%e7%a8%8b">#</a> + 多线程 +</h2><p>为了加速程序运行和处理的速度,我们可以使用多线程来并行处理。多线程的思想是:先将要进行的任务放入队列中,然后让这些任务同时运行,最终实现加速程序运行的效果。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">mulTreads</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">,</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;&gt;</span> <span class="n">fetchingThreads</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">l</span> <span class="o">=</span> <span class="n">todoList</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">l</span><span class="p">;</span> <span class="n">i</span> <span class="o">++</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span><span class="p">(</span><span class="n">waitTime</span><span class="p">,</span> <span class="n">todoList</span><span class="p">[</span><span class="n">i</span><span class="p">]));</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="n">MaxThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">front</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">fetchingThreads</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">fetchingThreads</span><span class="p">.</span><span class="n">begin</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">threadPtr</span> <span class="p">:</span> <span class="n">fetchingThreads</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">threadPtr</span><span class="o">-&gt;</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">mulTreads</span><span class="p">(</span><span class="n">todoList</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="线程池"> + <a href="#%e7%ba%bf%e7%a8%8b%e6%b1%a0">#</a> + 线程池 +</h2><p>多线程虽好,但是频繁地创建和删除线程,同样会造成时间和空间的浪费。因此,线程池出现了,在每次任务完成之后,保留现有线程并继续处理下一个任务。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span><span class="lnt">46 +</span><span class="lnt">47 +</span><span class="lnt">48 +</span><span class="lnt">49 +</span><span class="lnt">50 +</span><span class="lnt">51 +</span><span class="lnt">52 +</span><span class="lnt">53 +</span><span class="lnt">54 +</span><span class="lnt">55 +</span><span class="lnt">56 +</span><span class="lnt">57 +</span><span class="lnt">58 +</span><span class="lnt">59 +</span><span class="lnt">60 +</span><span class="lnt">61 +</span><span class="lnt">62 +</span><span class="lnt">63 +</span><span class="lnt">64 +</span><span class="lnt">65 +</span><span class="lnt">66 +</span><span class="lnt">67 +</span><span class="lnt">68 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ThreadPool</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span><span class="p">(</span><span class="n">size_t</span> <span class="n">numThreads</span><span class="p">)</span> <span class="o">:</span> <span class="n">stop</span><span class="p">(</span><span class="nb">false</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numThreads</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">workers</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">([</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;</span> <span class="n">task</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">wait</span><span class="p">(</span><span class="n">lock</span><span class="p">,</span> <span class="p">[</span><span class="k">this</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stop</span> <span class="o">||</span> <span class="o">!</span><span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">();</span> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">stop</span> <span class="o">&amp;&amp;</span> <span class="n">tasks</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">task</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">tasks</span><span class="p">.</span><span class="n">front</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">task</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 添加任务到线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">F</span><span class="o">&gt;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="n">enqueue</span><span class="p">(</span><span class="n">F</span><span class="o">&amp;&amp;</span> <span class="n">task</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">tasks</span><span class="p">.</span><span class="n">emplace</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">F</span><span class="o">&gt;</span><span class="p">(</span><span class="n">task</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_one</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="o">~</span><span class="n">ThreadPool</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unique_lock</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">queueMutex</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">stop</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="n">condition</span><span class="p">.</span><span class="n">notify_all</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span> <span class="o">&amp;</span><span class="nl">worker</span> <span class="p">:</span> <span class="n">workers</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="n">worker</span><span class="p">.</span><span class="n">join</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="o">&gt;</span> <span class="n">workers</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;&gt;</span> <span class="n">tasks</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">mutex</span> <span class="n">queueMutex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">condition_variable</span> <span class="n">condition</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">stop</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">int</span> <span class="n">numThreads</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">ThreadPool</span> <span class="n">pool</span><span class="p">(</span><span class="n">numThreads</span><span class="p">);</span> <span class="c1">// 创建包含7个线程的线程池 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">todoList</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 向线程池中添加任务 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="nl">time</span> <span class="p">:</span> <span class="n">todoList</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">pool</span><span class="p">.</span><span class="n">enqueue</span><span class="p">([</span><span class="n">time</span><span class="p">]</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">waitTime</span><span class="p">(</span><span class="n">time</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="p">});</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Linked List + https://3000ye.com/p/linked-list/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/p/linked-list/ + <img src="https://3000ye.com/p/linked-list/assets/dataStructures.jpg" alt="Featured image of post Linked List" /><h1 id="数据结构链表"> + <a href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e9%93%be%e8%a1%a8">#</a> + 数据结构:链表 +</h1><h2 id="线性表"> + <a href="#%e7%ba%bf%e6%80%a7%e8%a1%a8">#</a> + 线性表 +</h2><p>线性表是具有<strong>相同</strong>数据类型的 $n(n \ge 0)$ 个<strong>数据元素</strong>的<strong>有限序列</strong>,其中 $n$ 为<strong>表长</strong>,当 $n = 0$ 时线性表是一个<strong>空表</strong>。若用 $L$ 命名线性表,则其一般表示为:</p> +<p>$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$</p> +<p>几个概念:</p> +<ul> +<li>$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的<strong>位序</strong>,位序从 $1$ 开始,数组下标从 $0$ 开始。</li> +<li>$a_1$ 是<strong>表头元素</strong>,$a_n$ 是<strong>表尾元素</strong>。</li> +</ul> +<p>除第一个元素外,每个元素有且仅有一个<strong>直接前驱</strong>;除最后一个元素外,每个元素有且仅有一个<strong>直接后继</strong>。</p> +<p>线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。</p> +<h2 id="链表"> + <a href="#%e9%93%be%e8%a1%a8">#</a> + 链表 +</h2><p>链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。</p> +<p>这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。</p> +<p>链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。</p> +<h3 id="单向链表"> + <a href="#%e5%8d%95%e5%90%91%e9%93%be%e8%a1%a8">#</a> + 单向链表 +</h3><p>单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。</p> +<div style='display: flex; justify-content: center;'> +<img src='https://oi-wiki.org/ds/images/list.svg' alt='img' style='zoom:100%;' /> +</div> + + + + CppJson Ch02 + https://3000ye.com/p/cppjson-ch02/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/p/cppjson-ch02/ + <img src="https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch02" /><h1 id="cppjson-第二章节number-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%ba%8c%e7%ab%a0%e8%8a%82number-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第二章节:<code>number</code> 值解析 +</h1> + + + CppJson Ch01 + https://3000ye.com/p/cppjson-ch01/ + Sat, 16 Dec 2023 23:33:44 +0800 + + https://3000ye.com/p/cppjson-ch01/ + <img src="https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch01" /><h1 id="cppjson-第一章节自动测试null-和-bool-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%b8%80%e7%ab%a0%e8%8a%82%e8%87%aa%e5%8a%a8%e6%b5%8b%e8%af%95null-%e5%92%8c-bool-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第一章节:自动测试,<code>NULL</code> 和 <code>bool</code> 值解析 +</h1><h2 id="json-是什么"> + <a href="#json-%e6%98%af%e4%bb%80%e4%b9%88">#</a> + JSON 是什么 +</h2><p>JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为<a class="link" href="https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" target="_blank" rel="noopener" + >ECMA-404</a>。</p> +<p>虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。</p> +<p>例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;title&#34;</span><span class="o">:</span> <span class="s2">&#34;Design Patterns&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;subtitle&#34;</span><span class="o">:</span> <span class="s2">&#34;Elements of Reusable Object-Oriented Software&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;author&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Erich Gamma&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Richard Helm&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Ralph Johnson&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;John Vlissides&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;year&#34;</span><span class="o">:</span> <span class="mi">2009</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;weight&#34;</span><span class="o">:</span> <span class="mf">1.8</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;hardcover&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;publisher&#34;</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Company&#34;</span><span class="o">:</span> <span class="s2">&#34;Pearson Education&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Country&#34;</span><span class="o">:</span> <span class="s2">&#34;India&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;website&#34;</span><span class="o">:</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。</p> +<p>从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:</p> +<ul> +<li>null: 表示为 null</li> +<li>boolean: 表示为 true 或 false</li> +<li>number: 一般的浮点数表示方式,在下一单元详细说明</li> +<li>string: 表示为 &ldquo;&hellip;&rdquo;</li> +<li>array: 表示为 [ &hellip; ]</li> +<li>object: 表示为 { &hellip; }</li> +</ul> +<p>我们要实现的 JSON 库,主要是完成 3 个需求:</p> +<ol> +<li>把 JSON 文本解析为一个树状数据结构(parse)。</li> +<li>提供接口访问该数据结构(access)。</li> +<li>把数据结构转换成 JSON 文本(stringify)。</li> +</ol> +<p><img src="https://3000ye.com/p/cppjson-ch01/assets/requirement.png" + width="440" + height="78" + srcset="https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png 480w, https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + + class="gallery-image" + data-flex-grow="564" + data-flex-basis="1353px" + +></p> +<p>我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。</p> +<h2 id="搭建编译环境"> + <a href="#%e6%90%ad%e5%bb%ba%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#</a> + 搭建编译环境 +</h2><p>我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。</p> +<p>我们的 JSON 库名为 CppJson,代码文件只有 3 个:</p> +<ol> +<li><code>include/cppjson.hpp</code>:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。</li> +<li><code>cppjson.cpp</code>:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。</li> +<li><code>cppjsonTest.cpp</code>:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。</li> +</ol> +<p>为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 <a class="link" href="https://cmake.org/" target="_blank" rel="noopener" + >CMake</a>。</p> +<p>在 OS X 平台中,在命令行通过命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir build +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build +</span></span><span class="line"><span class="cl">cmake -DCMAKE_BUILD_TYPE<span class="o">=</span>Debug .. +</span></span><span class="line"><span class="cl">make +</span></span></code></pre></td></tr></table> +</div> +</div><p>将 Debug 改成 Release 就会生成 Release 配置的 makefile。</p> +<p>在 Vscode 中,可以通过配置 <code>tasks.json</code> 文件来进行自动 build:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//.vscode/tasks.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdir&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-p&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;-DCMAKE_BUILD_TYPE=Debug&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//在此处添加其它CMAKE选项 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;..&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-j16&#34;</span><span class="p">,],</span> <span class="c1">//根据机器cpu核心数量自行调整 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOrder&#34;</span><span class="p">:</span> <span class="s2">&#34;sequence&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOn&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> <span class="s2">&#34;make&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后执行 build 生成的文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ./build/cppjson_test_ch01 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">16/16 <span class="o">(</span>100.00%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。</p> +<h2 id="头文件与-api-设计"> + <a href="#%e5%a4%b4%e6%96%87%e4%bb%b6%e4%b8%8e-api-%e8%ae%be%e8%ae%a1">#</a> + 头文件与 API 设计 +</h2><p><code>Cpp</code> 语言有头文件的概念,需要使用 <code>#include</code>去引入头文件中的类型声明和函数声明。但由于头文件也可以 <code>#include</code> 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#pragma once +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonType</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NULL</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_TRUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_FALSE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NUMBER</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_STRING</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_ARRAY</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_OBJECT</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 <code>cppjson_value</code> 结构体表示,我们会称它为一个 JSON 值(JSON value)。</p> +<p>在此单元中,我们只需要实现 <code>null</code>, <code>true</code> 和 <code>false</code> 的解析,因此该结构体只需要存储一个 <code>cppjsonType</code>,之后的单元会逐步加入其他数据。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjsonType</span> <span class="n">type</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_value</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,我们现在只需要两个 API 函数,一个是解析 JSON:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>传入的 JSON 文本是一个 <code>string</code> 字符串,由于我们不应该改动这个输入字符串,所以使用 <code>const std::string</code> 类型。</p> +<p>返回值是以下这些枚举类中的值,无错误会返回 <code>cppjsonParseCode::OK</code>,其他值在下节解释。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonParseCode</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">OK</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">INVALID_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">ROOT_NOT_SINGULAR</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时我们只需要一个访问结果的函数,就是获取其类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonType</span> <span class="nf">cppjson_get_type</span><span class="p">(</span><span class="k">const</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="json-语法子集"> + <a href="#json-%e8%af%ad%e6%b3%95%e5%ad%90%e9%9b%86">#</a> + JSON 语法子集 +</h2><p>下面是此单元的 JSON 语法子集,使用 <a class="link" href="https://tools.ietf.org/html/rfc7159" target="_blank" rel="noopener" + >RFC7159</a> 中的 <a class="link" href="https://tools.ietf.org/html/rfc5234" target="_blank" rel="noopener" + >ABNF</a> 表示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">JSON-text</span> <span class="err">=</span> <span class="err">ws</span> <span class="err">value</span> <span class="err">ws</span> +</span></span><span class="line"><span class="cl"><span class="err">ws</span> <span class="err">=</span> <span class="err">*(%x</span><span class="mi">20</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">09</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">A</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">D)</span> +</span></span><span class="line"><span class="cl"><span class="err">value</span> <span class="err">=</span> <span class="kc">null</span> <span class="err">/</span> <span class="kc">false</span> <span class="err">/</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="kc">null</span> <span class="err">=</span> <span class="s2">&#34;null&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">false</span> <span class="err">=</span> <span class="s2">&#34;false&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">true</span> <span class="err">=</span> <span class="s2">&#34;true&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当中 <code>%xhh</code> 表示以 16 进制表示的字符,<code>/</code> 是多选一,<code>*</code> 是零或多个,<code>()</code> 用于分组。</p> +<p>那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。</p> +<p>第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。</p> +<p>第三行是说,我们现时的值只可以是 <code>null</code>、<code>false</code> 或 <code>true</code>,它们分别有对应的字面值(literal)。</p> +<p>我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。</p> +<p>在这个 JSON 语法子集下,我们定义 3 种错误码:</p> +<ul> +<li>若一个 JSON 只含有空白,传回 <code>LEPT_PARSE_EXPECT_VALUE</code>。</li> +<li>若一个值之后,在空白之后还有其他字符,传回 <code>LEPT_PARSE_ROOT_NOT_SINGULAR</code>。</li> +<li>若值不是那三种字面值,传回 <code>LEPT_PARSE_INVALID_VALUE</code>。</li> +</ul> +<h2 id="单元测试"> + <a href="#%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95">#</a> + 单元测试 +</h2><p>许多同学在做练习题时,都是以 <code>printf</code>/<code>cout</code> 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。</p> +<p>常用的单元测试框架有 xUnit 系列,如 C++ 的 <a class="link" href="https://github.com/google/googletest" target="_blank" rel="noopener" + >Google Test</a>、C# 的 <a class="link" href="https://www.nunit.org/" target="_blank" rel="noopener" + >NUnit</a>。我们为了简单起见,会编写一个极简单的单元测试方式。</p> +<p>一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:</p> +<ol> +<li>加入一个测试。</li> +<li>运行所有测试,新的测试应该会失败。</li> +<li>编写实现代码。</li> +<li>运行所有测试,若有测试失败回到3。</li> +<li>重构代码。</li> +<li>回到 1。</li> +</ol> +<p>TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。</p> +<p>但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。</p> +<p>回到 CppJson 项目,<code>cppjsonTest.cpp</code> 包含了一个极简的单元测试框架:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;include/cppjson.hpp&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;typeinfo&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">main_ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 测试宏接口 +</span></span></span><span class="line"><span class="cl"><span class="c1">// flag 表示测试点是否通过,如果未通过则打印异常信息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define EXPECT_BASE(flag, expect, actual) \ +</span></span></span><span class="line"><span class="cl"><span class="cp"> do {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> test_count ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> if (flag) test_pass ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> else {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> fprintf(stderr, &#34;%s:%d, expect = %s(%d), actual = %s(%d)\n&#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast&lt;int&gt;(expect), typeid(actual).name(), static_cast&lt;int&gt;(actual));\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> main_ret = 1;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> }\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> } while(0) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse_null</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_value</span> <span class="n">v</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_FALSE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">,</span> <span class="n">cppjson_parse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">,</span> <span class="s">&#34;null&#34;</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">,</span> <span class="n">cppjson_get_type</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse_null</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d/%d (%3.2f%%) passed</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">test_pass</span><span class="p">,</span> <span class="n">test_count</span><span class="p">,</span> <span class="n">test_pass</span> <span class="o">*</span> <span class="mf">100.0</span> <span class="o">/</span> <span class="n">test_count</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">main_ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时只提供了一个 <code>EXPECT_TYPE(expect, actual)</code> 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。</p> +<p>若按照 TDD 的步骤,我们先写一个测试,如上面的 <code>test_parse_null()</code>,而 <code>cppjson_parse()</code> 只返回 <code>cppjsonParseCode::OK</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/CppJson/ch01/cppjsonTest.cpp:30, <span class="nv">expect</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>0<span class="o">)</span>, <span class="nv">actual</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>2<span class="o">)</span> +</span></span><span class="line"><span class="cl">15/16 <span class="o">(</span>93.75%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>为通过的测试是因为 <code>cppjson_parse()</code> 没有把 <code>v.type</code> 改成 <code>cppjsonType::CPPJSON_NULL</code>,造成失败。我们再实现 <code>lept_parse()</code> 令到它能通过测试。</p> +<p>然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。</p> +<h2 id="实现解析器"> + <a href="#%e5%ae%9e%e7%8e%b0%e8%a7%a3%e6%9e%90%e5%99%a8">#</a> + 实现解析器 +</h2><p>有了 API 的设计、单元测试,终于要实现解析器了。</p> +<p>首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 <code>cppjson_context</code> 结构体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_context</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// json 解析函数:ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span> <span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_context</span> <span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 分别解析 ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">cppjson_parse_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span> <span class="o">?</span> <span class="n">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:</p> +<ul> +<li>n ➔ null</li> +<li>t ➔ true</li> +<li>f ➔ false</li> +<li>&quot; ➔ string</li> +<li>0-9/- ➔ number</li> +<li>[ ➔ array</li> +<li>{ ➔ object</li> +</ul> +<p>所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39; &#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\t&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\r&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span> <span class="o">++</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 ws2 之后是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">ROOT_NOT_SINGULAR</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 检测 null 值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_null</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">head</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">head</span> <span class="o">!=</span> <span class="s">&#34;null&#34;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 value +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_value</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;n&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_null</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;t&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_true</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;f&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_false</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;\0&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">EXPECT_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch09 + https://3000ye.com/p/c-primer-ch09/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/p/c-primer-ch09/ + <img src="https://3000ye.com/p/c-primer-ch09/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch09" /><h1 id="顺序容器"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8">#</a> + 顺序容器 +</h1><p>一个容器就是一些特定类型对象的集合,<strong>顺序容器(sequence container)</strong> 为程序员提供了控制元素存储和访问顺序的能力。</p> +<h2 id="顺序容器概述"> + <a href="#%e9%a1%ba%e5%ba%8f%e5%ae%b9%e5%99%a8%e6%a6%82%e8%bf%b0">#</a> + 顺序容器概述 +</h2><p>下面列出了标准库中的顺序容器,不同容器有不同的性能折中:</p> +<ul> +<li>想容器添加或从容器删除元素的代价。</li> +<li>非顺序访问容器中元素的代价。</li> +</ul> +<table> +<thead> +<tr> +<th>容器类型</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector</code></td> +<td>可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。</td> +</tr> +<tr> +<td><code>deque</code></td> +<td>双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。</td> +</tr> +<tr> +<td><code>list</code></td> +<td>双向链表。只支持双向顺序访问。在<code>list</code>中任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>forward_list</code></td> +<td>单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。</td> +</tr> +<tr> +<td><code>array</code></td> +<td>固定大小数组。支持快速随机访问。不能添加或者删除元素。</td> +</tr> +<tr> +<td><code>string</code></td> +<td>与<code>vector</code>相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。</td> +</tr> +</tbody> +</table> +<ul> +<li>除了固定大小的 <code>array</code> 外,其他容器都提供高效、灵活的内存管理。</li> +<li>通常使用 <code>vector</code> 是最好的选择,除非你有很好的理由选择其他容器。</li> +<li>如果程序中有很多小的元素,且空间的额外开销很重要,则不要使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序要求随记访问元素,应使用 <code>vector</code> 或 <code>deque</code>。</li> +<li>如果程序要求在容器的中间插入或删除元素,应使用 <code>list</code> 或 <code>forward_list</code>。</li> +<li>如果程序需要再头尾位置插入或删除元素,但不会再中间位置进行操作,应使用 <code>deque</code>。</li> +</ul> +<h2 id="容器库概念"> + <a href="#%e5%ae%b9%e5%99%a8%e5%ba%93%e6%a6%82%e5%bf%b5">#</a> + 容器库概念 +</h2><p>容器类型操作上形成了一种层次:</p> +<ul> +<li>某些操作是所有容器类型都提供的。</li> +<li>另一些操作仅针对顺序容器、关联容器或无序容器。</li> +</ul> +<h3 id="类型"> + <a href="#%e7%b1%bb%e5%9e%8b">#</a> + 类型 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iterator</code></td> +<td>此容器类型的迭代器类型</td> +</tr> +<tr> +<td><code>const_iterator</code></td> +<td>可以读取元素但不能修改元素的迭代器类型</td> +</tr> +<tr> +<td><code>size_type</code></td> +<td>无符号整数类型,足够保存此种容器类型最大可能的大小</td> +</tr> +<tr> +<td><code>difference_type</code></td> +<td>带符号整数类型,足够保存两个迭代器之间的距离</td> +</tr> +<tr> +<td><code>value_type</code></td> +<td>元素类型</td> +</tr> +<tr> +<td><code>reference</code></td> +<td>元素的左值类型;和<code>value_type &amp;</code>含义相同</td> +</tr> +<tr> +<td><code>const_reference</code></td> +<td>元素的<code>const</code>左值类型,即<code>const value_type &amp;</code></td> +</tr> +</tbody> +</table> +<h3 id="构造函数"> + <a href="#%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0">#</a> + 构造函数 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>C c;</code></td> +<td>默认构造函数,构造空容器</td> +</tr> +<tr> +<td><code>C c1(c2);</code>或<code>C c1 = c2;</code></td> +<td>构造<code>c2</code>的拷贝<code>c1</code></td> +</tr> +<tr> +<td><code>C c(b, e)</code></td> +<td>构造<code>c</code>,将迭代器<code>b</code>和<code>e</code>指定范围内的所有元素拷贝到<code>c</code></td> +</tr> +<tr> +<td><code>C c(a, b, c...)</code></td> +<td>列表初始化<code>c</code></td> +</tr> +<tr> +<td><code>C c(n)</code></td> +<td>只支持顺序容器,且不包括<code>array</code>,包含<code>n</code>个元素,这些元素进行了值初始化</td> +</tr> +<tr> +<td><code>C c(n, t)</code></td> +<td>包含<code>n</code>个初始值为<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>只有顺序容器的构造函数才接受大小参数,关联容器并不支持。</li> +<li><code>array</code>具有固定大小。</li> +<li>和其他容器不同,默认构造的<code>array</code>是非空的。</li> +<li>直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。</li> +<li>使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。</li> +</ul> +<h3 id="赋值和swap"> + <a href="#%e8%b5%8b%e5%80%bc%e5%92%8cswap">#</a> + 赋值和<code>swap</code> +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c1 = c2;</code></td> +<td>将<code>c1</code>中的元素替换成<code>c2</code>中的元素</td> +</tr> +<tr> +<td><code>c1 = {a, b, c...}</code></td> +<td>将<code>c1</code>中的元素替换成列表中的元素(不适用于<code>array</code>)</td> +</tr> +<tr> +<td><code>c1.swap(c2)</code></td> +<td>交换<code>c1</code>和<code>c2</code>的元素</td> +</tr> +<tr> +<td><code>swap(c1, c2)</code></td> +<td>等价于<code>c1.swap(c2)</code></td> +</tr> +<tr> +<td><code>c.assign(b, e)</code></td> +<td>将<code>c</code>中的元素替换成迭代器<code>b</code>和<code>e</code>表示范围中的元素,<code>b</code>和<code>e</code>不能指向<code>c</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(il)</code></td> +<td>将<code>c</code>中的元素替换成初始化列表<code>il</code>中的元素</td> +</tr> +<tr> +<td><code>c.assign(n, r)</code></td> +<td>将<code>c</code>中的元素替换为<code>n</code>个值是<code>t</code>的元素</td> +</tr> +</tbody> +</table> +<ul> +<li>使用非成员版本的<code>swap</code>是一个好习惯。</li> +<li><code>assign</code>操作不适用于关联容器和<code>array</code></li> +</ul> +<h3 id="大小"> + <a href="#%e5%a4%a7%e5%b0%8f">#</a> + 大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.size()</code></td> +<td><code>c</code>中元素的数目(不支持<code>forward_list</code>)</td> +</tr> +<tr> +<td><code>c.max_size()</code></td> +<td><code>c</code>中可保存的最大元素数目</td> +</tr> +<tr> +<td><code>c.empty()</code></td> +<td>若<code>c</code>中存储了元素,返回<code>false</code>,否则返回<code>true</code></td> +</tr> +</tbody> +</table> +<h3 id="添加元素"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%85%83%e7%b4%a0">#</a> + 添加元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.push_back(t)</code></td> +<td>在<code>c</code>尾部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_back(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.push_front(t)</code></td> +<td>在<code>c</code>头部创建一个值为<code>t</code>的元素,返回<code>void</code></td> +</tr> +<tr> +<td><code>c.emplace_front(args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前创建一个值是<code>t</code>的元素,返回指向新元素的迭代器</td> +</tr> +<tr> +<td><code>c.emplace(p, args)</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>c.insert(p, n, t)</code></td> +<td>在迭代器<code>p</code>指向的元素之前插入<code>n</code>个值为<code>t</code>的元素,返回指向第一个新元素的迭代器;如果<code>n</code>是0,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, b, e)</code></td> +<td>将迭代器<code>b</code>和<code>e</code>范围内的元素,插入到<code>p</code>指向的元素之前;如果范围为空,则返回<code>p</code></td> +</tr> +<tr> +<td><code>c.insert(p, il)</code></td> +<td><code>il</code>是一个花括号包围中的元素值列表,将其插入到<code>p</code>指向的元素之前;如果<code>il</code>是空,则返回<code>p</code></td> +</tr> +</tbody> +</table> +<ul> +<li>因为这些操作会改变大小,因此不适用于<code>array</code>。</li> +<li><code>forward_list</code>有自己专有版本的<code>insert</code>和<code>emplace</code>。</li> +<li><code>forward_list</code>不支持<code>push_back</code>和<code>emplace_back</code>。</li> +<li>当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。</li> +<li><code>emplace</code>开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。</li> +<li>传递给<code>emplace</code>的参数必须和元素类型的构造函数相匹配。</li> +</ul> +<h3 id="访问元素"> + <a href="#%e8%ae%bf%e9%97%ae%e5%85%83%e7%b4%a0">#</a> + 访问元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.back()</code></td> +<td>返回<code>c</code>中尾元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c.front()</code></td> +<td>返回<code>c</code>中头元素的引用。若<code>c</code>为空,函数行为未定义</td> +</tr> +<tr> +<td><code>c[n]</code></td> +<td>返回<code>c</code>中下标是<code>n</code>的元素的引用,<code>n</code>时候一个无符号证书。若<code>n&gt;=c.size()</code>,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.at(n)</code></td> +<td>返回下标为<code>n</code>的元素引用。如果下标越界,则抛出<code>out_of_range</code>异常</td> +</tr> +</tbody> +</table> +<ul> +<li>访问成员函数返回的是引用。</li> +<li><code>at</code>和下标操作只适用于<code>string</code>、<code>vector</code>、<code>deque</code>、<code>array</code>。</li> +<li><code>back</code>不适用于<code>forward_list</code>。</li> +<li>如果希望下标是合法的,可以使用<code>at</code>函数。</li> +</ul> +<h3 id="删除元素"> + <a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0">#</a> + 删除元素 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.pop_back()</code></td> +<td>删除<code>c</code>中尾元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.pop_front()</code></td> +<td>删除<code>c</code>中首元素,若<code>c</code>为空,则函数行为未定义。函数返回<code>void</code></td> +</tr> +<tr> +<td><code>c.erase(p)</code></td> +<td>删除迭代器<code>p</code>指向的元素,返回一个指向被删除元素之后的元素的迭代器,若<code>p</code>本身是尾后迭代器,则函数行为未定义</td> +</tr> +<tr> +<td><code>c.erase(b, e)</code></td> +<td>删除迭代器<code>b</code>和<code>e</code>范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若<code>e</code>本身就是尾后迭代器,则返回尾后迭代器</td> +</tr> +<tr> +<td><code>c.clear()</code></td> +<td>删除<code>c</code>中所有元素,返回<code>void</code></td> +</tr> +</tbody> +</table> +<ul> +<li>会改变容器大小,不适用于<code>array</code>。</li> +<li><code>forward_list</code>有特殊版本的<code>erase</code></li> +<li><code>forward_list</code>不支持<code>pop_back</code></li> +<li><code>vector</code>和<code>string</code>不支持<code>pop_front</code></li> +</ul> +<h3 id="特殊的-forwad_list操作"> + <a href="#%e7%89%b9%e6%ae%8a%e7%9a%84-forwad_list%e6%93%8d%e4%bd%9c">#</a> + 特殊的 <code>forwad_list</code>操作 +</h3><ul> +<li>链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。</li> +<li><code>forward_list</code>定义了<code>before_begin</code>,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。</li> +</ul> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>lst.before_begin()</code></td> +<td>返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。</td> +</tr> +<tr> +<td><code>lst.cbefore_begin()</code></td> +<td>同上,但是返回的是常量迭代器。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象</td> +</tr> +<tr> +<td><code>lst.insert_after(p, n, t)</code></td> +<td>在迭代器<code>p</code>之后插入元素。<code>t</code>是一个对象,<code>n</code>是数量。若<code>n</code>是0则函数行为未定义</td> +</tr> +<tr> +<td><code>lst.insert_after(p, b, e)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由迭代器<code>b</code>和<code>e</code>指定范围。</td> +</tr> +<tr> +<td><code>lst.insert_after(p, il)</code></td> +<td>在迭代器<code>p</code>之后插入元素。由<code>il</code>指定初始化列表。</td> +</tr> +<tr> +<td><code>emplace_after(p, args)</code></td> +<td>使用<code>args</code>在<code>p</code>之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若<code>p</code>为尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(p)</code></td> +<td>删除<code>p</code>指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若<code>p</code>指向<code>lst</code>的尾元素或者是一个尾后迭代器,则函数行为未定义。</td> +</tr> +<tr> +<td><code>lst.erase_after(b, e)</code></td> +<td>类似上面,删除对象换成从<code>b</code>到<code>e</code>指定的范围。</td> +</tr> +</tbody> +</table> +<h3 id="改变容器大小"> + <a href="#%e6%94%b9%e5%8f%98%e5%ae%b9%e5%99%a8%e5%a4%a7%e5%b0%8f">#</a> + 改变容器大小 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.resize(n)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,若<code>n&lt;c.size()</code>,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化</td> +</tr> +<tr> +<td><code>c.resize(n, t)</code></td> +<td>调整<code>c</code>的大小为<code>n</code>个元素,任何新添加的元素都初始化为值<code>t</code></td> +</tr> +</tbody> +</table> +<h3 id="获取迭代器"> + <a href="#%e8%8e%b7%e5%8f%96%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 获取迭代器 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>c.begin()</code>, <code>c.end()</code></td> +<td>返回指向<code>c</code>的首元素和尾元素之后位置的迭代器</td> +</tr> +<tr> +<td><code>c.cbegin()</code>, <code>c.cend()</code></td> +<td>返回<code>const_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>以<code>c</code>开头的版本是C++11新标准引入的</li> +<li>当不需要写访问时,应该使用<code>cbegin</code>和<code>cend</code>。</li> +</ul> +<h3 id="反向容器的额外成员"> + <a href="#%e5%8f%8d%e5%90%91%e5%ae%b9%e5%99%a8%e7%9a%84%e9%a2%9d%e5%a4%96%e6%88%90%e5%91%98">#</a> + 反向容器的额外成员 +</h3><table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>reverse_iterator</code></td> +<td>按逆序寻址元素的迭代器</td> +</tr> +<tr> +<td><code>const_reverse_iterator</code></td> +<td>不能修改元素的逆序迭代器</td> +</tr> +<tr> +<td><code>c.rbegin()</code>, <code>c.rend()</code></td> +<td>返回指向<code>c</code>的尾元素和首元素之前位置的迭代器</td> +</tr> +<tr> +<td><code>c.crbegin()</code>, <code>c.crend()</code></td> +<td>返回<code>const_reverse_iterator</code></td> +</tr> +</tbody> +</table> +<ul> +<li>不支持<code>forward_list</code></li> +</ul> + + + + C++ Primer Ch08 + https://3000ye.com/p/c-primer-ch08/ + Sun, 10 Dec 2023 15:24:27 +0800 + + https://3000ye.com/p/c-primer-ch08/ + <img src="https://3000ye.com/p/c-primer-ch08/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch08" /><h1 id="io-库"> + <a href="#io-%e5%ba%93">#</a> + IO 库 +</h1><p>我们的程序已经使用了很多 IO 库设施:</p> +<ul> +<li><code>istream</code> (输入流)类型,提供输入操作。</li> +<li><code>ostream</code> (输出流)类型,提供输出操作。</li> +<li><code>cin</code>,一个 <code>istream</code> 对象,从标准输入读取数据。</li> +<li><code>cout</code>,一个 <code>ostream</code> 对象,想标准输出写入数据。</li> +<li><code>cerr</code>,一个 <code>ostream</code> 对象,通常用于输出程序错误信息,写入到标准错误。</li> +<li><code>&gt;&gt;</code> 运算符,用来从一个 <code>istream</code> 对象中读取输入数据。</li> +<li><code>&lt;&lt;</code> 运算符,用来向一个 <code>ostream</code> 对象中写入输出数据。</li> +<li><code>getline</code> 函数,从一个给定的 <code>istream</code> 对象中读取一行数据,存入到一个给定的 <code>string</code> 对象中。</li> +</ul> +<h2 id="io-库-1"> + <a href="#io-%e5%ba%93-1">#</a> + IO 库 +</h2><p>IO 类型和对象一般都是操纵 <code>char</code> 数据的,但有些使用需要对文件、<code>string</code> 进行操作,因此分别定义了三个头文件:</p> +<ul> +<li><code>iostream</code> 头文件:从标准流中读写数据,<code>istream</code>、<code>ostream</code> 等。</li> +<li><code>fstream</code> 头文件:从文件中读写数据,<code>ifstream</code>、<code>ofstream</code> 等。</li> +<li><code>sstream</code> 头文件:从字符串中读写数据,<code>istringstream</code>、<code>ostringstream</code> 等。</li> +</ul> + + + + C++ Primer Ch07 + https://3000ye.com/p/c-primer-ch07/ + Sun, 10 Dec 2023 14:35:42 +0800 + + https://3000ye.com/p/c-primer-ch07/ + <img src="https://3000ye.com/p/c-primer-ch07/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch07" /><h1 id="类"> + <a href="#%e7%b1%bb">#</a> + 类 +</h1><p>类的基本思想是 <strong>数据抽象(data abstraction)</strong> 和 <strong>封装(encapsulation)</strong>。数据抽象是一种依赖于 <strong>接口(interface)</strong> 和 <strong>实现(implementation)</strong> 分离的编程(及设计)技术。类的接口包括用户所能执行的操作,类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。</p> +<p>封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,即类的用户只能使用接口而无法访问实现部分。</p> +<p>类要想实现数据抽象和封装,首先需要定义一个 <strong>抽象数据类型(abstract data type)</strong>。</p> +<h2 id="定义抽象数据类型"> + <a href="#%e5%ae%9a%e4%b9%89%e6%8a%bd%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#</a> + 定义抽象数据类型 +</h2><h2 id="访问控制与封装"> + <a href="#%e8%ae%bf%e9%97%ae%e6%8e%a7%e5%88%b6%e4%b8%8e%e5%b0%81%e8%a3%85">#</a> + 访问控制与封装 +</h2><p>我们为类定义了接口之后,没有任何机制强制用户使用这些接口,我们的类还没有进行封装。在 <code>C++</code> 中使用 <strong>访问说明符(access specifiers)</strong> 加强类的封装:</p> +<ul> +<li>定义在 <code>public</code> 说明符之后的成员,在整个程序内都可被访问。</li> +<li>定义在 <code>private</code> 说明符之后的成员,只能被类的成员访问,即隐藏了这些成员的实现。</li> +</ul> +<p>定义新的 <code>Sales_data</code> 类:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sales_data</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">bookNo</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="n">units_sold</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">revenue</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="nf">avg_price</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">units_sold</span> <span class="o">?</span> <span class="n">revenue</span> <span class="o">/</span> <span class="nl">units_sold</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">p</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="n">units_sold</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">revenue</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&amp;</span><span class="n">s</span><span class="p">)</span><span class="o">:</span> <span class="n">bookNo</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">istream</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">isbn</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">bookNo</span><span class="p">;</span> <span class="p">};</span> +</span></span><span class="line"><span class="cl"> <span class="n">Sales_data</span> <span class="o">&amp;</span><span class="n">combine</span><span class="p">(</span><span class="k">const</span> <span class="n">Sales_data</span><span class="o">&amp;</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch06 + https://3000ye.com/p/c-primer-ch06/ + Fri, 08 Dec 2023 21:13:28 +0800 + + https://3000ye.com/p/c-primer-ch06/ + <img src="https://3000ye.com/p/c-primer-ch06/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch06" /><h1 id="函数"> + <a href="#%e5%87%bd%e6%95%b0">#</a> + 函数 +</h1><p>函数是一个命名了的代码块,我们通过调用函数执行响应的代码。函数可以有 0 个或多个参数,而且(通常)会返回一个结果。可以重载函数,即同一个名字可以对应几个不同的函数。</p> +<h2 id="函数基础"> + <a href="#%e5%87%bd%e6%95%b0%e5%9f%ba%e7%a1%80">#</a> + 函数基础 +</h2><p>一个典型的 <strong>函数(function)</strong> 定义包括以下部分:返回类型(return type)、函数名字、由 0 个或多个 <strong>形参(parameter)</strong> 组成的列表以及函数体。</p> +<p>我们通过 <strong>调用运算符(call operator)</strong> 来执行函数:调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的 <strong>实参(argument)</strong> 列表,我们用实参初始化函数的形参,调用表达式的类型就是函数返回的类型。</p> +<p><strong>编写函数</strong>:例如编写一个求 <code>n</code> 的阶乘的函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fact</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">*=</span> <span class="n">n</span> <span class="o">--</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><strong>调用函数</strong>:要调用 <code>fact</code> 函数,首先需要提供一个整数,得到的返回结果也是一个整数值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fact</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;5! = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>函数的调用完成两项工作:实参初始化函数对应的形参,将控制权转移给被调函数。此时,<strong>主调函数(calling funciton)</strong> 的执行暂停,<strong>被调函数(called funciton)</strong> 开始执行。</p> +<h3 id="局部对象"> + <a href="#%e5%b1%80%e9%83%a8%e5%af%b9%e8%b1%a1">#</a> + 局部对象 +</h3><p>在 <code>C++</code> 语言中,名字有作用域,对象有 <strong>生命周期(lifetime)</strong>:</p> +<ul> +<li>名字的作用域是程序文本的一部分,名字在其中可见。</li> +<li>对象的生命周期是程序执行过程中该对象存在的一段时间。</li> +</ul> +<p>在函数体内,形参和内部定义的变量统称为 <strong>局部变量(local variable)</strong>,它们对函数而言是“局部”饿,仅在函数的作用域内可见,同时局部变量还会 **隐藏(hide)**在外层作用域中同名的其他声明中。</p> +<p><strong>局部静态对象</strong>:某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成 <code>static</code> 类型从而获得这样的对象,<strong>局部静态对象(local static object)</strong> 在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。</p> +<h2 id="参数传递"> + <a href="#%e5%8f%82%e6%95%b0%e4%bc%a0%e9%80%92">#</a> + 参数传递 +</h2> + + + C++ Primer Ch04 + https://3000ye.com/p/c-primer-ch04/ + Tue, 28 Nov 2023 08:49:13 +0800 + + https://3000ye.com/p/c-primer-ch04/ + <img src="https://3000ye.com/p/c-primer-ch04/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch04" /><h1 id="表达式"> + <a href="#%e8%a1%a8%e8%be%be%e5%bc%8f">#</a> + 表达式 +</h1><p>表达式由一个或多个 <strong>运算对象(operand)</strong> 组成对表达式求值将得到一个 <strong>结果(result)</strong>。</p> +<h2 id="基础"> + <a href="#%e5%9f%ba%e7%a1%80">#</a> + 基础 +</h2><h3 id="基本概念"> + <a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a> + 基本概念 +</h3><p><code>C++</code> 定义了一元运算符(unary operator)和二元运算符(binary operator),分别作用于一个运算对象和两个运算对象。此外,还有三元运算符,有些运算符既是一元也是二元运算符。</p> +<p>对于含有多个运算符的复杂表达式,首先需要理解运算符的:优先级(precedence)、结合律(associativity)以及运算对象的求值顺序(order of evaluation)。</p> +<p>在表达式求值过程中,小整数类型(<code>bool</code>、<code>char</code>、<code>short</code>)等通常会被 <strong>提升(promoted)</strong> 成较大的整数类型(<code>int</code>)。</p> +<p>当运算符作用在类类型的运算对象时,用户可以自定定义其含义,称为 <strong>重载运算符(overloaded operator)</strong>。</p> +<p><code>C++</code> 的表达式要不然是 <strong>右值(rvalue)</strong>,要不然就是 <strong>左值(lvalue)</strong>,这两个名词是从 <code>C</code> 继承过来的。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。</p> +<h3 id="求值顺序"> + <a href="#%e6%b1%82%e5%80%bc%e9%a1%ba%e5%ba%8f">#</a> + 求值顺序 +</h3><p>在大多数情况下,表达式求值的顺序是没有明确指定的:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">f1</span><span class="p">()</span> <span class="o">*</span> <span class="n">f2</span><span class="p">()</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>我们知道 <code>f1</code> 和 <code>f2</code> 一定会在执行乘法之前被调用,但是无法知道是 <code>f1</code> 先被调用还是 <code>f2</code> 先被调用。对于没有指定调用顺序的程序来说,如果 <code>f1</code> 和 <code>f2</code> 同时修改了同一个对象,将会引发错误并产生未定义的行为。</p> +<h2 id="算术运算符"> + <a href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 算术运算符 +</h2><p><strong>溢出</strong>:当计算的结果超出该类型所能表示的范围时就会产生溢出。</p> +<p><strong><code>bool</code> 类型不应该参与计算</strong>。</p> +<p><strong>取余运算</strong>:<code>m % n</code> 的结果的符号与 <code>m</code> 相同。</p> +<h2 id="逻辑运算符"> + <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 逻辑运算符 +</h2><p><strong>短路求值</strong>:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值,再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。</p> +<h2 id="赋值运算符"> + <a href="#%e8%b5%8b%e5%80%bc%e8%bf%90%e7%ae%97%e7%ac%a6">#</a> + 赋值运算符 +</h2> + + + C++ Primer Ch03 + https://3000ye.com/p/c-primer-ch03/ + Tue, 14 Nov 2023 22:14:23 +0800 + + https://3000ye.com/p/c-primer-ch03/ + <img src="https://3000ye.com/p/c-primer-ch03/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch03" /><h1 id="字符串向量和数组"> + <a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%90%91%e9%87%8f%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 字符串、向量和数组 +</h1><h2 id="命名空间的-using-声明"> + <a href="#%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e7%9a%84-using-%e5%a3%b0%e6%98%8e">#</a> + 命名空间的 <code>using</code> 声明 +</h2><p>我们使用的库函数都有一个对应的命名空间,通常需要在声明或初始化变量时指定命名空间。为了简化这个操作,我们可以使用<code>using</code>进行声明:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">;</span> <span class="c1">// 单独使用某个函数 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 批量声明 std 中所有函数 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>头文件中不应该包含 <code>using</code> 声明,这样使用了该头文件的源文件也会使用这个声明,会带来风险。</p> +</blockquote> +<h2 id="标准库类型-string"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-string">#</a> + 标准库类型 <code>string</code> +</h2><p>标准库类型 <code>string</code> 表示可变长的字符序列,使用 <code>string</code> 类型必须首先包含 <code>string</code> 头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="定义和初始化-string-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-string-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>string</code> 对象 +</h3><p>初始化 <code>string</code> 对象的方式:</p> +<table> +<thead> +<tr> +<th>方式</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>string s1</code></td> +<td>默认初始化,<code>s1</code>是个空字符串</td> +</tr> +<tr> +<td><code>string s2(s1)</code></td> +<td><code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s2 = s1</code></td> +<td>等价于<code>s2(s1)</code>,<code>s2</code>是<code>s1</code>的副本</td> +</tr> +<tr> +<td><code>string s3(&quot;value&quot;)</code></td> +<td><code>s3</code>是字面值“value”的副本,除了字面值最后的那个空字符外</td> +</tr> +<tr> +<td><code>string s3 = &quot;value&quot;</code></td> +<td>等价于<code>s3(&quot;value&quot;)</code>,<code>s3</code>是字面值&quot;value&quot;的副本</td> +</tr> +<tr> +<td><code>string s4(n, 'c')</code></td> +<td>把<code>s4</code>初始化为由连续<code>n</code>个字符<code>c</code>组成的串</td> +</tr> +</tbody> +</table> +<p>拷贝初始化(copy initialization):使用 <code>=</code> 将一个已有的对象拷贝到正在创建的对象。</p> +<p>直接初始化(direct initialization):通过括号给对象赋值。</p> +<h3 id="string-对象的操作"> + <a href="#string-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>string</code> 对象的操作 +</h3><p><code>string</code>的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>os &lt;&lt; s</code></td> +<td>将<code>s</code>写到输出流<code>os</code>当中,返回<code>os</code></td> +</tr> +<tr> +<td><code>is &gt;&gt; s</code></td> +<td>从<code>is</code>中读取字符串赋给<code>s</code>,字符串以空白分割,返回<code>is</code></td> +</tr> +<tr> +<td><code>getline(is, s)</code></td> +<td>从<code>is</code>中读取一行赋给<code>s</code>,返回<code>is</code></td> +</tr> +<tr> +<td><code>s.empty()</code></td> +<td><code>s</code>为空返回<code>true</code>,否则返回<code>false</code></td> +</tr> +<tr> +<td><code>s.size()</code></td> +<td>返回<code>s</code>中字符的个数</td> +</tr> +<tr> +<td><code>s[n]</code></td> +<td>返回<code>s</code>中第<code>n</code>个字符的引用,位置<code>n</code>从0计起</td> +</tr> +<tr> +<td><code>s1+s2</code></td> +<td>返回<code>s1</code>和<code>s2</code>连接后的结果</td> +</tr> +<tr> +<td><code>s1=s2</code></td> +<td>用<code>s2</code>的副本代替<code>s1</code>中原来的字符</td> +</tr> +<tr> +<td><code>s1==s2</code></td> +<td>如果<code>s1</code>和<code>s2</code>中所含的字符完全一样,则它们相等;<code>string</code>对象的相等性判断对字母的大小写敏感</td> +</tr> +<tr> +<td><code>s1!=s2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td> +<td>利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较)</td> +</tr> +</tbody> +</table> +<p>读取 <code>string</code> 对象:</p> +<ul> +<li>使用 <code>IO</code> 操作符 <code>&gt;&gt;</code> 读取:忽略开头的空白(空格符、换行符、制表符等),从第一个真正的字符开始读起,直到遇到下一个空白。</li> +<li>使用 <code>getline()</code> 函数读取:将一整行读取为 <code>string</code> 对象,包括空白。</li> +</ul> +<p><code>s.size()</code> 返回 <code>string::size_type</code> 类型,是 <strong>无符号</strong> 类型的值,不能和 <code>int</code> 混用。</p> +<p><code>s1 + s2</code> 使用时,必须保证至少其中一个为 <code>string</code> 类型。例如:<code>string s = &quot;hello&quot; + &quot;world&quot;</code> 错误,其 <code>+</code> 两边都为字符串字面值。</p> +<p><strong>字符串字面值</strong> 和 <code>string</code> 是不同的类型。</p> +<h3 id="处理-string-对象中的字符"> + <a href="#%e5%a4%84%e7%90%86-string-%e5%af%b9%e8%b1%a1%e4%b8%ad%e7%9a%84%e5%ad%97%e7%ac%a6">#</a> + 处理 <code>string</code> 对象中的字符 +</h3><p><code>C++</code> 修改了 <code>c</code> 的标准库 <code>ctype.h</code> 为 <code>cctype</code>,其中定义了一组标准函数:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>isalnum(c)</code></td> +<td>当<code>c</code>是字母或数字时为真</td> +</tr> +<tr> +<td><code>isalpha(c)</code></td> +<td>当<code>c</code>是字母时为真</td> +</tr> +<tr> +<td><code>iscntrl(c)</code></td> +<td>当<code>c</code>是控制字符时为真</td> +</tr> +<tr> +<td><code>isdigit(c)</code></td> +<td>当<code>c</code>是数字时为真</td> +</tr> +<tr> +<td><code>isgraph(c)</code></td> +<td>当<code>c</code>不是空格但可以打印时为真</td> +</tr> +<tr> +<td><code>islower(c)</code></td> +<td>当<code>c</code>是小写字母时为真</td> +</tr> +<tr> +<td><code>isprint(c)</code></td> +<td>当<code>c</code>是可打印字符时为真</td> +</tr> +<tr> +<td><code>ispunct(c)</code></td> +<td>当<code>c</code>是标点符号时为真</td> +</tr> +<tr> +<td><code>isspace(c)</code></td> +<td>当<code>c</code>是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)</td> +</tr> +<tr> +<td><code>isupper(c)</code></td> +<td>当<code>c</code>是大写字母时为真</td> +</tr> +<tr> +<td><code>isxdigit(c)</code></td> +<td>当<code>c</code>是十六进制数字时为真</td> +</tr> +<tr> +<td><code>tolower(c)</code></td> +<td>当<code>c</code>是大写字母,输出对应的小写字母;否则原样输出<code>c</code></td> +</tr> +<tr> +<td><code>toupper(c)</code></td> +<td>当<code>c</code>是小写字母,输出对应的大写字母;否则原样输出<code>c</code></td> +</tr> +</tbody> +</table> +<p>遍历字符串:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">c</span> <span class="p">:</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">...</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>str[idx]</code> 中的 <code>idx</code> 为 <code>string::size_type</code> 类型,如果使用 <code>int</code> 会隐式转换为该类型。</p> +<h2 id="标准库类型-vector"> + <a href="#%e6%a0%87%e5%87%86%e5%ba%93%e7%b1%bb%e5%9e%8b-vector">#</a> + 标准库类型 <code>vector</code> +</h2><p>标准库类型 <code>vector</code> 表示对象的集合,其中给所有对象的类型都相同。因为 <code>vector</code> 容纳着其他对象,所以称其为 <strong>容器(container)</strong>,使用 <code>vector</code> 必须包含其头文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p><code>vector</code> 同时也是 <strong>类模板(class template)</strong>,模板本身不是类或函数,但可以使用模板创建类,这个过程称为 <strong>实例化(instantiation)</strong>。</p> +<p>当使用模板时,需要指出编译器应把类或函数实例化成何种类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// ls 保存 int 类型的对象 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">files</span><span class="p">;</span> <span class="c1">// 该向量中的元素是 vector 对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p><code>vector</code> 是模板,<code>vector&lt;int&gt;</code> 是类型。</p> +</blockquote> +<h3 id="定义和初始化-vector-对象"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96-vector-%e5%af%b9%e8%b1%a1">#</a> + 定义和初始化 <code>vector</code> 对象 +</h3><p>初始化<code>vector</code>对象的方法:</p> +<table> +<thead> +<tr> +<th>方法</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>vector&lt;T&gt; v1</code></td> +<td><code>v1</code>是一个空<code>vector</code>,它潜在的元素是<code>T</code>类型的,执行默认初始化</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2(v1)</code></td> +<td><code>v2</code>中包含有<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v2 = v1</code></td> +<td>等价于<code>v2(v1)</code>,<code>v2</code>中包含<code>v1</code>所有元素的副本</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v3(n, val)</code></td> +<td><code>v3</code>包含了n个重复的元素,每个元素的值都是<code>val</code></td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v4(n)</code></td> +<td><code>v4</code>包含了n个重复地执行了值初始化的对象</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5{a, b, c...}</code></td> +<td><code>v5</code>包含了初始值个数的元素,每个元素被赋予相应的初始值</td> +</tr> +<tr> +<td><code>vector&lt;T&gt; v5={a, b, c...}</code></td> +<td>等价于<code>v5{a, b, c...}</code></td> +</tr> +</tbody> +</table> +<h3 id="vector-对象的操作"> + <a href="#vector-%e5%af%b9%e8%b1%a1%e7%9a%84%e6%93%8d%e4%bd%9c">#</a> + <code>vector</code> 对象的操作: +</h3><p><code>vector</code>支持的操作:</p> +<table> +<thead> +<tr> +<th>操作</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>v.emtpy()</code></td> +<td>如果<code>v</code>不含有任何元素,返回真;否则返回假</td> +</tr> +<tr> +<td><code>v.size()</code></td> +<td>返回<code>v</code>中元素的个数</td> +</tr> +<tr> +<td><code>v.push_back(t)</code></td> +<td>向<code>v</code>的尾端添加一个值为<code>t</code>的元素</td> +</tr> +<tr> +<td><code>v[n]</code></td> +<td>返回<code>v</code>中第<code>n</code>个位置上元素的<strong>引用</strong></td> +</tr> +<tr> +<td><code>v1 = v2</code></td> +<td>用<code>v2</code>中的元素拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 = {a,b,c...}</code></td> +<td>用列表中元素的拷贝替换<code>v1</code>中的元素</td> +</tr> +<tr> +<td><code>v1 == v2</code></td> +<td><code>v1</code>和<code>v2</code>相等当且仅当它们的元素数量相同且对应位置的元素值都相同</td> +</tr> +<tr> +<td><code>v1 != v2</code></td> +<td>同上</td> +</tr> +<tr> +<td><code>&lt;</code>,<code>&lt;=</code>,<code>&gt;</code>, <code>&gt;=</code></td> +<td>以字典顺序进行比较</td> +</tr> +</tbody> +</table> +<h2 id="迭代器介绍"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e4%bb%8b%e7%bb%8d">#</a> + 迭代器介绍 +</h2><p>除了下标运算符外,<strong>迭代器(iterator)</strong> 也可以访问对象中的元素,所有标准库的容器都支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问。</p> +<h3 id="使用迭代器"> + <a href="#%e4%bd%bf%e7%94%a8%e8%bf%ad%e4%bb%a3%e5%99%a8">#</a> + 使用迭代器 +</h3><p>拥有迭代器的类型都具有 <code>begin</code> 和 <code>end</code> 成员,其中 <code>begin</code> 成员返回指向第一个元素的迭代器:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">e</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="c1">// b 和 e 类型相同 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><code>end</code> 成员返回指向容器“尾元素的下一个位置(one past the end)”的迭代器,即 <code>end</code> 指向容器的 <strong>尾后(off the end)</strong> 元素。这样的迭代器通常没有意义,只是作为标记,被称为 <strong>尾后迭代器(off-the-end iterator)</strong> 或 <strong>尾迭代器(end iterator)</strong>。</p> +<blockquote> +<p>若容器为空,则 <code>begin</code> 和 <code>end</code> 都返回尾后迭代器。</p> +</blockquote> +<p>标准容器迭代器的运算符:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>*iter</code></td> +<td>返回迭代器<code>iter</code>所指向的<strong>元素的引用</strong></td> +</tr> +<tr> +<td><code>iter-&gt;mem</code></td> +<td>等价于<code>(*iter).mem</code></td> +</tr> +<tr> +<td><code>++iter</code></td> +<td>令<code>iter</code>指示容器中的下一个元素</td> +</tr> +<tr> +<td><code>--iter</code></td> +<td>令<code>iter</code>指示容器中的上一个元素</td> +</tr> +<tr> +<td><code>iter1 == iter2</code></td> +<td>判断两个迭代器是否相等</td> +</tr> +</tbody> +</table> +<blockquote> +<p>泛型编程:尽量使用 <code>!=</code> 来对迭代器进行判断</p> +</blockquote> +<p>迭代器也拥有自己的类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="p">;</span> <span class="c1">// it 是 vector&lt;int&gt; 类型的迭代器,可以读写元素 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">const_iterator</span> <span class="n">it2</span><span class="p">;</span> <span class="c1">// it2 只能读,不能写 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果容器中的值为常量,则 <code>begin</code> 和 <code>end</code> 返回 <code>const_iterator</code>,否则返回 <code>iterator</code>。</p> +<p>解引用和成员访问:解引用迭代器可以获得迭代器所指的对象,如果该对象是一个类,则可以进一步访问其成员:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">{</span><span class="s">&#34;str1&#34;</span><span class="p">,</span> <span class="s">&#34;str2&#34;</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">ls</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> <span class="c1">// s 为 &#34;str1&#34; +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">).</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 解引用访问 string 成员 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">empty</span><span class="p">();</span> <span class="c1">// 作用同上 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="迭代器运算"> + <a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e8%bf%90%e7%ae%97">#</a> + 迭代器运算 +</h3><p><code>string</code> 和 <code>vector</code> 的迭代器提供了额外的运算符,支持迭代器的关系运算和跨过多个元素,这些运算称为 <strong>迭代器运算(iterator arithmetic)</strong>:</p> +<table> +<thead> +<tr> +<th>运算符</th> +<th>解释</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>iter + n</code></td> +<td>迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter - n</code></td> +<td>迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。</td> +</tr> +<tr> +<td><code>iter1 += n</code></td> +<td>迭代器加法的复合赋值语句,将<code>iter1</code>加n的结果赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 -= n</code></td> +<td>迭代器减法的复合赋值语句,将<code>iter2</code>减n的加过赋给<code>iter1</code></td> +</tr> +<tr> +<td><code>iter1 - iter2</code></td> +<td>两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。</td> +</tr> +<tr> +<td><code>&gt;</code>、<code>&gt;=</code>、<code>&lt;</code>、<code>&lt;=</code></td> +<td>迭代器的关系运算符,如果某迭代器</td> +</tr> +</tbody> +</table> +<p>当两个迭代器指向同一个容器时,它们可以进行加减操作得到距离,这个距离的类型为 <code>difference_type</code> 类型,是带符号整数型。</p> +<h2 id="数组"> + <a href="#%e6%95%b0%e7%bb%84">#</a> + 数组 +</h2><p>数组可以看做 <code>vector</code> 的低配版,其 <strong>长度固定</strong>。</p> +<h3 id="定义和初始化内置数组"> + <a href="#%e5%ae%9a%e4%b9%89%e5%92%8c%e5%88%9d%e5%a7%8b%e5%8c%96%e5%86%85%e7%bd%ae%e6%95%b0%e7%bb%84">#</a> + 定义和初始化内置数组 +</h3><p>数组的声明和定义形如 <code>a[d]</code>,其中 <code>a</code> 是数组的名字,<code>d</code> 是数组的维度(大于 0):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="n">cnt2</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">arr2</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 含有 10 个整型指针的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr3</span><span class="p">[</span><span class="n">cnt</span><span class="p">];</span> <span class="c1">// 报错,cnt 非常量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr4</span><span class="p">[</span><span class="n">cnt2</span><span class="p">];</span> <span class="c1">// 含有 42 和整数的数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">arr5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> <span class="c1">// 自动计算长度的数组 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>字符数组具有一定特殊性,使用字符串初始化字符数组时在结尾处必须增加一个空字符:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">arr2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span> <span class="c1">// 报错,长度不够 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">a2</span><span class="p">[]</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组来初始化数组 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// 报错,不能用数组进行赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="访问数组元素"> + <a href="#%e8%ae%bf%e9%97%ae%e6%95%b0%e7%bb%84%e5%85%83%e7%b4%a0">#</a> + 访问数组元素 +</h3><p>数组的下标为 <code>size_t</code> 类型,是一种机器相关的无符号类型,它被设计得足够大以便能够表示内存中任意对象的大小。</p> +<blockquote> +<p>下标存在越界导致缓冲区溢出等情况,这种情况需要程序员自行检查。</p> +</blockquote> +<h3 id="指针和数组"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c%e6%95%b0%e7%bb%84">#</a> + 指针和数组 +</h3><p>使用数组时,编译器会将其转换成指针。使用取地址符可以获取数组的元素的指针,如果是取数组的指针,则默认返回数组第一个元素的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">ls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ls</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// 数组的元素的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span> <span class="c1">// 等价于 *p2 = &amp;ls[0] +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="c-风格字符串"> + <a href="#c-%e9%a3%8e%e6%a0%bc%e5%ad%97%e7%ac%a6%e4%b8%b2">#</a> + <code>C</code> 风格字符串 +</h3><p>字符串字面值是一种通用结构的实例,这种结构是 <code>C++</code> 由 <code>C</code> 继承而来的 <strong><code>C</code> 风格字符串(C-style character string)</strong> 。 +按此习惯书写的字符串存放在字符数组中并以 <strong>空字符结束(null terminated)</strong>。</p> +<blockquote> +<p>在 <code>C++</code> 程序中尽量不要使用 <code>C</code> 风格字符串,容易引起安全漏洞且不方便。</p> +</blockquote> +<p>C标准库String函数,定义在<code>&lt;cstring&gt;</code> 中:</p> +<table> +<thead> +<tr> +<th>函数</th> +<th>介绍</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>strlen(p)</code></td> +<td>返回<code>p</code>的长度,空字符不计算在内</td> +</tr> +<tr> +<td><code>strcmp(p1, p2)</code></td> +<td>比较<code>p1</code>和<code>p2</code>的相等性。如果<code>p1==p2</code>,返回0;如果<code>p1&gt;p2</code>,返回一个正值;如果<code>p1&lt;p2</code>,返回一个负值。</td> +</tr> +<tr> +<td><code>strcat(p1, p2)</code></td> +<td>将<code>p2</code>附加到<code>p1</code>之后,返回<code>p1</code></td> +</tr> +<tr> +<td><code>strcpy(p1, p2)</code></td> +<td>将<code>p2</code>拷贝给<code>p1</code>,返回<code>p1</code></td> +</tr> +</tbody> +</table> +<h2 id="多维数组"> + <a href="#%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 多维数组 +</h2><p>严格来说,<code>C++</code> 语言中没有多维数组,所谓的多维数组实际是数组的数组。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">arr</span><span class="p">[</span><span class="mi">10</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// 长度为 10 的数组,其中每个元素是长度为 20 的数组,且都初始化为 0 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="使用范围-for-语句处理多维数组"> + <a href="#%e4%bd%bf%e7%94%a8%e8%8c%83%e5%9b%b4-for-%e8%af%ad%e5%8f%a5%e5%a4%84%e7%90%86%e5%a4%9a%e7%bb%b4%e6%95%b0%e7%bb%84">#</a> + 使用范围 <code>for</code> 语句处理多维数组 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">row</span><span class="p">:</span> <span class="n">arr</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="nl">col</span><span class="p">:</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">col</span> <span class="o">=</span> <span class="n">cnt</span><span class="p">;</span> <span class="n">cnt</span> <span class="o">++</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + C++ Primer Ch02 + https://3000ye.com/p/c-primer-ch02/ + Mon, 06 Nov 2023 22:34:55 +0800 + + https://3000ye.com/p/c-primer-ch02/ + <img src="https://3000ye.com/p/c-primer-ch02/assets/c++primer.jpg" alt="Featured image of post C++ Primer Ch02" /><h1 id="变量和基本类型"> + <a href="#%e5%8f%98%e9%87%8f%e5%92%8c%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b">#</a> + 变量和基本类型 +</h1><p>变量提供一个具名的、可供程序操作的存储空间。<code>C++</code>中每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。</p> +<h2 id="变量的声明和定义"> + <a href="#%e5%8f%98%e9%87%8f%e7%9a%84%e5%a3%b0%e6%98%8e%e5%92%8c%e5%ae%9a%e4%b9%89">#</a> + 变量的声明和定义 +</h2><p>为了允许把程序拆分成多个逻辑部分来编写,<code>C++</code>语言支持<strong>分离式编译(separate complication)</strong> 机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。</p> +<p>为了支持分离式编译,<code>C++</code>语言将声明和定义区分开来。<strong>声明(declaration)</strong> 使得名字为程序所致,一个文件如果想使用别处定义的的名字则必须包含对那个名字的声明。而 <strong>定义(definition)</strong> 负责创建与名字关联的实体。</p> +<p>变量声明规定了变量的类型和名字,定义在此基础上,还申请存储空间,甚至可能会为变量赋一个初始值。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 声明 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义并赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义而非声明,extern失效 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p><strong>注意:</strong> 变量只能被定义一次,但可以被多次声明。因此在多个文件中使用同一个变量名时,需要多次声明,但只能有且仅在一个文件中定义。</p> +<h3 id="标识符"> + <a href="#%e6%a0%87%e8%af%86%e7%ac%a6">#</a> + 标识符 +</h3><p><code>C++</code>的 <strong>标识符(identifier)</strong> 由字母、数字和下划线组成,其中必须以字母或下划线开头,没有长度限制,但对大小写敏感。</p> +<h3 id="作用域"> + <a href="#%e4%bd%9c%e7%94%a8%e5%9f%9f">#</a> + 作用域 +</h3><p><strong>作用域(scope)</strong> 是程序的一部分,在其中名字有特定的含义,<code>C++</code>语言中大多数作用域都以花括号分隔。</p> +<p>作用域能彼此包含,被包含(嵌套)的作用域称为 <strong>内层作用域(inner scope)</strong>,包含着别的作用域的作用域称为 <strong>外层作用域(outer scope)</strong>。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,同时允许在内层作用域中重新定义外层作用域已有的名字。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// 全局作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">inner</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span> <span class="c1">// 内层作用域 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用全局变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">outer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 局部重新定义,覆盖全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 使用局部变量输出 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 显式访问全局变量 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">::</span><span class="n">outer</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">inner</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="复合类型"> + <a href="#%e5%a4%8d%e5%90%88%e7%b1%bb%e5%9e%8b">#</a> + 复合类型 +</h2><p><strong>复合类型(compound type)</strong> 是指基于其他类型定义的类型,主要介绍引用和指针。</p> +<h3 id="引用"> + <a href="#%e5%bc%95%e7%94%a8">#</a> + 引用 +</h3><p><strong>引用(reference)</strong> 为对象起了另外一个名字,定义引用时,程序把引用和它的初始值 <strong>绑定(bind)</strong> 在一起,而不是将初始者拷贝给引用。</p> +<blockquote> +<p>引用并非对象,相反的,它只是为一个已经存在的对象所起的另一个名字。</p> +</blockquote> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;iostream&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 引用 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 修改 y 的值,实际是修改 x 的值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针"> + <a href="#%e6%8c%87%e9%92%88">#</a> + 指针 +</h3><p><strong>指针(pointer)</strong> 是“指向(point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问,但指针还有很多不同点:</p> +<ul> +<li>指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。</li> +<li>指针无须在定义时赋初值,和其他内置类型一样,没有赋初值时将拥有一个不确定值。</li> +</ul> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ip1</span><span class="p">,</span> <span class="o">*</span><span class="n">ip2</span><span class="p">;</span> <span class="c1">// ip1 和 ip2 都是指向 int 类型对象的指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">double</span> <span class="n">dp</span><span class="p">,</span> <span class="o">*</span><span class="n">dp2</span><span class="p">;</span> <span class="c1">// dp2 是指向 double 类型对象的指针,dp 是 double 类型对象 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>指针存放某个对象的地址,想获取该地址,需要使用 <strong>取地址符(<code>&amp;</code>)</strong>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// p 存放变量 val 的地址,或者说 p 是指向变量 val 的指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>因为引用不是对象,没有实际地址,因此不能定义指向引用的指针。</p> +</blockquote> +<p>指针的值(即地址)应属于下列 4 种状态之一:</p> +<ul> +<li>指向一个对象。</li> +<li>指向紧邻对象所占空间的下一个位置。</li> +<li>空指针,意味着指针没有指向任何对象。</li> +<li>无效指针,也就是上述情况之外的其他值。</li> +</ul> +<p>如果指针指向了一个对象,则允许使用 <strong>解引用符(<code>*</code>)</strong> 来访问该对象:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">p</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="c1">// 由符号 * 得到指针 p 所指的对象,输出 42 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>解引用操作仅适用于有效指针,无效指针无法解引用。</p> +</blockquote> +<p><strong>空指针(null pointer)</strong> 不指向任何对象,在试图使用一个指针之前代码可以先检查其是否为空。</p> +<p>生成空指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> <span class="c1">// 等价于 int *p1 = 0 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中引用本身并非是一个对象。定义引用之后,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。</p> +<p>指针没有这种限制,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi 为空指针 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">;</span> <span class="c1">// pi2 存放 i 的地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="o">*</span><span class="n">pi3</span><span class="p">;</span> <span class="c1">// pi3 的值无法确定 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="n">pi3</span> <span class="o">=</span> <span class="n">pi2</span><span class="p">;</span> <span class="c1">// pi3 和 pi2 指向同一个对象 i +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// pi2 变为空指针 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="const-限定符"> + <a href="#const-%e9%99%90%e5%ae%9a%e7%ac%a6">#</a> + const 限定符 +</h2><p>有时我们想定义这样一种变量,它的值不能被改变,这在程序运行过程中对于某些特定值非常有用。为了满足这一要求,可以使用关键字<code>const</code>对变量的类型加以限定:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> <span class="c1">// 限定缓冲区大小为 512 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>默认情况下,<code>const</code>对象被设定为仅在单个文件内有效。当多个文件中出现了同名的<code>const</code>变量时,其等同于在多个文件中分别定义了独立的变量。但当我们需要其在多个文件中保持一致时,需要在定义和声明前面都加上<code>extern</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 定义文件 xxx.cpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 声明文件 xxx.hpp +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">bufferSize</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="指针和-const"> + <a href="#%e6%8c%87%e9%92%88%e5%92%8c-const">#</a> + 指针和 const +</h3><p>与引用一样,也可以令指针指向常量与非常量。<strong>指向常量的指针(pointer to const)</strong> 不能用于改变其所指对象的值。要想存放常量对象的地址,必须使用指向常量的指针:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span> <span class="c1">// val 为常量对像,其值不能改变 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">pi</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 报错,pi 为普通指针,不能存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span> <span class="c1">// 使用指向常量的指针存放常量对象地址 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">*</span><span class="n">pi2</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// 报错,不能给指向常量的指针赋值 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="处理类型"> + <a href="#%e5%a4%84%e7%90%86%e7%b1%bb%e5%9e%8b">#</a> + 处理类型 +</h2><p>随着程序越来越复杂,其中使用的变量类型也越复杂,如何处理这些类型成为一个问题。</p> +<h3 id="类型别名"> + <a href="#%e7%b1%bb%e5%9e%8b%e5%88%ab%e5%90%8d">#</a> + 类型别名 +</h3><p><strong>类型别名(type alias)</strong> 是一个名字,它是某种类型的同义词。</p> +<p>使用关键字<code>typedef</code>定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用 <strong>别名声明(alias declaration)</strong> 定义类型别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="c1">// ll 是 long long 的同义词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="auto-类型说明符"> + <a href="#auto-%e7%b1%bb%e5%9e%8b%e8%af%b4%e6%98%8e%e7%ac%a6">#</a> + auto 类型说明符 +</h3><p>编程时常常需要将表达式的结果赋给变量,这就要求需要事先知道结果的类型。但是要做到这一点有时并不容易,因此<code>C++11</code>引入了<code>auto</code>类型说明符,它能自动分析表达式结果的类型。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">item</span> <span class="o">=</span> <span class="n">val1</span> <span class="o">+</span> <span class="n">val2</span><span class="p">;</span> <span class="c1">// item 初始化为 val1 和 val2 相加的结果 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>使用<code>auto</code>定义或声明多个变量时,所有变量的类型必须一致:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mf">4.2</span><span class="p">;</span> <span class="c1">// 报错,同一行的变量类型必须一致 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="decltype-类型指示符"> + <a href="#decltype-%e7%b1%bb%e5%9e%8b%e6%8c%87%e7%a4%ba%e7%ac%a6">#</a> + decltype 类型指示符 +</h3><p>有时会遇到这种情况:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值初始化变量——即只使用表达式的数据类型,不使用表达式的结果。</p> +<p>因此<code>C++11</code>引入了<code>decltype</code>类型指示符,它的作用是返回操作数的数据类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">decltype</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// z 为 int 类型 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h2 id="自定义数据结构"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84">#</a> + 自定义数据结构 +</h2><p>内置的数据类型并不能满足所有的需求,因此<code>c++</code>提供了自定义数据类型的方式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 定义 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">jack</span><span class="p">{</span><span class="s">&#34;jack&#34;</span><span class="p">,</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">};</span> +</span></span><span class="line"><span class="cl"><span class="c1">// 先声明后赋值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">student</span> <span class="n">castor</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="n">castor</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;castor&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">sex</span> <span class="o">=</span> <span class="s">&#34;m&#34;</span><span class="p">,</span> <span class="n">castor</span><span class="p">.</span><span class="n">gender</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="自定义数据结构使用别名"> + <a href="#%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%bd%bf%e7%94%a8%e5%88%ab%e5%90%8d">#</a> + 自定义数据结构使用别名 +</h3><p>和内置数据类型一样,自定义数据结构也能使用别名:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="n">studeng</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 或直接在定义时使用别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="n">stu</span> <span class="o">=</span> <span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="编写自己的头文件"> + <a href="#%e7%bc%96%e5%86%99%e8%87%aa%e5%b7%b1%e7%9a%84%e5%a4%b4%e6%96%87%e4%bb%b6">#</a> + 编写自己的头文件 +</h3><p>当我们在编写头文件时,会引入其他头文件,而在生产文件中,又会再次引入这些头文件。这样就导致一个问题,某些头文件被重复引入了。因此,在编写头文件时需要做一定的保护措施:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// studeng.h +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp">#define STUDENT_H +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;string&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">student</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">sex</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">gender</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="cp">#endif +</span></span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/cpp/page/1/index.html b/tags/cpp/page/1/index.html new file mode 100644 index 0000000..85cd9ea --- /dev/null +++ b/tags/cpp/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/cpp/ + + + + + + diff --git a/tags/cpp/page/2/index.html b/tags/cpp/page/2/index.html new file mode 100644 index 0000000..95c4bc8 --- /dev/null +++ b/tags/cpp/page/2/index.html @@ -0,0 +1,611 @@ + + + + +Tag: CPP - Pager 2 - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

11 pages

+

CPP

+ +
+
+
+ +
+ + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/cppjson/index.html b/tags/cppjson/index.html new file mode 100644 index 0000000..cac9aba --- /dev/null +++ b/tags/cppjson/index.html @@ -0,0 +1,556 @@ + + + + +Tag: CppJson - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

2 pages

+

CppJson

+ +
+
+
+ +
+ + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/cppjson/index.xml b/tags/cppjson/index.xml new file mode 100644 index 0000000..9c8a904 --- /dev/null +++ b/tags/cppjson/index.xml @@ -0,0 +1,637 @@ + + + + CppJson on 3000ye's Blog + https://3000ye.com/tags/cppjson/ + Recent content in CppJson on 3000ye's Blog + Hugo -- gohugo.io + en-us + Mon, 01 Jan 2024 23:18:32 +0800 + CppJson Ch02 + https://3000ye.com/p/cppjson-ch02/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/p/cppjson-ch02/ + <img src="https://3000ye.com/p/cppjson-ch02/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch02" /><h1 id="cppjson-第二章节number-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%ba%8c%e7%ab%a0%e8%8a%82number-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第二章节:<code>number</code> 值解析 +</h1> + + + CppJson Ch01 + https://3000ye.com/p/cppjson-ch01/ + Sat, 16 Dec 2023 23:33:44 +0800 + + https://3000ye.com/p/cppjson-ch01/ + <img src="https://3000ye.com/p/cppjson-ch01/assets/JSON-Tutorial.jpg" alt="Featured image of post CppJson Ch01" /><h1 id="cppjson-第一章节自动测试null-和-bool-值解析"> + <a href="#cppjson-%e7%ac%ac%e4%b8%80%e7%ab%a0%e8%8a%82%e8%87%aa%e5%8a%a8%e6%b5%8b%e8%af%95null-%e5%92%8c-bool-%e5%80%bc%e8%a7%a3%e6%9e%90">#</a> + <code>CppJson</code> 第一章节:自动测试,<code>NULL</code> 和 <code>bool</code> 值解析 +</h1><h2 id="json-是什么"> + <a href="#json-%e6%98%af%e4%bb%80%e4%b9%88">#</a> + JSON 是什么 +</h2><p>JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为<a class="link" href="https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf" target="_blank" rel="noopener" + >ECMA-404</a>。</p> +<p>虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。</p> +<p>例如,一个动态网页想从服务器获得数据时,服务器从数据库查找数据,然后把数据转换成 JSON 文本格式:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;title&#34;</span><span class="o">:</span> <span class="s2">&#34;Design Patterns&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;subtitle&#34;</span><span class="o">:</span> <span class="s2">&#34;Elements of Reusable Object-Oriented Software&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;author&#34;</span><span class="o">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Erich Gamma&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Richard Helm&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Ralph Johnson&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;John Vlissides&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;year&#34;</span><span class="o">:</span> <span class="mi">2009</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;weight&#34;</span><span class="o">:</span> <span class="mf">1.8</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;hardcover&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;publisher&#34;</span><span class="o">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Company&#34;</span><span class="o">:</span> <span class="s2">&#34;Pearson Education&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;Country&#34;</span><span class="o">:</span> <span class="s2">&#34;India&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;website&#34;</span><span class="o">:</span> <span class="kc">null</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。</p> +<p>从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:</p> +<ul> +<li>null: 表示为 null</li> +<li>boolean: 表示为 true 或 false</li> +<li>number: 一般的浮点数表示方式,在下一单元详细说明</li> +<li>string: 表示为 &ldquo;&hellip;&rdquo;</li> +<li>array: 表示为 [ &hellip; ]</li> +<li>object: 表示为 { &hellip; }</li> +</ul> +<p>我们要实现的 JSON 库,主要是完成 3 个需求:</p> +<ol> +<li>把 JSON 文本解析为一个树状数据结构(parse)。</li> +<li>提供接口访问该数据结构(access)。</li> +<li>把数据结构转换成 JSON 文本(stringify)。</li> +</ol> +<p><img src="https://3000ye.com/p/cppjson-ch01/assets/requirement.png" + width="440" + height="78" + srcset="https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_480x0_resize_box_3.png 480w, https://3000ye.com/p/cppjson-ch01/assets/requirement_hu7ab85b9bc4611a543cc4532090c89f34_7375_1024x0_resize_box_3.png 1024w" + loading="lazy" + + + class="gallery-image" + data-flex-grow="564" + data-flex-basis="1353px" + +></p> +<p>我们会逐步实现这些需求。在本章节中,我们只实现最简单的 null 和 boolean 解析。</p> +<h2 id="搭建编译环境"> + <a href="#%e6%90%ad%e5%bb%ba%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#</a> + 搭建编译环境 +</h2><p>我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。</p> +<p>我们的 JSON 库名为 CppJson,代码文件只有 3 个:</p> +<ol> +<li><code>include/cppjson.hpp</code>:CppJson 的头文件(header file),含有对外的类型和 API 函数声明。</li> +<li><code>cppjson.cpp</code>:CppJson 的实现文件(implementation file),含有内部的类型声明和函数实现。此文件会编译成库。</li> +<li><code>cppjsonTest.cpp</code>:我们使用测试驱动开发(test driven development, TDD)。此文件包含测试程序,需要链接 CppJson 库。</li> +</ol> +<p>为了方便跨平台开发,我们会使用一个现时最流行的软件配置工具 <a class="link" href="https://cmake.org/" target="_blank" rel="noopener" + >CMake</a>。</p> +<p>在 OS X 平台中,在命令行通过命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir build +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build +</span></span><span class="line"><span class="cl">cmake -DCMAKE_BUILD_TYPE<span class="o">=</span>Debug .. +</span></span><span class="line"><span class="cl">make +</span></span></code></pre></td></tr></table> +</div> +</div><p>将 Debug 改成 Release 就会生成 Release 配置的 makefile。</p> +<p>在 Vscode 中,可以通过配置 <code>tasks.json</code> 文件来进行自动 build:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//.vscode/tasks.json +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;mkdir&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-p&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34;-DCMAKE_BUILD_TYPE=Debug&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="c1">//在此处添加其它CMAKE选项 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;..&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;make&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;-j16&#34;</span><span class="p">,],</span> <span class="c1">//根据机器cpu核心数量自行调整 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;cwd&#34;</span><span class="p">:</span> <span class="s2">&#34;${fileDirname}/build&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOrder&#34;</span><span class="p">:</span> <span class="s2">&#34;sequence&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;dependsOn&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;mkdirbuild&#34;</span><span class="p">,</span> <span class="s2">&#34;cmake&#34;</span><span class="p">,</span> <span class="s2">&#34;make&#34;</span><span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后执行 build 生成的文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ./build/cppjson_test_ch01 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">16/16 <span class="o">(</span>100.00%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。</p> +<h2 id="头文件与-api-设计"> + <a href="#%e5%a4%b4%e6%96%87%e4%bb%b6%e4%b8%8e-api-%e8%ae%be%e8%ae%a1">#</a> + 头文件与 API 设计 +</h2><p><code>Cpp</code> 语言有头文件的概念,需要使用 <code>#include</code>去引入头文件中的类型声明和函数声明。但由于头文件也可以 <code>#include</code> 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#pragma once +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如前所述,JSON 中有 6 种数据类型,如果把 true 和 false 当作两个类型就是 7 种,我们为此声明一个枚举类(enumeration calss):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonType</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NULL</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_TRUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_FALSE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_NUMBER</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_STRING</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_ARRAY</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">CPPJSON_OBJECT</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>接下来,我们声明 JSON 的数据结构。JSON 是一个树形结构,我们最终需要实现一个树的数据结构,每个节点使用 <code>cppjson_value</code> 结构体表示,我们会称它为一个 JSON 值(JSON value)。</p> +<p>在此单元中,我们只需要实现 <code>null</code>, <code>true</code> 和 <code>false</code> 的解析,因此该结构体只需要存储一个 <code>cppjsonType</code>,之后的单元会逐步加入其他数据。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjsonType</span> <span class="n">type</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_value</span><span class="p">;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后,我们现在只需要两个 API 函数,一个是解析 JSON:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>传入的 JSON 文本是一个 <code>string</code> 字符串,由于我们不应该改动这个输入字符串,所以使用 <code>const std::string</code> 类型。</p> +<p>返回值是以下这些枚举类中的值,无错误会返回 <code>cppjsonParseCode::OK</code>,其他值在下节解释。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">cppjsonParseCode</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">OK</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">INVALID_VALUE</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="n">ROOT_NOT_SINGULAR</span> +</span></span><span class="line"><span class="cl"><span class="p">};</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时我们只需要一个访问结果的函数,就是获取其类型:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">cppjsonType</span> <span class="nf">cppjson_get_type</span><span class="p">(</span><span class="k">const</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">);</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="json-语法子集"> + <a href="#json-%e8%af%ad%e6%b3%95%e5%ad%90%e9%9b%86">#</a> + JSON 语法子集 +</h2><p>下面是此单元的 JSON 语法子集,使用 <a class="link" href="https://tools.ietf.org/html/rfc7159" target="_blank" rel="noopener" + >RFC7159</a> 中的 <a class="link" href="https://tools.ietf.org/html/rfc5234" target="_blank" rel="noopener" + >ABNF</a> 表示:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">JSON-text</span> <span class="err">=</span> <span class="err">ws</span> <span class="err">value</span> <span class="err">ws</span> +</span></span><span class="line"><span class="cl"><span class="err">ws</span> <span class="err">=</span> <span class="err">*(%x</span><span class="mi">20</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">09</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">A</span> <span class="err">/</span> <span class="err">%x</span><span class="mi">0</span><span class="err">D)</span> +</span></span><span class="line"><span class="cl"><span class="err">value</span> <span class="err">=</span> <span class="kc">null</span> <span class="err">/</span> <span class="kc">false</span> <span class="err">/</span> <span class="kc">true</span> +</span></span><span class="line"><span class="cl"><span class="kc">null</span> <span class="err">=</span> <span class="s2">&#34;null&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">false</span> <span class="err">=</span> <span class="s2">&#34;false&#34;</span> +</span></span><span class="line"><span class="cl"><span class="kc">true</span> <span class="err">=</span> <span class="s2">&#34;true&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>当中 <code>%xhh</code> 表示以 16 进制表示的字符,<code>/</code> 是多选一,<code>*</code> 是零或多个,<code>()</code> 用于分组。</p> +<p>那么第一行的意思是,JSON 文本由 3 部分组成,首先是空白(whitespace),接着是一个值,最后是空白。</p> +<p>第二行告诉我们,所谓空白,是由零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)所组成。</p> +<p>第三行是说,我们现时的值只可以是 <code>null</code>、<code>false</code> 或 <code>true</code>,它们分别有对应的字面值(literal)。</p> +<p>我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法,我们要产生对应的错误码,方便使用者追查问题。</p> +<p>在这个 JSON 语法子集下,我们定义 3 种错误码:</p> +<ul> +<li>若一个 JSON 只含有空白,传回 <code>LEPT_PARSE_EXPECT_VALUE</code>。</li> +<li>若一个值之后,在空白之后还有其他字符,传回 <code>LEPT_PARSE_ROOT_NOT_SINGULAR</code>。</li> +<li>若值不是那三种字面值,传回 <code>LEPT_PARSE_INVALID_VALUE</code>。</li> +</ul> +<h2 id="单元测试"> + <a href="#%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95">#</a> + 单元测试 +</h2><p>许多同学在做练习题时,都是以 <code>printf</code>/<code>cout</code> 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。</p> +<p>常用的单元测试框架有 xUnit 系列,如 C++ 的 <a class="link" href="https://github.com/google/googletest" target="_blank" rel="noopener" + >Google Test</a>、C# 的 <a class="link" href="https://www.nunit.org/" target="_blank" rel="noopener" + >NUnit</a>。我们为了简单起见,会编写一个极简单的单元测试方式。</p> +<p>一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:</p> +<ol> +<li>加入一个测试。</li> +<li>运行所有测试,新的测试应该会失败。</li> +<li>编写实现代码。</li> +<li>运行所有测试,若有测试失败回到3。</li> +<li>重构代码。</li> +<li>回到 1。</li> +</ol> +<p>TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试,而不会写了一些不需要的代码,或是没有被测试的代码。</p> +<p>但无论我们是采用 TDD,或是先实现后测试,都应尽量加入足够覆盖率的单元测试。</p> +<p>回到 CppJson 项目,<code>cppjsonTest.cpp</code> 包含了一个极简的单元测试框架:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;include/cppjson.hpp&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;typeinfo&gt;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">main_ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="n">test_pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 测试宏接口 +</span></span></span><span class="line"><span class="cl"><span class="c1">// flag 表示测试点是否通过,如果未通过则打印异常信息 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define EXPECT_BASE(flag, expect, actual) \ +</span></span></span><span class="line"><span class="cl"><span class="cp"> do {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> test_count ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> if (flag) test_pass ++;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> else {\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> fprintf(stderr, &#34;%s:%d, expect = %s(%d), actual = %s(%d)\n&#34;, __FILE__, __LINE__, typeid(expect).name(), static_cast&lt;int&gt;(expect), typeid(actual).name(), static_cast&lt;int&gt;(actual));\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> main_ret = 1;\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> }\ +</span></span></span><span class="line"><span class="cl"><span class="cp"> } while(0) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="cp">#define EXPECT_TYPE(expect, actual) EXPECT_BASE((expect) == (actual), expect, actual) +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse_null</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_value</span> <span class="n">v</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_FALSE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">,</span> <span class="n">cppjson_parse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">,</span> <span class="s">&#34;null&#34;</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"> <span class="n">EXPECT_TYPE</span><span class="p">(</span><span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">,</span> <span class="n">cppjson_get_type</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v</span><span class="p">));</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">test_parse</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse_null</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// ... +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">test_parse</span><span class="p">();</span> +</span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d/%d (%3.2f%%) passed</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">test_pass</span><span class="p">,</span> <span class="n">test_count</span><span class="p">,</span> <span class="n">test_pass</span> <span class="o">*</span> <span class="mf">100.0</span> <span class="o">/</span> <span class="n">test_count</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">main_ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>现时只提供了一个 <code>EXPECT_TYPE(expect, actual)</code> 的宏,每次使用这个宏时,如果 expect != actual(预期值不等于实际值),便会输出错误信息。</p> +<p>若按照 TDD 的步骤,我们先写一个测试,如上面的 <code>test_parse_null()</code>,而 <code>cppjson_parse()</code> 只返回 <code>cppjsonParseCode::OK</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/CppJson/ch01/cppjsonTest.cpp:30, <span class="nv">expect</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>0<span class="o">)</span>, <span class="nv">actual</span> <span class="o">=</span> 16cppjsonParseCode<span class="o">(</span>2<span class="o">)</span> +</span></span><span class="line"><span class="cl">15/16 <span class="o">(</span>93.75%<span class="o">)</span> passed +</span></span></code></pre></td></tr></table> +</div> +</div><p>为通过的测试是因为 <code>cppjson_parse()</code> 没有把 <code>v.type</code> 改成 <code>cppjsonType::CPPJSON_NULL</code>,造成失败。我们再实现 <code>lept_parse()</code> 令到它能通过测试。</p> +<p>然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。</p> +<h2 id="实现解析器"> + <a href="#%e5%ae%9e%e7%8e%b0%e8%a7%a3%e6%9e%90%e5%99%a8">#</a> + 实现解析器 +</h2><p>有了 API 的设计、单元测试,终于要实现解析器了。</p> +<p>首先为了减少解析函数之间传递多个参数,我们把这些数据都放进一个 <code>cppjson_context</code> 结构体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">cppjson_context</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// json 解析函数:ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse</span><span class="p">(</span><span class="n">cppjson_value</span> <span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cppjson_context</span> <span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 分别解析 ws1 value ws2 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">cppjson_parse_value</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span> <span class="o">?</span> <span class="n">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">ret</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>CppJson 是一个手写的递归下降解析器(recursive descent parser)。由于 JSON 语法特别简单,我们不需要写分词器(tokenizer),只需检测下一个字符,便可以知道它是哪种类型的值,然后调用相关的分析函数。对于完整的 JSON 语法,跳过空白后,只需检测当前字符:</p> +<ul> +<li>n ➔ null</li> +<li>t ➔ true</li> +<li>f ➔ false</li> +<li>&quot; ➔ string</li> +<li>0-9/- ➔ number</li> +<li>[ ➔ array</li> +<li>{ ➔ object</li> +</ul> +<p>所以,我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span><span class="lnt">37 +</span><span class="lnt">38 +</span><span class="lnt">39 +</span><span class="lnt">40 +</span><span class="lnt">41 +</span><span class="lnt">42 +</span><span class="lnt">43 +</span><span class="lnt">44 +</span><span class="lnt">45 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39; &#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\t&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span> <span class="n">or</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\r&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span> <span class="o">++</span><span class="p">;</span> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 ws2 之后是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_root_not_singular</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="c1">// 删除空白符 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">cppjson_parse_whitespace</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="c1">// 判断是否还有非空值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">ROOT_NOT_SINGULAR</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 检测 null 值 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_null</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">head</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">head</span> <span class="o">!=</span> <span class="s">&#34;null&#34;</span><span class="p">)</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> +</span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">cppjsonType</span><span class="o">::</span><span class="n">CPPJSON_NULL</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">OK</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1">// 解析 value +</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">cppjsonParseCode</span> <span class="nf">cppjson_parse_value</span><span class="p">(</span><span class="n">cppjson_context</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="n">cppjson_value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">json</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;n&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_null</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;t&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_true</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;f&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjson_parse_false</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> +</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;\0&#39;</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">EXPECT_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="n">cppjsonParseCode</span><span class="o">::</span><span class="n">INVALID_VALUE</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/cppjson/page/1/index.html b/tags/cppjson/page/1/index.html new file mode 100644 index 0000000..125a3d5 --- /dev/null +++ b/tags/cppjson/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/cppjson/ + + + + + + diff --git a/tags/data-structure/index.html b/tags/data-structure/index.html new file mode 100644 index 0000000..b17b87f --- /dev/null +++ b/tags/data-structure/index.html @@ -0,0 +1,536 @@ + + + + +Tag: Data Structure - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Data Structure

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/data-structure/index.xml b/tags/data-structure/index.xml new file mode 100644 index 0000000..4c4f5ab --- /dev/null +++ b/tags/data-structure/index.xml @@ -0,0 +1,49 @@ + + + + Data Structure on 3000ye's Blog + https://3000ye.com/tags/data-structure/ + Recent content in Data Structure on 3000ye's Blog + Hugo -- gohugo.io + en-us + Wed, 21 Feb 2024 19:14:32 +0800 + Linked List + https://3000ye.com/p/linked-list/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/p/linked-list/ + <img src="https://3000ye.com/p/linked-list/assets/dataStructures.jpg" alt="Featured image of post Linked List" /><h1 id="数据结构链表"> + <a href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e9%93%be%e8%a1%a8">#</a> + 数据结构:链表 +</h1><h2 id="线性表"> + <a href="#%e7%ba%bf%e6%80%a7%e8%a1%a8">#</a> + 线性表 +</h2><p>线性表是具有<strong>相同</strong>数据类型的 $n(n \ge 0)$ 个<strong>数据元素</strong>的<strong>有限序列</strong>,其中 $n$ 为<strong>表长</strong>,当 $n = 0$ 时线性表是一个<strong>空表</strong>。若用 $L$ 命名线性表,则其一般表示为:</p> +<p>$$ +L = (a_1, a_2, \cdots, a_i, a_{i + 1}, \cdots, a_n) +$$</p> +<p>几个概念:</p> +<ul> +<li>$a_i$ 是线性表中的“第 $i$ 个”元素线性表中的<strong>位序</strong>,位序从 $1$ 开始,数组下标从 $0$ 开始。</li> +<li>$a_1$ 是<strong>表头元素</strong>,$a_n$ 是<strong>表尾元素</strong>。</li> +</ul> +<p>除第一个元素外,每个元素有且仅有一个<strong>直接前驱</strong>;除最后一个元素外,每个元素有且仅有一个<strong>直接后继</strong>。</p> +<p>线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。我们常用的数组就是一种典型的顺序存储结构。</p> +<h2 id="链表"> + <a href="#%e9%93%be%e8%a1%a8">#</a> + 链表 +</h2><p>链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。</p> +<p>这种存储方式的优点是定点插入和定点删除的时间复杂度为 $O(1)$,缺点是访问的时间复杂度最坏为 $O(n)$。</p> +<p>链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。</p> +<h3 id="单向链表"> + <a href="#%e5%8d%95%e5%90%91%e9%93%be%e8%a1%a8">#</a> + 单向链表 +</h3><p>单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。</p> +<div style='display: flex; justify-content: center;'> +<img src='https://oi-wiki.org/ds/images/list.svg' alt='img' style='zoom:100%;' /> +</div> + + + + + diff --git a/tags/data-structure/page/1/index.html b/tags/data-structure/page/1/index.html new file mode 100644 index 0000000..172aff7 --- /dev/null +++ b/tags/data-structure/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/data-structure/ + + + + + + diff --git a/tags/dhu/index.html b/tags/dhu/index.html new file mode 100644 index 0000000..80c5265 --- /dev/null +++ b/tags/dhu/index.html @@ -0,0 +1,556 @@ + + + + +Tag: DHU - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

2 pages

+

DHU

+ +
+
+
+ +
+ + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/dhu/index.xml b/tags/dhu/index.xml new file mode 100644 index 0000000..2725045 --- /dev/null +++ b/tags/dhu/index.xml @@ -0,0 +1,520 @@ + + + + DHU on 3000ye's Blog + https://3000ye.com/tags/dhu/ + Recent content in DHU on 3000ye's Blog + Hugo -- gohugo.io + en-us + Fri, 16 Feb 2024 23:33:47 +0800 + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + dhuBachelor + https://3000ye.com/p/dhubachelor/ + Thu, 12 Oct 2023 16:41:55 +0800 + + https://3000ye.com/p/dhubachelor/ + <img src="https://3000ye.com/p/dhubachelor/assets/latex.jpg" alt="Featured image of post dhuBachelor" /><h1 id="东华大学学士毕业论文-latex-模板使用手册"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e5%a3%ab%e6%af%95%e4%b8%9a%e8%ae%ba%e6%96%87-latex-%e6%a8%a1%e6%9d%bf%e4%bd%bf%e7%94%a8%e6%89%8b%e5%86%8c">#</a> + 东华大学学士毕业论文 LaTeX 模板使用手册 +</h1><h2 id="导入模板"> + <a href="#%e5%af%bc%e5%85%a5%e6%a8%a1%e6%9d%bf">#</a> + 导入模板 +</h2><h3 id="使用-git-克隆仓库"> + <a href="#%e4%bd%bf%e7%94%a8-git-%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93">#</a> + 使用 Git 克隆仓库 +</h3><p>将仓库克隆到你需要的位置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@github.com:3000ye/dhuBachelor.git +</span></span><span class="line"><span class="cl"><span class="c1"># 或</span> +</span></span><span class="line"><span class="cl">git clone https://github.com/3000ye/dhuBachelor.git +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="下载-zip-文件"> + <a href="#%e4%b8%8b%e8%bd%bd-zip-%e6%96%87%e4%bb%b6">#</a> + 下载 Zip 文件 +</h3><p>如果你不会使用<code>Git</code>,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载<code>Zip</code>文件:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/zip.png" alt="img" style="zoom:100%"/> +</div> +<h3 id="微信公众号获取"> + <a href="#%e5%be%ae%e4%bf%a1%e5%85%ac%e4%bc%97%e5%8f%b7%e8%8e%b7%e5%8f%96">#</a> + 微信公众号获取 +</h3><p>如果你没有科学代理,无法进入<code>Github</code>,请扫码关注公众号:<code>3000ye Blog</code>然后回复<code>latex模板</code>获取百度网盘分享链接。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/weixin.jpg" alt="img" style="zoom:100%"/> +</div> +<h2 id="开始使用"> + <a href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8">#</a> + 开始使用 +</h2><h3 id="安装字体"> + <a href="#%e5%ae%89%e8%a3%85%e5%ad%97%e4%bd%93">#</a> + 安装字体 +</h3><p>在使用模板之前,请先找到并打开<code>fonts</code>文件夹,安装里面的所有字体。</p> +<h3 id="配置-tex-环境"> + <a href="#%e9%85%8d%e7%bd%ae-tex-%e7%8e%af%e5%a2%83">#</a> + 配置 TeX 环境 +</h3><p>如果你是 $\LaTeX$ 小白,请先行阅读:<a class="link" href="https://3000ye.com/p/elegant-latex/" target="_blank" rel="noopener" + >使用 LaTeX 优雅地完成创作</a>,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。</p> +<h3 id="尝试编译"> + <a href="#%e5%b0%9d%e8%af%95%e7%bc%96%e8%af%91">#</a> + 尝试编译 +</h3><p>使用你喜欢的编辑器,打开<code>dhuBachelor.tex</code>文件,选择<code>xelatex</code>命令编译文件。如果没有出现报错,同目录下会生成一个<code>dhuBachelor.pdf</code>文件,这就是我们的论文。</p> +<h2 id="各个组件说明"> + <a href="#%e5%90%84%e4%b8%aa%e7%bb%84%e4%bb%b6%e8%af%b4%e6%98%8e">#</a> + 各个组件说明 +</h2><p>看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。</p> +<p>本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。</p> +<h3 id="论文题目"> + <a href="#%e8%ae%ba%e6%96%87%e9%a2%98%e7%9b%ae">#</a> + 论文题目 +</h3><p>根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reTitle</span><span class="nb">{</span>中文题目<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\reTitleEN</span><span class="nb">{</span>英文题目<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="摘要"> + <a href="#%e6%91%98%e8%a6%81">#</a> + 摘要 +</h3><p>根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reAbstract</span> <span class="c">% 中文摘要 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reAbstractEN</span> <span class="c">% 英文摘要 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>摘要内容直接写在摘要下方,首行缩进两字符。</p> +<h3 id="关键词"> + <a href="#%e5%85%b3%e9%94%ae%e8%af%8d">#</a> + 关键词 +</h3><p>中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。</p> +<p>英文关键词:Times New Roman(标题加黑)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reKeyword</span><span class="nb">{</span>关键词1,关键词2,关键词3,关键词4<span class="nb">}</span> <span class="c">% 中文关键词 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reKeywordEN</span><span class="nb">{</span>Keyword1, Keyword2, Keyword3<span class="nb">}</span> <span class="c">% 英文关键词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="目录"> + <a href="#%e7%9b%ae%e5%bd%95">#</a> + 目录 +</h3><p>目录标题需居中显示,添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\tableofcontents</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="各级标题"> + <a href="#%e5%90%84%e7%ba%a7%e6%a0%87%e9%a2%98">#</a> + 各级标题 +</h3><p>规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reSection</span><span class="nb">{</span>一级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsection</span><span class="nb">{</span>二级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsubsection</span><span class="nb">{</span>三级标题<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="有序列表"> + <a href="#%e6%9c%89%e5%ba%8f%e5%88%97%e8%a1%a8">#</a> + 有序列表 +</h3><p>规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\orderedList</span><span class="nb">{</span> <span class="c">% 使用 (i) 排序,缩进 2 字符 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> <span class="c">% \par的作用是将内容换行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> +</span></span><span class="line"><span class="cl"> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="数学公式"> + <a href="#%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 数学公式 +</h3><p>行内公式:<code>$f(x) = x + 1$</code></p> +<p>跨行公式:跨行公式请使用<code>equation</code>环境,默认按照章节自动编号。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> x = a<span class="nb">_</span>0 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>1 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>2 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>3 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>4<span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改<code>width</code>大小和图片路径,并根据实际更改图例和索引。</p> +<p>注意:图片需要保存在<code>assets/</code>目录中才能被正确插入,读者可以自己新建其他目录实现插入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] <span class="c">% 图片位于文字下方 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> <span class="c">% 居中 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="c">% 设置图片占页面宽度的比例(默认0.8) +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片标题<span class="nb">}</span> <span class="c">% 图例,按章节编号 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>fig: 数据结构2<span class="nb">}</span> <span class="c">% 图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>有时可能需要多图并排,模板使用<code>minipage</code>来实现并排展示,使用时可以修改子图所占比例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\hspace</span><span class="nb">{</span>1em<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入表格"> + <a href="#%e6%8f%92%e5%85%a5%e8%a1%a8%e6%a0%bc">#</a> + 插入表格 +</h3><p>使用 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a> 工具生成表格代码后,需要手动添加分割线(<code>\toprule, \midrule, \bottomrule</code>),以达到三线表的格式要求。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||l<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> parameter <span class="nb">&amp;</span> Description <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">I</span><span class="s">$</span> <span class="nb">&amp;</span> Land area collection <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">J</span><span class="s">$</span> <span class="nb">&amp;</span> Flower pollination demand set <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">D_j</span><span class="s">$</span> <span class="nb">&amp;</span> Number of pollinating bees required for flower pollination <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">T_k</span><span class="s">$</span> <span class="nb">&amp;</span> Honeycomb size grade, <span class="s">$</span><span class="nb">k </span><span class="o">=</span><span class="nb"> </span><span class="m">1</span><span class="nb">, </span><span class="m">2</span><span class="nb">, </span><span class="nv">\cdots</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">B</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum number of hive <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">R_{ik}</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum influence radius of a single honeycomb <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 一个表<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>对于需要多表并排的情况,和图片的方式类似,使用<code>minipage</code>来实现:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格1标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格2标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bb%a3%e7%a0%81">#</a> + 插入代码 +</h3><p>可以直接在<code>.tex</code>文件中编写代码,并指定语言和标题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>lstlisting<span class="nb">}</span>[language=c++,title=<span class="nb">{</span>code.cpp<span class="nb">}</span>] +</span></span><span class="line"><span class="cl">#include &#34;bits/stdc++.h&#34; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">using namespace std; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">int main() <span class="nb">{</span> +</span></span><span class="line"><span class="cl"> cout &lt;&lt; &#34;3000ye 的 LaTeX 模板!&#34; &lt;&lt; endl; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> return 0; +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>lstlisting<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在<code>assets/</code>目录下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\lstinputlisting</span><span class="na">[language=c++, title=code.cpp]</span><span class="nb">{</span>code/code.cpp<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入伪代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bc%aa%e4%bb%a3%e7%a0%81">#</a> + 插入伪代码 +</h3><p>使用宏包<code>algorithm, algorithmic</code>来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>Example Pseudocode<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="m">0</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\IF</span> <span class="nb">{</span><span class="s">$</span><span class="nb">x</span><span class="nv">\leq</span><span class="nb"> </span><span class="m">0</span><span class="s">$</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">+</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ELSE</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">-</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ENDIF</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="参考文献"> + <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae">#</a> + 参考文献 +</h3><p>参考文献使用<code>\bibitem</code>来添加,添加时需要手动更改<code>{RNi}</code>索引(<code>i</code>是你文献的序号)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reference</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN1<span class="nb">}</span> 参考文献1 +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN2<span class="nb">}</span> 参考文献2 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="致谢"> + <a href="#%e8%87%b4%e8%b0%a2">#</a> + 致谢 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reThanks</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> 致谢,3000ye 的 <span class="k">\LaTeX</span> 模板! +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/dhu/page/1/index.html b/tags/dhu/page/1/index.html new file mode 100644 index 0000000..8507bc1 --- /dev/null +++ b/tags/dhu/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/dhu/ + + + + + + diff --git a/tags/feishu/index.html b/tags/feishu/index.html new file mode 100644 index 0000000..99a386a --- /dev/null +++ b/tags/feishu/index.html @@ -0,0 +1,536 @@ + + + + +Tag: Feishu - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Feishu

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/feishu/index.xml b/tags/feishu/index.xml new file mode 100644 index 0000000..752ee92 --- /dev/null +++ b/tags/feishu/index.xml @@ -0,0 +1,194 @@ + + + + Feishu on 3000ye's Blog + https://3000ye.com/tags/feishu/ + Recent content in Feishu on 3000ye's Blog + Hugo -- gohugo.io + en-us + Fri, 22 Dec 2023 13:47:39 +0800 + Feishu Robot + https://3000ye.com/p/feishu-robot/ + Fri, 22 Dec 2023 13:47:39 +0800 + + https://3000ye.com/p/feishu-robot/ + <img src="https://3000ye.com/p/feishu-robot/assets/feishuGitlab.png" alt="Featured image of post Feishu Robot" /><h1 id="飞书机器人监听-gitlab-项目"> + <a href="#%e9%a3%9e%e4%b9%a6%e6%9c%ba%e5%99%a8%e4%ba%ba%e7%9b%91%e5%90%ac-gitlab-%e9%a1%b9%e7%9b%ae">#</a> + 飞书机器人监听 Gitlab 项目 +</h1><p>在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 <code>push</code> 到 Gitlab。</p> +<p>使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/feishu.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="准备工作"> + <a href="#%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c">#</a> + 准备工作 +</h2><p>实现这个功能,你需要准备:</p> +<ul> +<li>电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。</li> +<li>飞书账号:用于创建机器人监听 Gitlab 项目。</li> +<li>服务器(Linux):用于部署任务,并自动 <code>push</code> 到 Gitlab。</li> +</ul> +<h2 id="创建-gitlab-账号并新建项目"> + <a href="#%e5%88%9b%e5%bb%ba-gitlab-%e8%b4%a6%e5%8f%b7%e5%b9%b6%e6%96%b0%e5%bb%ba%e9%a1%b9%e7%9b%ae">#</a> + 创建 Gitlab 账号并新建项目 +</h2><p>使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/</p> +<p>注册完成后新建一个新项目,并添加一个 <code>README.md</code> 文件。</p> +<h2 id="配置-ssh-秘钥"> + <a href="#%e9%85%8d%e7%bd%ae-ssh-%e7%a7%98%e9%92%a5">#</a> + 配置 SSH 秘钥 +</h2><p>使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install git +</span></span><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config --global user.email <span class="s2">&#34;email&#34;</span> +</span></span><span class="line"><span class="cl">ssh-keygen -C <span class="s2">&#34;email&#34;</span> -t rsa +</span></span><span class="line"><span class="cl">cat ~/.ssh/id_rsa.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中 <code>name</code> 为 Gitlab 账号的名称,<code>email</code> 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。</p> +<p>然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。</p> +<h2 id="clone-项目并配置任务"> + <a href="#clone-%e9%a1%b9%e7%9b%ae%e5%b9%b6%e9%85%8d%e7%bd%ae%e4%bb%bb%e5%8a%a1">#</a> + Clone 项目并配置任务 +</h2><p>在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img3.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后在 Terminal 中找到合适的位置,Clone 项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git +</span></span></code></pre></td></tr></table> +</div> +</div><p>编写你的任务脚本(<code>shell</code> 或 <code>Python</code> 等),然后确保其能正常运行。</p> +<h2 id="新建飞书指令"> + <a href="#%e6%96%b0%e5%bb%ba%e9%a3%9e%e4%b9%a6%e6%8c%87%e4%bb%a4">#</a> + 新建飞书指令 +</h2><p>使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img4.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img5.png' alt='img' style='zoom:50%;' /> +</div> +<div style='display: flex; justify-content: center;'> +<img src='assets/img6.png' alt='img' style='zoom:50%;' /> +</div> +<p>点击完成后,启用机器人即可。</p> +<h2 id="创建定时任务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 创建定时任务 +</h2><p>首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。</p> +<p>本文推荐使用 <code>systemctl</code> 和 <code>systemd</code> 的 <code>timer</code> 来创建定时任务。</p> +<h3 id="创建定时器配置"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e9%85%8d%e7%bd%ae">#</a> + 创建定时器配置 +</h3><p>创建一个 <code>task.timer</code> 文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Timer Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span> +</span></span><span class="line"><span class="cl"><span class="c1"># OnCalendar设置定时规则,这里是每天10点</span> +</span></span><span class="line"><span class="cl"><span class="na">OnCalendar</span><span class="o">=</span><span class="s">*-*-* 10:00:00</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Install]</span> +</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建定时器服务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e6%9c%8d%e5%8a%a1">#</a> + 创建定时器服务 +</h3><p>创建一个 <code>task.service</code> 文件(默认使用 root 用户,建议指定 <code>User</code> 用户):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Service Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Service]</span> +</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">simple</span> +</span></span><span class="line"><span class="cl"><span class="na">User</span><span class="o">=</span><span class="s">user</span> +</span></span><span class="line"><span class="cl"><span class="c1"># 建议使用 shell 脚本</span> +</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/path/to/your/script.sh</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加定时任务"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 添加定时任务 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo cp your_timer_name.timer /etc/systemd/system/ +</span></span><span class="line"><span class="cl">sudo cp your_service_name.service /etc/systemd/system/ +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">sudo systemctl daemon-reload +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 手动启用任务,测试任务是否正常运行</span> +</span></span><span class="line"><span class="cl">sudo systemctl start your_timer_name.service +</span></span><span class="line"><span class="cl"><span class="c1"># 输出任务运行日志</span> +</span></span><span class="line"><span class="cl">sudo journalctl -u your_timer_name.service +</span></span></code></pre></td></tr></table> +</div> +</div><p>任务运行成功后,使用以下命令启用定时任务:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> your_timer_name.timer +</span></span><span class="line"><span class="cl">sudo systemctl list-timers +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/feishu/page/1/index.html b/tags/feishu/page/1/index.html new file mode 100644 index 0000000..c5d8fe0 --- /dev/null +++ b/tags/feishu/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/feishu/ + + + + + + diff --git a/tags/git/index.html b/tags/git/index.html new file mode 100644 index 0000000..1dc6d31 --- /dev/null +++ b/tags/git/index.html @@ -0,0 +1,556 @@ + + + + +Tag: Git - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

2 pages

+

Git

+ +
+
+
+ +
+ + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/git/index.xml b/tags/git/index.xml new file mode 100644 index 0000000..fa0871e --- /dev/null +++ b/tags/git/index.xml @@ -0,0 +1,278 @@ + + + + Git on 3000ye's Blog + https://3000ye.com/tags/git/ + Recent content in Git on 3000ye's Blog + Hugo -- gohugo.io + en-us + Tue, 26 Dec 2023 16:23:26 +0800 + SSH Key Agent + https://3000ye.com/p/ssh-key-agent/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/p/ssh-key-agent/ + <img src="https://3000ye.com/p/ssh-key-agent/assets/gitssh.png" alt="Featured image of post SSH Key Agent" /><h1 id="为不同的-git-服务生成不同的-ssh-key"> + <a href="#%e4%b8%ba%e4%b8%8d%e5%90%8c%e7%9a%84-git-%e6%9c%8d%e5%8a%a1%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 为不同的 Git 服务生成不同的 SSH Key +</h1><p>在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:</p> +<ul> +<li>我有多个 Git 平台(Github, Gitlab, &hellip;)的账号,每个平台的账号都需要一个 SSH Key。</li> +<li>在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。</li> +<li>不同平台的账号,可能使用相同邮箱进行注册。</li> +</ul> +<p>这时,如何生成和管理 SSH Key 成为一个问题。</p> +<h2 id="设置局部-git-信息"> + <a href="#%e8%ae%be%e7%bd%ae%e5%b1%80%e9%83%a8-git-%e4%bf%a1%e6%81%af">#</a> + 设置局部 Git 信息 +</h2><p>在不同文件夹下,都可以设置不同的局部 Git 信息:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config user.email <span class="s2">&#34;email&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="开启-ssh-agent"> + <a href="#%e5%bc%80%e5%90%af-ssh-agent">#</a> + 开启 SSH Agent +</h2><p>开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="生成不同的-ssh-key"> + <a href="#%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 生成不同的 SSH Key +</h2><p>指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -f ~/.ssh/id_rsa_name -C <span class="s2">&#34;your_email@example.com&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>将生成的私钥添加进代理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-add ~/.ssh/id_rsa_name +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># MacOS</span> +</span></span><span class="line"><span class="cl">ssh-add --apple-use-keychain ~/.ssh/id_rsa_name +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="将公钥添加到-git-平台"> + <a href="#%e5%b0%86%e5%85%ac%e9%92%a5%e6%b7%bb%e5%8a%a0%e5%88%b0-git-%e5%b9%b3%e5%8f%b0">#</a> + 将公钥添加到 Git 平台 +</h2><p>首先复制 SSH 公钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat ~/.ssh/id_rsa_name.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后到 Git 平台中,添加该公钥。</p> + + + + Feishu Robot + https://3000ye.com/p/feishu-robot/ + Fri, 22 Dec 2023 13:47:39 +0800 + + https://3000ye.com/p/feishu-robot/ + <img src="https://3000ye.com/p/feishu-robot/assets/feishuGitlab.png" alt="Featured image of post Feishu Robot" /><h1 id="飞书机器人监听-gitlab-项目"> + <a href="#%e9%a3%9e%e4%b9%a6%e6%9c%ba%e5%99%a8%e4%ba%ba%e7%9b%91%e5%90%ac-gitlab-%e9%a1%b9%e7%9b%ae">#</a> + 飞书机器人监听 Gitlab 项目 +</h1><p>在服务器部署定时任务(爬虫、拉取数据库、模型训练等),任务完成后自动填写 commit 信息,并 <code>push</code> 到 Gitlab。</p> +<p>使用飞书机器人自动监听 Gitlab 项目,并获取 commit 信息,最终发送到飞书指定用户或群聊。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/feishu.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="准备工作"> + <a href="#%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c">#</a> + 准备工作 +</h2><p>实现这个功能,你需要准备:</p> +<ul> +<li>电子邮箱:用于创建 Gitlab 账号和绑定 SSH 秘钥。</li> +<li>飞书账号:用于创建机器人监听 Gitlab 项目。</li> +<li>服务器(Linux):用于部署任务,并自动 <code>push</code> 到 Gitlab。</li> +</ul> +<h2 id="创建-gitlab-账号并新建项目"> + <a href="#%e5%88%9b%e5%bb%ba-gitlab-%e8%b4%a6%e5%8f%b7%e5%b9%b6%e6%96%b0%e5%bb%ba%e9%a1%b9%e7%9b%ae">#</a> + 创建 Gitlab 账号并新建项目 +</h2><p>使用准备好的邮箱,注册 Gitlab 账号:https://gitlab.com/</p> +<p>注册完成后新建一个新项目,并添加一个 <code>README.md</code> 文件。</p> +<h2 id="配置-ssh-秘钥"> + <a href="#%e9%85%8d%e7%bd%ae-ssh-%e7%a7%98%e9%92%a5">#</a> + 配置 SSH 秘钥 +</h2><p>使用 Terminal 连接你的服务器,使用以下命令(Ubuntu 22.04):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install git +</span></span><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config --global user.email <span class="s2">&#34;email&#34;</span> +</span></span><span class="line"><span class="cl">ssh-keygen -C <span class="s2">&#34;email&#34;</span> -t rsa +</span></span><span class="line"><span class="cl">cat ~/.ssh/id_rsa.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>其中 <code>name</code> 为 Gitlab 账号的名称,<code>email</code> 为 Gitlab 账号的邮箱,将输出的内容复制到剪切板。</p> +<p>然后在 Gitlab 中,进入用户设置,找到 SSH 秘钥然后选择添加新秘钥,粘贴剪切板的内容保存即可。</p> +<h2 id="clone-项目并配置任务"> + <a href="#clone-%e9%a1%b9%e7%9b%ae%e5%b9%b6%e9%85%8d%e7%bd%ae%e4%bb%bb%e5%8a%a1">#</a> + Clone 项目并配置任务 +</h2><p>在 Gitlab 中打开项目,点击右上角的代码,复制使用 SSH 克隆的链接:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img3.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后在 Terminal 中找到合适的位置,Clone 项目:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@gitlab.com:xxxxxxxxxxxxxxxxxx.git +</span></span></code></pre></td></tr></table> +</div> +</div><p>编写你的任务脚本(<code>shell</code> 或 <code>Python</code> 等),然后确保其能正常运行。</p> +<h2 id="新建飞书指令"> + <a href="#%e6%96%b0%e5%bb%ba%e9%a3%9e%e4%b9%a6%e6%8c%87%e4%bb%a4">#</a> + 新建飞书指令 +</h2><p>使用飞书机器人助手,新建机器人指令,触发器选择 [新的 commit 创建],按照教程绑定 Gitlab 账号和项目:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img4.png' alt='img' style='zoom:50%;' /> +</div> +<p>然后选择操作 [通过官方机器人发消息],设置消息内容为 commit 说明,并选择发送目标:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/img5.png' alt='img' style='zoom:50%;' /> +</div> +<div style='display: flex; justify-content: center;'> +<img src='assets/img6.png' alt='img' style='zoom:50%;' /> +</div> +<p>点击完成后,启用机器人即可。</p> +<h2 id="创建定时任务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 创建定时任务 +</h2><p>首先测试飞书机器人是否能够正常抓取 Gitlab 项目的 commit,测试成功后,在服务器上为任务创建定时任务。</p> +<p>本文推荐使用 <code>systemctl</code> 和 <code>systemd</code> 的 <code>timer</code> 来创建定时任务。</p> +<h3 id="创建定时器配置"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e9%85%8d%e7%bd%ae">#</a> + 创建定时器配置 +</h3><p>创建一个 <code>task.timer</code> 文件:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Timer Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span> +</span></span><span class="line"><span class="cl"><span class="c1"># OnCalendar设置定时规则,这里是每天10点</span> +</span></span><span class="line"><span class="cl"><span class="na">OnCalendar</span><span class="o">=</span><span class="s">*-*-* 10:00:00</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Install]</span> +</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="创建定时器服务"> + <a href="#%e5%88%9b%e5%bb%ba%e5%ae%9a%e6%97%b6%e5%99%a8%e6%9c%8d%e5%8a%a1">#</a> + 创建定时器服务 +</h3><p>创建一个 <code>task.service</code> 文件(默认使用 root 用户,建议指定 <code>User</code> 用户):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> +</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Your Service Description</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="k">[Service]</span> +</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">simple</span> +</span></span><span class="line"><span class="cl"><span class="na">User</span><span class="o">=</span><span class="s">user</span> +</span></span><span class="line"><span class="cl"><span class="c1"># 建议使用 shell 脚本</span> +</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/path/to/your/script.sh</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="添加定时任务"> + <a href="#%e6%b7%bb%e5%8a%a0%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1">#</a> + 添加定时任务 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo cp your_timer_name.timer /etc/systemd/system/ +</span></span><span class="line"><span class="cl">sudo cp your_service_name.service /etc/systemd/system/ +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">sudo systemctl daemon-reload +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 手动启用任务,测试任务是否正常运行</span> +</span></span><span class="line"><span class="cl">sudo systemctl start your_timer_name.service +</span></span><span class="line"><span class="cl"><span class="c1"># 输出任务运行日志</span> +</span></span><span class="line"><span class="cl">sudo journalctl -u your_timer_name.service +</span></span></code></pre></td></tr></table> +</div> +</div><p>任务运行成功后,使用以下命令启用定时任务:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> your_timer_name.timer +</span></span><span class="line"><span class="cl">sudo systemctl list-timers +</span></span></code></pre></td></tr></table> +</div> +</div> + + + + diff --git a/tags/git/page/1/index.html b/tags/git/page/1/index.html new file mode 100644 index 0000000..5a3dba0 --- /dev/null +++ b/tags/git/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/git/ + + + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..7f51d8b --- /dev/null +++ b/tags/index.html @@ -0,0 +1,608 @@ + + + + +Tags + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

14 pages

+

Tags

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/index.xml b/tags/index.xml new file mode 100644 index 0000000..d8f5df9 --- /dev/null +++ b/tags/index.xml @@ -0,0 +1,123 @@ + + + + Tags on 3000ye's Blog + https://3000ye.com/tags/ + Recent content in Tags on 3000ye's Blog + Hugo -- gohugo.io + en-us + Mon, 18 Mar 2024 21:06:19 +0800 + CPP + https://3000ye.com/tags/cpp/ + Mon, 18 Mar 2024 21:06:19 +0800 + + https://3000ye.com/tags/cpp/ + + + + Data Structure + https://3000ye.com/tags/data-structure/ + Wed, 21 Feb 2024 19:14:32 +0800 + + https://3000ye.com/tags/data-structure/ + + + + Beamer + https://3000ye.com/tags/beamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/tags/beamer/ + + + + DHU + https://3000ye.com/tags/dhu/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/tags/dhu/ + + + + Latex + https://3000ye.com/tags/latex/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/tags/latex/ + + + + CppJson + https://3000ye.com/tags/cppjson/ + Mon, 01 Jan 2024 23:18:32 +0800 + + https://3000ye.com/tags/cppjson/ + + + + PM + https://3000ye.com/tags/pm/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/tags/pm/ + + + + PRD + https://3000ye.com/tags/prd/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/tags/prd/ + + + + Git + https://3000ye.com/tags/git/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/tags/git/ + + + + SSH + https://3000ye.com/tags/ssh/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/tags/ssh/ + + + + Feishu + https://3000ye.com/tags/feishu/ + Fri, 22 Dec 2023 13:47:39 +0800 + + https://3000ye.com/tags/feishu/ + + + + C++ Primer + https://3000ye.com/tags/c++-primer/ + Sun, 10 Dec 2023 15:58:28 +0800 + + https://3000ye.com/tags/c++-primer/ + + + + Camera + https://3000ye.com/tags/camera/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/tags/camera/ + + + + Test Tag + https://3000ye.com/tags/test-tag/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/tags/test-tag/ + <img src="https://3000ye.com/tags/test-tag/nord.jpg" alt="Featured image of post Test Tag" /> + + + + diff --git a/tags/latex/index.html b/tags/latex/index.html new file mode 100644 index 0000000..5ce867b --- /dev/null +++ b/tags/latex/index.html @@ -0,0 +1,596 @@ + + + + +Tag: Latex - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

4 pages

+

Latex

+ +
+
+
+ +
+ + + + + + + + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/latex/index.xml b/tags/latex/index.xml new file mode 100644 index 0000000..d4d7d08 --- /dev/null +++ b/tags/latex/index.xml @@ -0,0 +1,837 @@ + + + + Latex on 3000ye's Blog + https://3000ye.com/tags/latex/ + Recent content in Latex on 3000ye's Blog + Hugo -- gohugo.io + en-us + Fri, 16 Feb 2024 23:33:47 +0800 + dhuBeamer + https://3000ye.com/p/dhubeamer/ + Fri, 16 Feb 2024 23:33:47 +0800 + + https://3000ye.com/p/dhubeamer/ + <img src="https://3000ye.com/p/dhubeamer/assets/latex.jpg" alt="Featured image of post dhuBeamer" /><h1 id="东华大学学术-beamer-模板"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e6%9c%af-beamer-%e6%a8%a1%e6%9d%bf">#</a> + 东华大学学术 Beamer 模板 +</h1><p>本模板为东华大学专用的学术报告 Slides 的 LaTeX Beamer 模板使用说明,主要特点为:</p> +<ul> +<li>设计元素全部来源于东华大学 <a class="link" href="https://www.dhu.edu.cn/bsxt/listm.htm" target="_blank" rel="noopener" + >标识系统</a>,浓浓的东华风。</li> +<li>Slides 整体风格借鉴了东华大学标准 PPT 模板与学术 PPT 模板(2020版,<a class="link" href="https://www.dhu.edu.cn/_upload/article/files/d2/8c/2137ec0c44238fd6fbd3ee28ff07/9f9b566a-67f1-4717-991f-477ee5b43acb.zip" target="_blank" rel="noopener" + >模板连接</a>)。</li> +<li>简洁清晰的底部导航区让 Slides 更适合学术汇报使用。</li> +<li>前后端分离式设计,<code>.tex</code> 文件中只需聚焦内容,<code>.sty</code> 配置文件隔离存放。</li> +</ul> +<h2 id="使用-dhubeamer-模板"> + <a href="#%e4%bd%bf%e7%94%a8-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 使用 dhuBeamer 模板 +</h2><h3 id="认识-beamer-中的元素"> + <a href="#%e8%ae%a4%e8%af%86-beamer-%e4%b8%ad%e7%9a%84%e5%85%83%e7%b4%a0">#</a> + 认识 Beamer 中的元素 +</h3><p>Beamer 根据元素在 Slides 中的不同作用,主要做了以下划分:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/Beamer元素.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="修改-dhubeamer-模板"> + <a href="#%e4%bf%ae%e6%94%b9-dhubeamer-%e6%a8%a1%e6%9d%bf">#</a> + 修改 dhuBeamer 模板 +</h2> + + + Slides + https://3000ye.com/p/slides/ + Fri, 20 Oct 2023 10:04:55 +0800 + + https://3000ye.com/p/slides/ + <img src="https://3000ye.com/p/slides/assets/latex.jpg" alt="Featured image of post Slides" /><h1 id="使用-latex-绘制-slides"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e7%bb%98%e5%88%b6-slides">#</a> + 使用 $\LaTeX$ 绘制 Slides +</h1> + + + dhuBachelor + https://3000ye.com/p/dhubachelor/ + Thu, 12 Oct 2023 16:41:55 +0800 + + https://3000ye.com/p/dhubachelor/ + <img src="https://3000ye.com/p/dhubachelor/assets/latex.jpg" alt="Featured image of post dhuBachelor" /><h1 id="东华大学学士毕业论文-latex-模板使用手册"> + <a href="#%e4%b8%9c%e5%8d%8e%e5%a4%a7%e5%ad%a6%e5%ad%a6%e5%a3%ab%e6%af%95%e4%b8%9a%e8%ae%ba%e6%96%87-latex-%e6%a8%a1%e6%9d%bf%e4%bd%bf%e7%94%a8%e6%89%8b%e5%86%8c">#</a> + 东华大学学士毕业论文 LaTeX 模板使用手册 +</h1><h2 id="导入模板"> + <a href="#%e5%af%bc%e5%85%a5%e6%a8%a1%e6%9d%bf">#</a> + 导入模板 +</h2><h3 id="使用-git-克隆仓库"> + <a href="#%e4%bd%bf%e7%94%a8-git-%e5%85%8b%e9%9a%86%e4%bb%93%e5%ba%93">#</a> + 使用 Git 克隆仓库 +</h3><p>将仓库克隆到你需要的位置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone git@github.com:3000ye/dhuBachelor.git +</span></span><span class="line"><span class="cl"><span class="c1"># 或</span> +</span></span><span class="line"><span class="cl">git clone https://github.com/3000ye/dhuBachelor.git +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="下载-zip-文件"> + <a href="#%e4%b8%8b%e8%bd%bd-zip-%e6%96%87%e4%bb%b6">#</a> + 下载 Zip 文件 +</h3><p>如果你不会使用<code>Git</code>,可以进入网页:https://github.com/3000ye/dhuBachelor 然后下载<code>Zip</code>文件:</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/zip.png" alt="img" style="zoom:100%"/> +</div> +<h3 id="微信公众号获取"> + <a href="#%e5%be%ae%e4%bf%a1%e5%85%ac%e4%bc%97%e5%8f%b7%e8%8e%b7%e5%8f%96">#</a> + 微信公众号获取 +</h3><p>如果你没有科学代理,无法进入<code>Github</code>,请扫码关注公众号:<code>3000ye Blog</code>然后回复<code>latex模板</code>获取百度网盘分享链接。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/weixin.jpg" alt="img" style="zoom:100%"/> +</div> +<h2 id="开始使用"> + <a href="#%e5%bc%80%e5%a7%8b%e4%bd%bf%e7%94%a8">#</a> + 开始使用 +</h2><h3 id="安装字体"> + <a href="#%e5%ae%89%e8%a3%85%e5%ad%97%e4%bd%93">#</a> + 安装字体 +</h3><p>在使用模板之前,请先找到并打开<code>fonts</code>文件夹,安装里面的所有字体。</p> +<h3 id="配置-tex-环境"> + <a href="#%e9%85%8d%e7%bd%ae-tex-%e7%8e%af%e5%a2%83">#</a> + 配置 TeX 环境 +</h3><p>如果你是 $\LaTeX$ 小白,请先行阅读:<a class="link" href="https://3000ye.com/p/elegant-latex/" target="_blank" rel="noopener" + >使用 LaTeX 优雅地完成创作</a>,在这个教程里你可以学会如何安装并配置适合你的 $\TeX$ 环境。</p> +<h3 id="尝试编译"> + <a href="#%e5%b0%9d%e8%af%95%e7%bc%96%e8%af%91">#</a> + 尝试编译 +</h3><p>使用你喜欢的编辑器,打开<code>dhuBachelor.tex</code>文件,选择<code>xelatex</code>命令编译文件。如果没有出现报错,同目录下会生成一个<code>dhuBachelor.pdf</code>文件,这就是我们的论文。</p> +<h2 id="各个组件说明"> + <a href="#%e5%90%84%e4%b8%aa%e7%bb%84%e4%bb%b6%e8%af%b4%e6%98%8e">#</a> + 各个组件说明 +</h2><p>看到这里,相信你已经成功编译好了模板文件,现在你可以在其基础上创作你的论文。</p> +<p>本模板的格式严格按照东华大学本科生毕业设计(论文)撰写规范设置,下面是一些会用到的组件的详细说明。</p> +<h3 id="论文题目"> + <a href="#%e8%ae%ba%e6%96%87%e9%a2%98%e7%9b%ae">#</a> + 论文题目 +</h3><p>根据要求,论文题目使用三号黑体,上下各空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reTitle</span><span class="nb">{</span>中文题目<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\reTitleEN</span><span class="nb">{</span>英文题目<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="摘要"> + <a href="#%e6%91%98%e8%a6%81">#</a> + 摘要 +</h3><p>根据要求,摘要使用四号黑体,下面空一行,居中显示。添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reAbstract</span> <span class="c">% 中文摘要 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reAbstractEN</span> <span class="c">% 英文摘要 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>摘要内容直接写在摘要下方,首行缩进两字符。</p> +<h3 id="关键词"> + <a href="#%e5%85%b3%e9%94%ae%e8%af%8d">#</a> + 关键词 +</h3><p>中文关键词:小四号黑体(标题),小四号宋体(关键词),逗号分隔,末尾没有标点符号。</p> +<p>英文关键词:Times New Roman(标题加黑)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reKeyword</span><span class="nb">{</span>关键词1,关键词2,关键词3,关键词4<span class="nb">}</span> <span class="c">% 中文关键词 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\reKeywordEN</span><span class="nb">{</span>Keyword1, Keyword2, Keyword3<span class="nb">}</span> <span class="c">% 英文关键词 +</span></span></span></code></pre></td></tr></table> +</div> +</div><h3 id="目录"> + <a href="#%e7%9b%ae%e5%bd%95">#</a> + 目录 +</h3><p>目录标题需居中显示,添加代码:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\tableofcontents</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>center<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="各级标题"> + <a href="#%e5%90%84%e7%ba%a7%e6%a0%87%e9%a2%98">#</a> + 各级标题 +</h3><p>规范中明确给出,最多只能使用三级标题,其中一级标题需上下各空一行。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reSection</span><span class="nb">{</span>一级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsection</span><span class="nb">{</span>二级标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\subsubsection</span><span class="nb">{</span>三级标题<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="有序列表"> + <a href="#%e6%9c%89%e5%ba%8f%e5%88%97%e8%a1%a8">#</a> + 有序列表 +</h3><p>规范中并没有给出无序列表的样例,因此不建议使用,只用有序列表:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\orderedList</span><span class="nb">{</span> <span class="c">% 使用 (i) 排序,缩进 2 字符 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> <span class="c">% \par的作用是将内容换行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">\item</span> 有序列表标题 <span class="k">\par</span> +</span></span><span class="line"><span class="cl"> 这是有序列表的内容 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="数学公式"> + <a href="#%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 数学公式 +</h3><p>行内公式:<code>$f(x) = x + 1$</code></p> +<p>跨行公式:跨行公式请使用<code>equation</code>环境,默认按照章节自动编号。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> x = a<span class="nb">_</span>0 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>1 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>2 +</span></span><span class="line"><span class="cl"> + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>3 + <span class="k">\cfrac</span><span class="nb">{</span>1<span class="nb">}{</span>a<span class="nb">_</span>4<span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> <span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>equation<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>图片插入默认在文字下方,请严格按照模板的格式进行插入,使用时只需要改<code>width</code>大小和图片路径,并根据实际更改图例和索引。</p> +<p>注意:图片需要保存在<code>assets/</code>目录中才能被正确插入,读者可以自己新建其他目录实现插入。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] <span class="c">% 图片位于文字下方 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> <span class="c">% 居中 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="c">% 设置图片占页面宽度的比例(默认0.8) +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片标题<span class="nb">}</span> <span class="c">% 图例,按章节编号 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>fig: 数据结构2<span class="nb">}</span> <span class="c">% 图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>有时可能需要多图并排,模板使用<code>minipage</code>来实现并排展示,使用时可以修改子图所占比例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>figure<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\hspace</span><span class="nb">{</span>1em<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.40<span class="k">\textwidth</span><span class="nb">}</span> <span class="c">%minipage使之保持同一行 +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\includegraphics</span><span class="na">[width=0.8\textwidth]</span><span class="nb">{</span>assets/dataStructures.jpg<span class="nb">}</span><span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>图片2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>figure<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入表格"> + <a href="#%e6%8f%92%e5%85%a5%e8%a1%a8%e6%a0%bc">#</a> + 插入表格 +</h3><p>使用 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a> 工具生成表格代码后,需要手动添加分割线(<code>\toprule, \midrule, \bottomrule</code>),以达到三线表的格式要求。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||l<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> parameter <span class="nb">&amp;</span> Description <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">I</span><span class="s">$</span> <span class="nb">&amp;</span> Land area collection <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">J</span><span class="s">$</span> <span class="nb">&amp;</span> Flower pollination demand set <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">D_j</span><span class="s">$</span> <span class="nb">&amp;</span> Number of pollinating bees required for flower pollination <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">T_k</span><span class="s">$</span> <span class="nb">&amp;</span> Honeycomb size grade, <span class="s">$</span><span class="nb">k </span><span class="o">=</span><span class="nb"> </span><span class="m">1</span><span class="nb">, </span><span class="m">2</span><span class="nb">, </span><span class="nv">\cdots</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">B</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum number of hive <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">R_{ik}</span><span class="s">$</span> <span class="nb">&amp;</span> Maximum influence radius of a single honeycomb <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 一个表<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>对于需要多表并排的情况,和图片的方式类似,使用<code>minipage</code>来实现:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span><span class="lnt">13 +</span><span class="lnt">14 +</span><span class="lnt">15 +</span><span class="lnt">16 +</span><span class="lnt">17 +</span><span class="lnt">18 +</span><span class="lnt">19 +</span><span class="lnt">20 +</span><span class="lnt">21 +</span><span class="lnt">22 +</span><span class="lnt">23 +</span><span class="lnt">24 +</span><span class="lnt">25 +</span><span class="lnt">26 +</span><span class="lnt">27 +</span><span class="lnt">28 +</span><span class="lnt">29 +</span><span class="lnt">30 +</span><span class="lnt">31 +</span><span class="lnt">32 +</span><span class="lnt">33 +</span><span class="lnt">34 +</span><span class="lnt">35 +</span><span class="lnt">36 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格1标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格1标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>minipage<span class="nb">}</span>[c]<span class="nb">{</span>0.45<span class="k">\textwidth</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>table<span class="nb">}</span>[H] +</span></span><span class="line"><span class="cl"> <span class="k">\centering</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>表格2标题<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>tabular<span class="nb">}{</span>c||lc<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\toprule</span> +</span></span><span class="line"><span class="cl"> Symbol <span class="nb">&amp;</span> Description <span class="nb">&amp;</span> Unit <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\midrule</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">t</span><span class="s">$</span> <span class="nb">&amp;</span> <span class="s">$</span><span class="nb">t_{th}</span><span class="s">$</span> year <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">e_k</span><span class="s">$</span> <span class="nb">&amp;</span> the error term <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">X_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Raw data matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="s">$</span><span class="nb">Y_{ij}</span><span class="s">$</span> <span class="nb">&amp;</span> Positive matrix <span class="nb">&amp;</span> <span class="s">$</span><span class="nv">\sim</span><span class="s">$</span> <span class="k">\\</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bottomrule</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>tabular<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\label</span><span class="nb">{</span>tab: 表格2标题<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span> <span class="k">\end</span><span class="nb">{</span>table<span class="nb">}</span><span class="c">% +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\end</span><span class="nb">{</span>minipage<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bb%a3%e7%a0%81">#</a> + 插入代码 +</h3><p>可以直接在<code>.tex</code>文件中编写代码,并指定语言和标题:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>lstlisting<span class="nb">}</span>[language=c++,title=<span class="nb">{</span>code.cpp<span class="nb">}</span>] +</span></span><span class="line"><span class="cl">#include &#34;bits/stdc++.h&#34; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">using namespace std; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl">int main() <span class="nb">{</span> +</span></span><span class="line"><span class="cl"> cout &lt;&lt; &#34;3000ye 的 LaTeX 模板!&#34; &lt;&lt; endl; +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> return 0; +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>lstlisting<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>另一种更为推荐的方式是加载文件中的代码,代码文件需要保存在<code>assets/</code>目录下:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\lstinputlisting</span><span class="na">[language=c++, title=code.cpp]</span><span class="nb">{</span>code/code.cpp<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="插入伪代码"> + <a href="#%e6%8f%92%e5%85%a5%e4%bc%aa%e4%bb%a3%e7%a0%81">#</a> + 插入伪代码 +</h3><p>使用宏包<code>algorithm, algorithmic</code>来实现伪代码的添加,具体实现可以查看文档,下面是一个简单示例:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\caption</span><span class="nb">{</span>Example Pseudocode<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\begin</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="m">0</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\IF</span> <span class="nb">{</span><span class="s">$</span><span class="nb">x</span><span class="nv">\leq</span><span class="nb"> </span><span class="m">0</span><span class="s">$</span><span class="nb">}</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">+</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ELSE</span> +</span></span><span class="line"><span class="cl"> <span class="k">\STATE</span> <span class="s">$</span><span class="nb">x</span><span class="nv">\gets</span><span class="nb"> x</span><span class="o">-</span><span class="m">1</span><span class="s">$</span> +</span></span><span class="line"><span class="cl"> <span class="k">\ENDIF</span> +</span></span><span class="line"><span class="cl"> <span class="k">\end</span><span class="nb">{</span>algorithmic<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>algorithm<span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="参考文献"> + <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae">#</a> + 参考文献 +</h3><p>参考文献使用<code>\bibitem</code>来添加,添加时需要手动更改<code>{RNi}</code>索引(<code>i</code>是你文献的序号)。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reference</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN1<span class="nb">}</span> 参考文献1 +</span></span><span class="line"><span class="cl"> <span class="k">\bibitem</span><span class="nb">{</span>RN2<span class="nb">}</span> 参考文献2 +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="致谢"> + <a href="#%e8%87%b4%e8%b0%a2">#</a> + 致谢 +</h3><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\reThanks</span><span class="nb">{</span> +</span></span><span class="line"><span class="cl"> 致谢,3000ye 的 <span class="k">\LaTeX</span> 模板! +</span></span><span class="line"><span class="cl"><span class="nb">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div> + + + Elegant LaTeX + https://3000ye.com/p/elegant-latex/ + Mon, 09 Oct 2023 17:18:55 +0800 + + https://3000ye.com/p/elegant-latex/ + <img src="https://3000ye.com/p/elegant-latex/assets/latex.jpg" alt="Featured image of post Elegant LaTeX" /><h1 id="使用-latex-优雅地完成创作"> + <a href="#%e4%bd%bf%e7%94%a8-latex-%e4%bc%98%e9%9b%85%e5%9c%b0%e5%ae%8c%e6%88%90%e5%88%9b%e4%bd%9c">#</a> + 使用 $\LaTeX$ 优雅地完成创作 +</h1><p>$\LaTeX$ 是一个文档准备系统 (Document Preparing System),它非常适用于生成高印刷质量的科技类和数学类文档。它也能够生成所有其他种类的文档,小到简单的信件,大到完整的书籍。 $\LaTeX$ 使用 $\TeX$ 作为它的排版引擎,学习 $\LaTeX$ 是一个漫长而痛苦的过程,我们应该充分利用已知的资料,来尽量完成我们的需求。</p> +<h2 id="从安装-tex-引擎开始"> + <a href="#%e4%bb%8e%e5%ae%89%e8%a3%85-tex-%e5%bc%95%e6%93%8e%e5%bc%80%e5%a7%8b">#</a> + 从安装 $\TeX$ 引擎开始 +</h2><p>$\TeX$ 引擎类似于 <code>gcc/g++</code> 或 <code>Python</code>,用于编译 $\LaTeX$ 文档。</p> +<p>不同平台中 $\TeX$ 的安装方法不尽相同,本文提供:<code>Windows11</code>、<code>Linux(Ubuntu 22.04)</code>、<code>MacOs(12.7)</code>、<code>Windows11-wsl2(Ubuntu22.04)</code>的安装方法。</p> +<p>如果你只是想简单体验 $\LaTeX$,可以使用 <a class="link" href="https://www.overleaf.com/" target="_blank" rel="noopener" + >overleaf</a> 在线编译平台。但出于环境稳定性和数据的安全性等因素,并不建议将其作为主力平台。</p> +<h3 id="windows11"> + <a href="#windows11">#</a> + Windows11 +</h3><p>进入网站<a class="link" href="https://tug.org/texlive/windows.html#install" target="_blank" rel="noopener" + >tug.org for windows</a>,点击<code>install-tl-windows.exe</code>下载 $\TeX$ 安装器,然后运行安装即可。</p> +<div style="display: flex; justify-content: center;"> +<img src="assets/windowsInstall.png" alt="img" style="zoom:100%"/> +</div> +<p>不过,这种方法需要一直联网安装,网速不好的环境可以直接下载<code>iso</code>镜像进行本地安装。本文给出清华源镜像地址:<a class="link" href="https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/" target="_blank" rel="noopener" + >mirrors.tuna</a>,下载后缀为<code>.iso</code>的文件(只用下载一个)。</p> +<p>下载完成后双击文件挂载镜像,然后打开镜像文件夹,右键点击<code>install-tl-windows.bat</code>文件,使用管理员打开,然后按照指引安装即可。</p> +<p>最新版本的安装器会自动添加环境变量,安装完成后打开<code>cmd</code>然后输入:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/W32TeX<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="linuxubuntu2204"> + <a href="#linuxubuntu2204">#</a> + Linux(Ubuntu22.04) +</h3><p>打开终端,然后执行安装命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt update +</span></span><span class="line"><span class="cl">sudo apt upgrade +</span></span><span class="line"><span class="cl">sudo apt install texlive-full +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023/Debian<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="macos127"> + <a href="#macos127">#</a> + MacOs(12.7) +</h3><p>打开终端,然后执行安装命令(推荐安装无窗体版本):</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install mactex-no-gui +</span></span></code></pre></td></tr></table> +</div> +</div><p>等待安装完成即可,安装完成后执行命令:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tex --version +</span></span></code></pre></td></tr></table> +</div> +</div><p>若能输出 $\TeX$ 版本信息则安装成功:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">TeX 3.141592653 <span class="o">(</span>TeX Live 2023<span class="o">)</span> +</span></span><span class="line"><span class="cl">kpathsea version 6.3.5 +</span></span><span class="line"><span class="cl">Copyright <span class="m">2023</span> D.E. Knuth. +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="windows11-wsl2ubuntu2204"> + <a href="#windows11-wsl2ubuntu2204">#</a> + Windows11-wsl2(Ubuntu22.04) +</h3><p>在<code>wsl2</code>中安装方式与在<code>Linux</code>中一样。</p> +<h2 id="找到属于你的编辑器"> + <a href="#%e6%89%be%e5%88%b0%e5%b1%9e%e4%ba%8e%e4%bd%a0%e7%9a%84%e7%bc%96%e8%be%91%e5%99%a8">#</a> + 找到属于你的编辑器 +</h2><p>市面上有很多 $\LaTeX$ 编辑器,且与使用的系统有关,下面是一些主观评价:</p> +<ul> +<li>全平台通用: +<ul> +<li><code>Vs Code</code>:作为地表最强编辑器,<code>Vs Code</code>拥有非常丰富的 $\LaTeX$ 插件和完备的配置方案,并且可以免费使用,但缺点是配置较为繁琐。</li> +<li><code>Jetbrains</code>: 与<code>Vs Code</code>相对应的是<code>Jetbrains</code>系列, 其虽然也有 $\LaTeX$ 插件,但使用体验非常不好,且其文件管理方式并不适合每个人。</li> +<li><code>Neovim</code>:如果说<code>Vs Code</code>是编辑器中的王后,那么<code>nvim</code>就是国王。<code>nvim</code>可以实现最大程度的自定义编辑方案,拥有海量插件生态,但缺点是学习路线非常陡峭,常人难以驾驭。</li> +<li><code>sublime text</code>:<code>nvim</code>固然强大,但其难以上手的特点使得很多人对其望而却步。<code>sublime</code>打破了这个束缚,其界面优雅程度不亚于<code>nvim</code>,也具有丰富的插件来实现你的理想配置,但配置同样较为繁琐,且需要付费。</li> +<li><code>TexStudio</code>:<code>texlive</code>默认自带编辑器,简单好用容易上手,是很多教程的主推编辑器,但笔者认为界面过于丑陋,不建议用。</li> +</ul> +</li> +<li><code>Windows</code>独占: +<ul> +<li><code>Winedt 11</code>:如果不考虑跨平台,那么<code>Winedt 11</code>就是<code>Windows</code>上的最佳编辑器。这是一款罕见的非所见即所得的编辑器,笔者认为这完美契合了 $\LaTeX$ 的风格,同时其优雅成熟的界面和高度可定制化的功能使其一骑绝尘。但需要付费(169元买断)。</li> +</ul> +</li> +</ul> +<p>综上所述,笔者最推荐<code>Vs Code</code>,但如果你只有<code>Windows</code>平台的使用需求并不介意一点费用的话,请果断购买<code>Winedt 11</code>。同样的,<code>MacOs</code>也拥有独占编辑器,但笔者没用过,在此不做评价。</p> +<p>关于这些编辑器如何配置,网上的教程有很多,读者可自行查阅。</p> +<h2 id="一本教程入门-latex-语法"> + <a href="#%e4%b8%80%e6%9c%ac%e6%95%99%e7%a8%8b%e5%85%a5%e9%97%a8-latex-%e8%af%ad%e6%b3%95">#</a> + 一本教程入门 $\LaTeX$ 语法 +</h2><p>对于所有初学者来说,<a class="link" href="https://mirror-hk.koddos.net/CTAN/info/lshort/chinese/lshort-zh-cn.pdf" target="_blank" rel="noopener" + >Ishort-zh-cn</a>都是最好的入门教程。在开始你的创作之前,请务必先行完整阅读一遍,并动手尝试书中的案例。</p> +<p>如果你已经完成了所有的案例,相信你已经对 $\LaTeX$ 语法有了简单了解,下面笔者给出一些新手可能遇到的常见问题。但请不要灰心,$\LaTeX$ 的学习是一件持久且困难的事,我们并不需要完全精通,只需要能够达到创作目的即可。</p> +<h3 id="打印中文"> + <a href="#%e6%89%93%e5%8d%b0%e4%b8%ad%e6%96%87">#</a> + 打印中文 +</h3><p>$\LaTeX$ 默认只打印英文,如果没有合理的设置,<code>.tex</code>文件中的中文将无法正确打印。</p> +<p>从下图可以看出,目前支持全平台通用的方案只有<code>XeLaTeX</code>和<code>LuaTeX</code>。因此,主流方案是一般使用<code>xelatex+ctex</code>编译方案,底层调用<code>xeCJK</code>字符集来实现中文打印。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/xeCJK.png' alt='img' style='zoom:100%;' /> +</div> +<p>使用时只需在导言区加入,编译器会使用默认字体进行编译:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8]</span><span class="nb">{</span>ctex<span class="nb">}</span> +</span></span><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>如果要指定字体,则需分别设置<code>font-family</code>:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\usepackage</span><span class="na">[UTF8, fontset=none]</span><span class="nb">{</span>ctex<span class="nb">}</span> <span class="c">% 清除默认字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\usepackage</span><span class="nb">{</span>fontspec<span class="nb">}</span> <span class="c">% 设置字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmainfont</span><span class="nb">{</span>SimSun<span class="nb">}</span>[AutoFakeBold=true, BoldFont=<span class="nb">{</span>SimHei<span class="nb">}</span>, ItalicFont=<span class="nb">{</span>KaiTi<span class="nb">}</span>] <span class="c">% 正文字体(宋体,黑体,楷体) +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKsansfont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>KaiTi<span class="nb">}</span> <span class="c">% 无衬线字体 +</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="k">\setCJKmonofont</span><span class="na">[AutoFakeBold=3]</span><span class="nb">{</span>SimHei<span class="nb">}</span> <span class="c">% 等宽字体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><p>同样的,英文也可以自定义字体:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-tex" data-lang="tex"><span class="line"><span class="cl"><span class="k">\setmainfont</span><span class="nb">{</span>Times New Roman<span class="nb">}</span> <span class="c">% 设置英文字体为新罗马体 +</span></span></span></code></pre></td></tr></table> +</div> +</div><blockquote> +<p>详细配置可以参考:<a class="link" href="https://zhuanlan.zhihu.com/p/538459335" target="_blank" rel="noopener" + >LaTex 中文字体配置指南</a></p> +</blockquote> +<h3 id="打印数学公式"> + <a href="#%e6%89%93%e5%8d%b0%e6%95%b0%e5%ad%a6%e5%85%ac%e5%bc%8f">#</a> + 打印数学公式 +</h3><p>你是否好奇过数学教材或者论文中复杂的数学公式是如何编写的?答案就是 $\LaTeX$,这也是 $\LaTeX$ 为什么被奉为珍宝的原因之一。</p> +<p>但是笔者并不建议读者专门花时间来学习如何编写 $\LaTeX$ 数学公式,而是利用现成的工具来快速完成你的公式。</p> +<ul> +<li>在线数学公式生成平台:<a class="link" href="https://www.latexlive.com/" target="_blank" rel="noopener" + >latexlive</a>可以在线点击生成你所需要的数学公式,但前提是你已经了解了一些复杂数学环境。</li> +<li><code>MathType</code>:如果你是<code>Windows</code>用户,那么强烈建议使用<code>MathType</code>来生成数学公式的 $\LaTeX$ 代码,好处是完全不需要代码基础并且功能十分强大,但是需要付费。</li> +<li><code>mathpix</code>:这是一款专为 $\LaTeX$ 打造的数学公式 OCR 识别器,你可以截屏、拍照、甚至手写数学公式来得到你想要的代码。</li> +</ul> +<h3 id="绘制表格"> + <a href="#%e7%bb%98%e5%88%b6%e8%a1%a8%e6%a0%bc">#</a> + 绘制表格 +</h3><p>你一定使用过<code>Excel</code>来绘制表格,但是在 $\LaTeX$ 中绘制表格并不是一件轻松的事情,其中有非常多的坑且几乎每个人都无法避免。</p> +<p>但不用担心,本文为你介绍开源项目 <a class="link" href="https://www.ctan.org/pkg/excel2latex" target="_blank" rel="noopener" + >excel2latex</a>。这是一款<code>Excel</code>插件,可以将你在<code>Excel</code>中绘制的表格自动转译为 $\LaTeX$ 代码。</p> +<p>但是这个插件并不是万能的,比如绘制三线表,即使是在<code>Excel</code>中也较为繁琐。因此,最好的处理方式是使用<code>excel2latex</code>插件生成表格主体,然后再自己添加分隔格式。</p> +<h3 id="插入图片"> + <a href="#%e6%8f%92%e5%85%a5%e5%9b%be%e7%89%87">#</a> + 插入图片 +</h3><p>绘制表格和插入图片并称为 $\LaTeX$ 中两大天坑,对于图片插入笔者尚未发现有效替代工具,在下文中会给出一些示例代码供读者参考。</p> +<h2 id="学会使用代码片段"> + <a href="#%e5%ad%a6%e4%bc%9a%e4%bd%bf%e7%94%a8%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + 学会使用代码片段 +</h2><p>阅读到这里,相信你已经能够使用 $\LaTeX$ 创作出你自己的内容了。那么你应该不难发现,在创作的时候有很多代码都是可以重复使用的,只需要更改一些参数即可。但是 $\LaTeX$ 并不能像编程语言那样编写函数来实现代码的复用,当然有其他方法来实现(比如编写<code>.sty</code>和<code>.cls</code>文件),但这对初学者来说太难了。</p> +<p>因此,有没有一种好的方法可以实现这个需求呢?答案是代码片段(code snippets)。</p> +<p>代码片段可以给你的编辑器添加些许魔力。它如同咒语一般。你只要说出指令(输入前缀),挥动魔杖(按下 Enter 或者 Tab 键),然后神奇的事情就发生在你眼前了。</p> +<h3 id="vs-code配置代码片段"> + <a href="#vs-code%e9%85%8d%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Vs Code配置代码片段 +</h3><p>点击左下角的设置按钮,然后点击设置用户代码片段:</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/snippets.jpg' alt='img' style='zoom:100%;' /> +</div> +<p>在弹出的窗口中输入<code>latex</code>然后选中即可跳转到<code>latex.json</code>文件,我们可以在这里设置我们的代码片段。</p> +<p>比如这段设置,保存文件后我们只需要在<code>.tex</code>后缀的文件中输入<code>insertImg</code>然后回车就会自动填充以下代码,并且使用<code>tab</code>来依次输入参数。</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt"> 1 +</span><span class="lnt"> 2 +</span><span class="lnt"> 3 +</span><span class="lnt"> 4 +</span><span class="lnt"> 5 +</span><span class="lnt"> 6 +</span><span class="lnt"> 7 +</span><span class="lnt"> 8 +</span><span class="lnt"> 9 +</span><span class="lnt">10 +</span><span class="lnt">11 +</span><span class="lnt">12 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;insertImg&#34;</span><span class="err">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;prefix&#34;</span><span class="p">:</span> <span class="s2">&#34;insertImg&#34;</span><span class="p">,</span> <span class="c1">// 代码片段别名 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;body&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="c1">// 代码片段主体 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\begin{figure}[H]&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\centering&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="s2">&#34; \\includegraphics[width=0.8\\textwidth]{$1}&#34;</span><span class="p">,</span> <span class="c1">// 参数1:图片路径 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\caption{$2}&#34;</span><span class="p">,</span> <span class="c1">// 参数2:图片标题 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34; \\label{$3}&#34;</span><span class="p">,</span> <span class="c1">// 参数3:图片索引 +</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">&#34;\\end{figure}$0&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">],</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;insert one img with 0.8 width&#34;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h3 id="winedt-设置代码片段"> + <a href="#winedt-%e8%ae%be%e7%bd%ae%e4%bb%a3%e7%a0%81%e7%89%87%e6%ae%b5">#</a> + Winedt 设置代码片段 +</h3><p>依次点击<code>Option -&gt; Options Interface -&gt; Menus and Toolbar -&gt; Main Menu</code>,修改或添加配置:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">ITEM</span><span class="o">=</span><span class="s">&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> CAPTION=&#34;&amp;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> IMAGE=&#34;Figure&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> MACRO=&#34;Exe(&#39;%b\Menus\Insert\Image.edt&#39;);&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> SHORTCUT=&#34;49222::Ctrl+Alt+F&#34; +</span></span></span><span class="line"><span class="cl"><span class="s"> REQ_DOCUMENT=1 </span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后就可以使用快捷键<code>Ctrl+Alt+F</code>填充插入图片的代码片段。</p> +<h2 id="使用模板来专注内容"> + <a href="#%e4%bd%bf%e7%94%a8%e6%a8%a1%e6%9d%bf%e6%9d%a5%e4%b8%93%e6%b3%a8%e5%86%85%e5%ae%b9">#</a> + 使用模板来专注内容 +</h2><p>使用 $\LaTeX$ 来完成创作时,不同的需求的格式要求通常也不同。一般而言,格式的设置复杂且繁琐,如果将大部分时间花在调整格式上面有违 $\LaTeX$ 的初衷。</p> +<p>因此,常见期刊都会提供对应的 $\LaTeX$ 风格模板和示例,其中主要文件通常为:</p> +<ul> +<li><code>.sty</code>:$\LaTeX$ 样式文件,包含一组宏包和命令,用于定制文档的样式、格式和功能。通常包括:宏包的引入、自定义命令、颜色与字体预设等。</li> +<li><code>.cls</code>:$\LaTeX$ 文档文件,定义文档的整体结构和布局。通常包括:导言区设置、章节标题样式、页眉页脚与文档尺寸预设等。</li> +<li><code>.tex</code>:示例文件,通常会包括论文中会用到的所有样式的示例代码。</li> +</ul> +<p>阅读示例文件可以让我们快速创作出符合格式要求的作品,让我们不再为格式烦恼,只用专注于内容本身。</p> + + + + + diff --git a/tags/latex/page/1/index.html b/tags/latex/page/1/index.html new file mode 100644 index 0000000..126918e --- /dev/null +++ b/tags/latex/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/latex/ + + + + + + diff --git a/tags/page/1/index.html b/tags/page/1/index.html new file mode 100644 index 0000000..b043e35 --- /dev/null +++ b/tags/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/ + + + + + + diff --git a/tags/page/2/index.html b/tags/page/2/index.html new file mode 100644 index 0000000..1d97516 --- /dev/null +++ b/tags/page/2/index.html @@ -0,0 +1,617 @@ + + + + +Tags + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Section + +

+ +
+
+

14 pages

+

Tags

+ +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/pm/index.html b/tags/pm/index.html new file mode 100644 index 0000000..e16c63d --- /dev/null +++ b/tags/pm/index.html @@ -0,0 +1,536 @@ + + + + +Tag: PM - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

PM

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/pm/index.xml b/tags/pm/index.xml new file mode 100644 index 0000000..53dcb75 --- /dev/null +++ b/tags/pm/index.xml @@ -0,0 +1,71 @@ + + + + PM on 3000ye's Blog + https://3000ye.com/tags/pm/ + Recent content in PM on 3000ye's Blog + Hugo -- gohugo.io + en-us + Thu, 28 Dec 2023 14:18:06 +0800 + Happy Geek PRD + https://3000ye.com/p/happy-geek-prd/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/p/happy-geek-prd/ + <img src="https://3000ye.com/p/happy-geek-prd/assets/prd.jpg" alt="Featured image of post Happy Geek PRD" /><h1 id="创作极客喜欢的-prd"> + <a href="#%e5%88%9b%e4%bd%9c%e6%9e%81%e5%ae%a2%e5%96%9c%e6%ac%a2%e7%9a%84-prd">#</a> + 创作极客喜欢的 PRD +</h1><p>产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。</p> +<p>产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。</p> +<p>在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:</p> +<ul> +<li>产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。</li> +<li>在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。</li> +<li>开发完成后,项目结果远远不如预期。</li> +</ul> +<p>因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/PRD 结构.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="产品简介"> + <a href="#%e4%ba%a7%e5%93%81%e7%ae%80%e4%bb%8b">#</a> + 产品简介 +</h2><h2 id="行业概要"> + <a href="#%e8%a1%8c%e4%b8%9a%e6%a6%82%e8%a6%81">#</a> + 行业概要 +</h2><h2 id="发行版本"> + <a href="#%e5%8f%91%e8%a1%8c%e7%89%88%e6%9c%ac">#</a> + 发行版本 +</h2><h3 id="排期表"> + <a href="#%e6%8e%92%e6%9c%9f%e8%a1%a8">#</a> + 排期表 +</h3><h3 id="产品设计"> + <a href="#%e4%ba%a7%e5%93%81%e8%ae%be%e8%ae%a1">#</a> + 产品设计 +</h3><h4 id="实体关系图"> + <a href="#%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb%e5%9b%be">#</a> + 实体关系图 +</h4><h4 id="用户角色权限表"> + <a href="#%e7%94%a8%e6%88%b7%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e8%a1%a8">#</a> + 用户角色权限表 +</h4><table> +<thead> +<tr> +<th>function</th> +<th>user1</th> +<th>user2</th> +</tr> +</thead> +<tbody> +<tr> +<td>func 01</td> +<td>get</td> +<td>get</td> +</tr> +</tbody> +</table> + + + + + diff --git a/tags/pm/page/1/index.html b/tags/pm/page/1/index.html new file mode 100644 index 0000000..3e17c36 --- /dev/null +++ b/tags/pm/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/pm/ + + + + + + diff --git a/tags/prd/index.html b/tags/prd/index.html new file mode 100644 index 0000000..00d7ea5 --- /dev/null +++ b/tags/prd/index.html @@ -0,0 +1,536 @@ + + + + +Tag: PRD - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

PRD

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/prd/index.xml b/tags/prd/index.xml new file mode 100644 index 0000000..8cea8fb --- /dev/null +++ b/tags/prd/index.xml @@ -0,0 +1,71 @@ + + + + PRD on 3000ye's Blog + https://3000ye.com/tags/prd/ + Recent content in PRD on 3000ye's Blog + Hugo -- gohugo.io + en-us + Thu, 28 Dec 2023 14:18:06 +0800 + Happy Geek PRD + https://3000ye.com/p/happy-geek-prd/ + Thu, 28 Dec 2023 14:18:06 +0800 + + https://3000ye.com/p/happy-geek-prd/ + <img src="https://3000ye.com/p/happy-geek-prd/assets/prd.jpg" alt="Featured image of post Happy Geek PRD" /><h1 id="创作极客喜欢的-prd"> + <a href="#%e5%88%9b%e4%bd%9c%e6%9e%81%e5%ae%a2%e5%96%9c%e6%ac%a2%e7%9a%84-prd">#</a> + 创作极客喜欢的 PRD +</h1><p>产品需求文档(Product Requirements Document,PRD)是软件工程和互联网产品设计中的术语,是将商业需求文档(Business Requirements Document,BRD)和市场需求文档(Market Requirements Document,MRD)用更加专业的语言进行描述。</p> +<p>产品需求文档,是交互设计的基础。通常包含了产品的理念宗旨、功能需求、逻辑架构、页面设计等信息。产品需求文档的撰写,是软件工程的重要阶段,对于把握产品需求、保证产品经理、设计师和软件开发者等人员之间的沟通有据有着重要意义。</p> +<p>在实际的公司生产中,由于产品经理和开发人员之间往往会发生以下问题:</p> +<ul> +<li>产品经理辛苦写了 PRD,但开发人员却不用心看,或根本看不懂。</li> +<li>在开发过程中,开发人员反复确认相关细节,造成沟通的时间浪费。</li> +<li>开发完成后,项目结果远远不如预期。</li> +</ul> +<p>因此,如何写一份用户体验好、开发喜欢看、靠谱的需求文档成为每个产品经理的必修课。</p> +<div style='display: flex; justify-content: center;'> +<img src='assets/PRD 结构.png' alt='img' style='zoom:50%;' /> +</div> +<h2 id="产品简介"> + <a href="#%e4%ba%a7%e5%93%81%e7%ae%80%e4%bb%8b">#</a> + 产品简介 +</h2><h2 id="行业概要"> + <a href="#%e8%a1%8c%e4%b8%9a%e6%a6%82%e8%a6%81">#</a> + 行业概要 +</h2><h2 id="发行版本"> + <a href="#%e5%8f%91%e8%a1%8c%e7%89%88%e6%9c%ac">#</a> + 发行版本 +</h2><h3 id="排期表"> + <a href="#%e6%8e%92%e6%9c%9f%e8%a1%a8">#</a> + 排期表 +</h3><h3 id="产品设计"> + <a href="#%e4%ba%a7%e5%93%81%e8%ae%be%e8%ae%a1">#</a> + 产品设计 +</h3><h4 id="实体关系图"> + <a href="#%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb%e5%9b%be">#</a> + 实体关系图 +</h4><h4 id="用户角色权限表"> + <a href="#%e7%94%a8%e6%88%b7%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e8%a1%a8">#</a> + 用户角色权限表 +</h4><table> +<thead> +<tr> +<th>function</th> +<th>user1</th> +<th>user2</th> +</tr> +</thead> +<tbody> +<tr> +<td>func 01</td> +<td>get</td> +<td>get</td> +</tr> +</tbody> +</table> + + + + + diff --git a/tags/prd/page/1/index.html b/tags/prd/page/1/index.html new file mode 100644 index 0000000..37c7093 --- /dev/null +++ b/tags/prd/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/prd/ + + + + + + diff --git a/tags/ssh/index.html b/tags/ssh/index.html new file mode 100644 index 0000000..310242e --- /dev/null +++ b/tags/ssh/index.html @@ -0,0 +1,536 @@ + + + + +Tag: SSH - 3000ye's Blog + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

SSH

+ +
+
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/ssh/index.xml b/tags/ssh/index.xml new file mode 100644 index 0000000..88ad349 --- /dev/null +++ b/tags/ssh/index.xml @@ -0,0 +1,95 @@ + + + + SSH on 3000ye's Blog + https://3000ye.com/tags/ssh/ + Recent content in SSH on 3000ye's Blog + Hugo -- gohugo.io + en-us + Tue, 26 Dec 2023 16:23:26 +0800 + SSH Key Agent + https://3000ye.com/p/ssh-key-agent/ + Tue, 26 Dec 2023 16:23:26 +0800 + + https://3000ye.com/p/ssh-key-agent/ + <img src="https://3000ye.com/p/ssh-key-agent/assets/gitssh.png" alt="Featured image of post SSH Key Agent" /><h1 id="为不同的-git-服务生成不同的-ssh-key"> + <a href="#%e4%b8%ba%e4%b8%8d%e5%90%8c%e7%9a%84-git-%e6%9c%8d%e5%8a%a1%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 为不同的 Git 服务生成不同的 SSH Key +</h1><p>在一台设备上使用 Git 托管代码时,我们可能会遇到以下需求:</p> +<ul> +<li>我有多个 Git 平台(Github, Gitlab, &hellip;)的账号,每个平台的账号都需要一个 SSH Key。</li> +<li>在同一个 Git 平台,我有多个账号,每个账号都需要一个 SSH Key。</li> +<li>不同平台的账号,可能使用相同邮箱进行注册。</li> +</ul> +<p>这时,如何生成和管理 SSH Key 成为一个问题。</p> +<h2 id="设置局部-git-信息"> + <a href="#%e8%ae%be%e7%bd%ae%e5%b1%80%e9%83%a8-git-%e4%bf%a1%e6%81%af">#</a> + 设置局部 Git 信息 +</h2><p>在不同文件夹下,都可以设置不同的局部 Git 信息:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config user.name <span class="s2">&#34;name&#34;</span> +</span></span><span class="line"><span class="cl">git config user.email <span class="s2">&#34;email&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="开启-ssh-agent"> + <a href="#%e5%bc%80%e5%90%af-ssh-agent">#</a> + 开启 SSH Agent +</h2><p>开启 SSH Agent 对 SSH 执行代理,用于缓存私钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="生成不同的-ssh-key"> + <a href="#%e7%94%9f%e6%88%90%e4%b8%8d%e5%90%8c%e7%9a%84-ssh-key">#</a> + 生成不同的 SSH Key +</h2><p>指定文件名,生成不同的 SSH Key,即使相同邮箱也可以进行区分:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -f ~/.ssh/id_rsa_name -C <span class="s2">&#34;your_email@example.com&#34;</span> +</span></span></code></pre></td></tr></table> +</div> +</div><p>将生成的私钥添加进代理:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-add ~/.ssh/id_rsa_name +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># MacOS</span> +</span></span><span class="line"><span class="cl">ssh-add --apple-use-keychain ~/.ssh/id_rsa_name +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="将公钥添加到-git-平台"> + <a href="#%e5%b0%86%e5%85%ac%e9%92%a5%e6%b7%bb%e5%8a%a0%e5%88%b0-git-%e5%b9%b3%e5%8f%b0">#</a> + 将公钥添加到 Git 平台 +</h2><p>首先复制 SSH 公钥:</p> +<div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat ~/.ssh/id_rsa_name.pub +</span></span></code></pre></td></tr></table> +</div> +</div><p>然后到 Git 平台中,添加该公钥。</p> + + + + + diff --git a/tags/ssh/page/1/index.html b/tags/ssh/page/1/index.html new file mode 100644 index 0000000..8d8826e --- /dev/null +++ b/tags/ssh/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/ssh/ + + + + + + diff --git a/tags/test-tag/index.html b/tags/test-tag/index.html new file mode 100644 index 0000000..5f2c56f --- /dev/null +++ b/tags/test-tag/index.html @@ -0,0 +1,547 @@ + + + + +Tag: Test Tag - 3000ye's Blog + + + + + + + + + + + + + + + + +
+ + + + +
+
+

+ + Tags + +

+ +
+
+

1 page

+

Test Tag

+ +

Test pages in test tag.

+ +
+
+ + +
+ +
+
+ +
+ + + +
+
+ + +
+ Built with Hugo
+ Theme Stack designed by Jimmy +
+
+ + +
+
+ + + + + diff --git a/tags/test-tag/index.xml b/tags/test-tag/index.xml new file mode 100644 index 0000000..373f760 --- /dev/null +++ b/tags/test-tag/index.xml @@ -0,0 +1,78 @@ + + + + Test Tag on 3000ye's Blog + https://3000ye.com/tags/test-tag/ + Recent content in Test Tag on 3000ye's Blog + Hugo -- gohugo.io + en-us + Sun, 08 Oct 2023 14:30:49 +0000 + Test + https://3000ye.com/p/test/ + Sun, 08 Oct 2023 14:30:49 +0000 + + https://3000ye.com/p/test/ + <img src="https://3000ye.com/p/test/nord.jpg" alt="Featured image of post Test" /><h1 id="test-page"> + <a href="#test-page">#</a> + Test page +</h1><h2 id="picture"> + <a href="#picture">#</a> + picture +</h2><p><img src="https://3000ye.com/p/test/nord.jpg" + width="5472" + height="2976" + srcset="https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_480x0_resize_q75_box.jpg 480w, https://3000ye.com/p/test/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_1024x0_resize_q75_box.jpg 1024w" + loading="lazy" + + alt="nord" + + + class="gallery-image" + data-flex-grow="183" + data-flex-basis="441px" + +></p> +<h2 id="math"> + <a href="#math">#</a> + math +</h2><p>$$ +\varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } +$$</p> +<h2 id="code"> + <a href="#code">#</a> + code +</h2><div class="highlight"><div class="chroma"> +<table class="lntable"><tr><td class="lntd"> +<pre tabindex="0" class="chroma"><code><span class="lnt">1 +</span><span class="lnt">2 +</span><span class="lnt">3 +</span><span class="lnt">4 +</span><span class="lnt">5 +</span><span class="lnt">6 +</span><span class="lnt">7 +</span><span class="lnt">8 +</span><span class="lnt">9 +</span></code></pre></td> +<td class="lntd"> +<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;bits/stdc++.h&#34;</span><span class="cp"> +</span></span></span><span class="line"><span class="cl"><span class="cp"></span> +</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello World!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre></td></tr></table> +</div> +</div><h2 id="test-latex"> + <a href="#test-latex">#</a> + Test $\LaTeX$ +</h2><h2 id="frac23"> + <a href="#frac23">#</a> + $\frac{2}{3}$ +</h2> + + + + diff --git a/tags/test-tag/nord.jpg b/tags/test-tag/nord.jpg new file mode 100644 index 0000000..9776f86 Binary files /dev/null and b/tags/test-tag/nord.jpg differ diff --git a/tags/test-tag/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg b/tags/test-tag/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg new file mode 100644 index 0000000..2b077ad Binary files /dev/null and b/tags/test-tag/nord_hu694f0055e3faf079179eb7eb8ca96cd4_877866_120x120_fill_q75_box_smart1.jpg differ diff --git a/tags/test-tag/page/1/index.html b/tags/test-tag/page/1/index.html new file mode 100644 index 0000000..dcb167a --- /dev/null +++ b/tags/test-tag/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://3000ye.com/tags/test-tag/ + + + + + + diff --git a/ts/main.js b/ts/main.js new file mode 100644 index 0000000..91ab333 --- /dev/null +++ b/ts/main.js @@ -0,0 +1,11 @@ +(()=>{var g=class e{galleryUID;items=[];constructor(t,r=1){if(window.PhotoSwipe==null||window.PhotoSwipeUI_Default==null){console.error("PhotoSwipe lib not loaded.");return}this.galleryUID=r,e.createGallery(t),this.loadItems(t),this.bindClick()}loadItems(t){this.items=[];let r=t.querySelectorAll("figure.gallery-image");for(let i of r){let n=i.querySelector("figcaption"),o=i.querySelector("img"),s={w:parseInt(o.getAttribute("width")),h:parseInt(o.getAttribute("height")),src:o.src,msrc:o.getAttribute("data-thumb")||o.src,el:i};n&&(s.title=n.innerHTML),this.items.push(s)}}static createGallery(t){let r=t.querySelectorAll("img.gallery-image");for(let o of Array.from(r)){let s=o.closest("p");if(!s||!t.contains(s)||(s.textContent.trim()==""&&s.classList.add("no-text"),!s.classList.contains("no-text")))continue;let d=o.parentElement.tagName=="A",m=o,a=document.createElement("figure");if(a.style.setProperty("flex-grow",o.getAttribute("data-flex-grow")||"1"),a.style.setProperty("flex-basis",o.getAttribute("data-flex-basis")||"0"),d&&(m=o.parentElement),m.parentElement.insertBefore(a,m),a.appendChild(m),o.hasAttribute("alt")){let l=document.createElement("figcaption");l.innerText=o.getAttribute("alt"),a.appendChild(l)}if(!d){a.className="gallery-image";let l=document.createElement("a");l.href=o.src,l.setAttribute("target","_blank"),o.parentNode.insertBefore(l,o),l.appendChild(o)}}let i=t.querySelectorAll("figure.gallery-image"),n=[];for(let o of i)n.length?o.previousElementSibling===n[n.length-1]?n.push(o):n.length&&(e.wrap(n),n=[o]):n=[o];n.length>0&&e.wrap(n)}static wrap(t){let r=document.createElement("div");r.className="gallery";let i=t[0].parentNode,n=t[0];i.insertBefore(r,n);for(let o of t)r.appendChild(o)}open(t){let r=document.querySelector(".pswp");new window.PhotoSwipe(r,window.PhotoSwipeUI_Default,this.items,{index:t,galleryUID:this.galleryUID,getThumbBoundsFn:n=>{let o=this.items[n].el.getElementsByTagName("img")[0],s=window.pageYOffset||document.documentElement.scrollTop,c=o.getBoundingClientRect();return{x:c.left,y:c.top+s,w:c.width}}}).init()}bindClick(){for(let[t,r]of this.items.entries())r.el.querySelector("a").addEventListener("click",n=>{n.preventDefault(),this.open(t)})}},b=g;var u={};if(localStorage.hasOwnProperty("StackColorsCache"))try{u=JSON.parse(localStorage.getItem("StackColorsCache"))}catch{u={}}async function S(e,t,r){if(!e)return await Vibrant.from(r).getPalette();if(!u.hasOwnProperty(e)||u[e].hash!==t){let i=await Vibrant.from(r).getPalette();u[e]={hash:t,Vibrant:{hex:i.Vibrant.hex,rgb:i.Vibrant.rgb,bodyTextColor:i.Vibrant.bodyTextColor},DarkMuted:{hex:i.DarkMuted.hex,rgb:i.DarkMuted.rgb,bodyTextColor:i.DarkMuted.bodyTextColor}},localStorage.setItem("StackColorsCache",JSON.stringify(u))}return u[e]}var D=(e,t=500)=>{e.classList.add("transiting"),e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=t+"ms",e.style.height=e.offsetHeight+"px",e.offsetHeight,e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0",window.setTimeout(()=>{e.classList.remove("show"),e.style.removeProperty("height"),e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom"),e.style.removeProperty("overflow"),e.style.removeProperty("transition-duration"),e.style.removeProperty("transition-property"),e.classList.remove("transiting")},t)},q=(e,t=500)=>{e.classList.add("transiting"),e.style.removeProperty("display"),e.classList.add("show");let r=e.offsetHeight;e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0",e.offsetHeight,e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=t+"ms",e.style.height=r+"px",e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom"),window.setTimeout(()=>{e.style.removeProperty("height"),e.style.removeProperty("overflow"),e.style.removeProperty("transition-duration"),e.style.removeProperty("transition-property"),e.classList.remove("transiting")},t)},B=(e,t=500)=>window.getComputedStyle(e).display==="none"?q(e,t):D(e,t);function v(){let e=document.getElementById("toggle-menu");e&&e.addEventListener("click",()=>{document.getElementById("main-menu").classList.contains("transiting")||(document.body.classList.toggle("show-menu"),B(document.getElementById("main-menu"),300),e.classList.toggle("is-active"))})}function N(e,t,r){var i=document.createElement(e);for(let n in t)if(n&&t.hasOwnProperty(n)){let o=t[n];n=="dangerouslySetInnerHTML"?i.innerHTML=o.__html:o===!0?i.setAttribute(n,n):o!==!1&&o!=null&&i.setAttribute(n,o.toString())}for(let n=2;n{this.isDark()?this.currentScheme="light":this.currentScheme="dark",this.setBodyClass(),this.currentScheme==this.systemPreferScheme&&(this.currentScheme="auto"),this.saveScheme()})}isDark(){return this.currentScheme=="dark"||this.currentScheme=="auto"&&this.systemPreferScheme=="dark"}dispatchEvent(t){let r=new CustomEvent("onColorSchemeChange",{detail:t});window.dispatchEvent(r)}setBodyClass(){this.isDark()?document.documentElement.dataset.scheme="dark":document.documentElement.dataset.scheme="light",this.dispatchEvent(document.documentElement.dataset.scheme)}getSavedScheme(){let t=localStorage.getItem(this.localStorageKey);return t=="light"||t=="dark"||t=="auto"?t:"auto"}bindMatchMedia(){window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",t=>{t.matches?this.systemPreferScheme="dark":this.systemPreferScheme="light",this.setBodyClass()})}},E=y;function p(e){let t;return()=>{t&&window.cancelAnimationFrame(t),t=window.requestAnimationFrame(()=>e())}}var O=".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]",T="#TableOfContents",L="#TableOfContents li",k="active-class";function V(e,t){let r=e.querySelector("a").offsetHeight,i=e.offsetTop-t.offsetHeight/2+r/2-t.offsetTop;i<0&&(i=0),t.scrollTo({top:i,behavior:"smooth"})}function U(e){let t={};return e.forEach(r=>{let n=r.querySelector("a").getAttribute("href");n.startsWith("#")&&(t[n.slice(1)]=r)}),t}function C(e){let t=[];return e.forEach(r=>{t.push({id:r.id,offset:r.offsetTop})}),t.sort((r,i)=>r.offset-i.offset),t}function M(){let e=document.querySelectorAll(O);if(!e){console.warn("No header matched query",e);return}let t=document.querySelector(T);if(!t){console.warn("No toc matched query",T);return}let r=document.querySelectorAll(L);if(!r){console.warn("No navigation matched query",L);return}let i=C(e),n=!1;t.addEventListener("mouseenter",p(()=>n=!0)),t.addEventListener("mouseleave",p(()=>n=!1));let o,s=U(r);function c(){let m=document.documentElement.scrollTop||document.body.scrollTop,a;i.forEach(f=>{m>=f.offset-20&&(a=document.getElementById(f.id))});let l;a&&(l=s[a.id]),a&&!l?console.debug("No link found for section",a):l!==o&&(o&&o.classList.remove(k),l&&(l.classList.add(k),n||V(l,t)),o=l)}window.addEventListener("scroll",p(c));function d(){i=C(e),c()}window.addEventListener("resize",p(d))}var $="a[href]";function P(){document.querySelectorAll($).forEach(e=>{e.getAttribute("href").startsWith("#")&&e.addEventListener("click",r=>{r.preventDefault();let i=decodeURI(e.getAttribute("href").substring(1)),n=document.getElementById(i),o=n.getBoundingClientRect().top-document.documentElement.getBoundingClientRect().top;window.history.pushState({},"",e.getAttribute("href")),scrollTo({top:o,behavior:"smooth"})})})}var x={init:()=>{v();let e=document.querySelector(".article-content");e&&(new b(e),P(),M());let t=document.querySelector(".article-list--tile");t&&new IntersectionObserver(async(s,c)=>{s.forEach(d=>{if(!d.isIntersecting)return;c.unobserve(d.target),d.target.querySelectorAll("article.has-image").forEach(async a=>{let l=a.querySelector("img"),f=l.src,H=l.getAttribute("data-key"),I=l.getAttribute("data-hash"),A=a.querySelector(".article-details"),h=await S(H,I,f);A.style.background=` + linear-gradient(0deg, + rgba(${h.DarkMuted.rgb[0]}, ${h.DarkMuted.rgb[1]}, ${h.DarkMuted.rgb[2]}, 0.5) 0%, + rgba(${h.Vibrant.rgb[0]}, ${h.Vibrant.rgb[1]}, ${h.Vibrant.rgb[2]}, 0.75) 100%)`})})}).observe(t);let r=document.querySelectorAll(".article-content div.highlight"),i="Copy",n="Copied!";r.forEach(o=>{let s=document.createElement("button");s.innerHTML=i,s.classList.add("copyCodeButton"),o.appendChild(s);let c=o.querySelector("code[data-lang]");c&&s.addEventListener("click",()=>{navigator.clipboard.writeText(c.textContent).then(()=>{s.textContent=n,setTimeout(()=>{s.textContent=i},1e3)}).catch(d=>{alert(d),console.log("Something went wrong",d)})})}),new E(document.getElementById("dark-mode-toggle"))}};window.addEventListener("load",()=>{setTimeout(function(){x.init()},0)});window.Stack=x;window.createElement=w;})(); +/*! +* Hugo Theme Stack +* +* @author: Jimmy Cai +* @website: https://jimmycai.com +* @link: https://github.com/CaiJimmy/hugo-theme-stack +*/ diff --git a/ts/search.js b/ts/search.js new file mode 100644 index 0000000..e22d8ef --- /dev/null +++ b/ts/search.js @@ -0,0 +1 @@ +(()=>{var m={"&":"&","<":"<",">":">",'"':""","\u2026":"…"};function T(l){return m[l]||l}function d(l){return l.replace(/[&<>"]/g,T)}function w(l){return l.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}var g=class l{data;form;input;list;resultTitle;resultTitleTemplate;constructor({form:t,input:e,list:r,resultTitle:o,resultTitleTemplate:n}){this.form=t,this.input=e,this.list=r,this.resultTitle=o,this.resultTitleTemplate=n,this.handleQueryString(),this.bindQueryStringChange(),this.bindSearchForm()}static processMatches(t,e,r=!0,o=140,n=20){e.sort((a,s)=>a.start-s.start);let h=0,i=0,c=0,u=[];for(;hi?(u.push(`${d(t.substring(i,i+n))} [...] `),u.push(`${d(t.substring(a.start-n,a.start))}`),c+=n*2):(u.push(d(t.substring(i,a.start))),c+=a.start-i);let s=h+1,p=a.end;for(;s${d(t.substring(a.start,p))}`),c+=p-a.start,h=s,i=p,r&&c>o)break}if(i(i[h]=w(n),n.trim()!=="")).join("|"),"gi");for(let n of e){let h=[],i=[],c={...n,preview:"",matchCount:0},u=n.content.matchAll(o);for(let s of Array.from(u))i.push({start:s.index,end:s.index+s[0].length});let a=n.title.matchAll(o);for(let s of Array.from(a))h.push({start:s.index,end:s.index+s[0].length});h.length>0&&(c.title=l.processMatches(c.title,h,!1)),i.length>0?c.preview=l.processMatches(c.content,i):c.preview=d(c.content.substring(0,140)),c.matchCount=h.length+i.length,c.matchCount>0&&r.push(c)}return r.sort((n,h)=>h.matchCount-n.matchCount)}async doSearch(t){let e=performance.now(),r=await this.searchKeywords(t);this.clear();for(let n of r)this.list.append(l.render(n));let o=performance.now();this.resultTitle.innerText=this.generateResultTitle(r.length,((o-e)/1e3).toPrecision(1))}generateResultTitle(t,e){return this.resultTitleTemplate.replace("#PAGES_COUNT",t).replace("#TIME_SECONDS",e)}async getData(){if(!this.data){let t=this.form.dataset.json;this.data=await fetch(t).then(r=>r.json());let e=new DOMParser;for(let r of this.data)r.content=e.parseFromString(r.content,"text/html").body.innerText}return this.data}bindSearchForm(){let t="",e=r=>{r.preventDefault();let o=this.input.value.trim();if(l.updateQueryString(o,!0),o==="")return t="",this.clear();t!==o&&(t=o,this.doSearch(o.split(" ")))};this.input.addEventListener("input",e),this.input.addEventListener("compositionend",e)}clear(){this.list.innerHTML="",this.resultTitle.innerText=""}bindQueryStringChange(){window.addEventListener("popstate",t=>{this.handleQueryString()})}handleQueryString(){let e=new URL(window.location.toString()).searchParams.get("keyword");this.input.value=e,e?this.doSearch(e.split(" ")):this.clear()}static updateQueryString(t,e=!1){let r=new URL(window.location.toString());t===""?r.searchParams.delete("keyword"):r.searchParams.set("keyword",t),e?window.history.replaceState("","",r.toString()):window.history.pushState("","",r.toString())}static render(t){return createElement("article",null,createElement("a",{href:t.permalink},createElement("div",{class:"article-details"},createElement("h2",{class:"article-title",dangerouslySetInnerHTML:{__html:t.title}}),createElement("section",{class:"article-preview",dangerouslySetInnerHTML:{__html:t.preview}})),t.image&&createElement("div",{class:"article-image"},createElement("img",{src:t.image,loading:"lazy"}))))}};window.addEventListener("load",()=>{setTimeout(function(){let l=document.querySelector(".search-form"),t=l.querySelector("input"),e=document.querySelector(".search-result--list"),r=document.querySelector(".search-result--title");new g({form:l,input:t,list:e,resultTitle:r,resultTitleTemplate:window.searchResultTitleTemplate})},0)});var f=g;})();