-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
182 lines (87 loc) · 343 KB
/
local-search.xml
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>C++知识点</title>
<link href="/2020/07/05/CKnowledgement/"/>
<url>/2020/07/05/CKnowledgement/</url>
<content type="html"><![CDATA[<div class="markdown-body">C++面试知识点(Github镜像源)<a id="more"></a><div align="center">📖 Github   |   <a href="https://interview.huihut.com" target="_blank" rel="noopener">📚 Docsify</a></div> <br><div align="center">简体中文   |   <a href="https://github.com/huihut/interview/blob/master/README_en.md" target="_blank" rel="noopener">English</a></div> <br><p><b><details><summary>💡 关于</summary></b></p><p>📚 本仓库是面向 C/C++ 技术方向校招求职者、初学者的基础知识总结,包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。</p><p>💡 侧边目录支持方式:<a href="https://interview.huihut.com" target="_blank" rel="noopener">📚 Docsify 文档</a>、<a href="https://github.com/jawil/GayHub" target="_blank" rel="noopener">Github + TOC 导航</a>(<a href="https://raw.githubusercontent.com/huihut/interview/master/images/TOC预览.png" target="_blank" rel="noopener">TOC预览.png</a>)</p><p>📄 保存为 PDF 方式:使用 Chrome 浏览器打开 <a href="https://interview.huihut.com" target="_blank" rel="noopener">📚 Docsify 文档</a> 页面,缩起左侧目录-右键 - 打印 - 选择目标打印机是另存为PDF - 保存(<a href="https://raw.githubusercontent.com/huihut/interview/master/images/打印预览.png" target="_blank" rel="noopener">打印预览.png</a>)</p><p>🙏 仓库内容如有错误或改进欢迎 issue 或 pr,建议或讨论可在 <a href="https://github.com/huihut/interview/issues/12" target="_blank" rel="noopener">#12</a> 提出。由于本人水平有限,仓库中的知识点有来自本人原创、读书笔记、书籍、博文等,非原创均已标明出处,如有遗漏,请 issue 提出。本仓库遵循 <a href="https://github.com/huihut/interview/blob/master/LICENSE" target="_blank" rel="noopener">CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享)</a> 协议,转载请注明出处,不得用于商业目的。</p></details><h2 id="📑-目录"><a href="#📑-目录" class="headerlink" title="📑 目录"></a>📑 目录</h2><ul><li><a href="#cc">➕ C/C++</a></li><li><a href="#effective">⭐️ Effective</a></li><li><a href="#stl">📦 STL</a></li><li><a href="#data-structure">〽️ 数据结构</a></li><li><a href="#algorithm">⚡️ 算法</a></li><li><a href="#problems">❓ Problems</a></li><li><a href="#os">💻 操作系统</a></li><li><a href="#computer-network">☁️ 计算机网络</a></li><li><a href="#network-programming">🌩 网络编程</a></li><li><a href="#database">💾 数据库</a></li><li><a href="#design-pattern">📏 设计模式</a></li><li><a href="#link-loading-library">⚙️ 链接装载库</a></li><li><a href="#books">📚 书籍</a></li><li><a href="#cc-development-direction">🔱 C/C++ 发展方向</a></li><li><a href="#review-of-brush-questions-website">💯 复习刷题网站</a></li><li><a href="#interview-questions-experience">📝 面试题目经验</a></li><li><a href="#recruitment-time-post">📆 招聘时间岗位</a></li><li><a href="#recommend">👍 内推</a></li><li><a href="#contributor">👬 贡献者</a></li><li><a href="#support-sponsor">🍭 支持赞助</a></li></ul><p><a id="cc"></a></p><h2 id="➕-C-C"><a href="#➕-C-C" class="headerlink" title="➕ C/C++"></a>➕ C/C++</h2><h3 id="const"><a href="#const" class="headerlink" title="const"></a>const</h3><h4 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h4><ol><li>修饰变量,说明该变量不可以被改变;</li><li>修饰指针,分为指向常量的指针(pointer to const)和自身是常量的指针(常量指针,const pointer);</li><li>修饰引用,指向常量的引用(reference to const),用于形参类型,即避免了拷贝,又避免了函数对值的修改;</li><li>修饰成员函数,说明该成员函数内不能修改成员变量。</li></ol><h4 id="const-的指针与引用"><a href="#const-的指针与引用" class="headerlink" title="const 的指针与引用"></a>const 的指针与引用</h4><ul><li>指针<ul><li>指向常量的指针(pointer to const)</li><li>自身是常量的指针(常量指针,const pointer)</li></ul></li><li>引用<ul><li>指向常量的引用(reference to const)</li><li>没有 const reference,因为引用本身就是 const pointer</li></ul></li></ul><blockquote><p>(为了方便记忆可以想成)被 const 修饰(在 const 后面)的值不可改变,如下文使用例子中的 <code>p2</code>、<code>p3</code></p></blockquote><h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>const 使用</p><pre><code class="hljs cpp"><span class="hljs-comment">// 类</span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span></span><span class="hljs-class">{</span><span class="hljs-keyword">private</span>: <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> a; <span class="hljs-comment">// 常对象成员,只能在初始化列表赋值</span><span class="hljs-keyword">public</span>: <span class="hljs-comment">// 构造函数</span> A() : a(<span class="hljs-number">0</span>) { }; A(<span class="hljs-keyword">int</span> x) : a(x) { }; <span class="hljs-comment">// 初始化列表</span> <span class="hljs-comment">// const可用于对重载函数的区分</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getValue</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 普通成员函数</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getValue</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>; <span class="hljs-comment">// 常成员函数,不得修改类中的任何数据成员的值</span>};<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">function</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ <span class="hljs-comment">// 对象</span> A b; <span class="hljs-comment">// 普通对象,可以调用全部成员函数、更新常成员变量</span> <span class="hljs-keyword">const</span> A a; <span class="hljs-comment">// 常对象,只能调用常成员函数</span> <span class="hljs-keyword">const</span> A *p = &a; <span class="hljs-comment">// 指针变量,指向常对象</span> <span class="hljs-keyword">const</span> A &q = a; <span class="hljs-comment">// 指向常对象的引用</span> <span class="hljs-comment">// 指针</span> <span class="hljs-keyword">char</span> greeting[] = <span class="hljs-string">"Hello"</span>; <span class="hljs-keyword">char</span>* p1 = greeting; <span class="hljs-comment">// 指针变量,指向字符数组变量</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* p2 = greeting; <span class="hljs-comment">// 指针变量,指向字符数组常量(const 后面是 char,说明指向的字符(char)不可改变)</span> <span class="hljs-keyword">char</span>* <span class="hljs-keyword">const</span> p3 = greeting; <span class="hljs-comment">// 自身是常量的指针,指向字符数组变量(const 后面是 p3,说明 p3 指针自身不可改变)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* <span class="hljs-keyword">const</span> p4 = greeting; <span class="hljs-comment">// 自身是常量的指针,指向字符数组常量</span>}<span class="hljs-comment">// 函数</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">function1</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> Var)</span></span>; <span class="hljs-comment">// 传递过来的参数在函数内不可变</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">function2</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* Var)</span></span>; <span class="hljs-comment">// 参数指针所指内容为常量</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">function3</span><span class="hljs-params">(<span class="hljs-keyword">char</span>* <span class="hljs-keyword">const</span> Var)</span></span>; <span class="hljs-comment">// 参数指针为常量</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">function4</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span>& Var)</span></span>; <span class="hljs-comment">// 引用参数在函数内为常量</span><span class="hljs-comment">// 函数返回值</span><span class="hljs-function"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> <span class="hljs-title">function5</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 返回一个常数</span><span class="hljs-function"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span>* <span class="hljs-title">function6</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 返回一个指向常量的指针变量,使用:const int *p = function6();</span><span class="hljs-function"><span class="hljs-keyword">int</span>* <span class="hljs-keyword">const</span> <span class="hljs-title">function7</span><span class="hljs-params">()</span></span>; <span class="hljs-comment">// 返回一个指向变量的常指针,使用:int* const p = function7();</span></code></pre><h3 id="static"><a href="#static" class="headerlink" title="static"></a>static</h3><h4 id="作用-1"><a href="#作用-1" class="headerlink" title="作用"></a>作用</h4><ol><li>修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。</li><li>修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。</li><li>修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。</li><li>修饰成员函数,修饰成员函数使得不需要生成对象就可以访问该函数,但是在 static 函数内不能访问非静态成员。</li></ol><h3 id="this-指针"><a href="#this-指针" class="headerlink" title="this 指针"></a>this 指针</h3><ol><li><code>this</code> 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。</li><li>当对一个对象调用成员函数时,编译程序先将对象的地址赋给 <code>this</code> 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 <code>this</code> 指针。</li><li>当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。</li><li><code>this</code> 指针被隐含地声明为: <code>ClassName *const this</code>,这意味着不能给 <code>this</code> 指针赋值;在 <code>ClassName</code> 类的 <code>const</code> 成员函数中,<code>this</code> 指针的类型为:<code>const ClassName* const</code>,这说明不能对 <code>this</code> 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);</li><li><code>this</code> 并不是一个常规变量,而是个右值,所以不能取得 <code>this</code> 的地址(不能 <code>&this</code>)。</li><li>在以下场景中,经常需要显式引用 <code>this</code> 指针:<ol><li>为实现对象的链式引用;</li><li>为避免对同一对象进行赋值操作;</li><li>在实现一些数据结构时,如 <code>list</code>。</li></ol></li></ol><h3 id="inline-内联函数"><a href="#inline-内联函数" class="headerlink" title="inline 内联函数"></a>inline 内联函数</h3><h4 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h4><ul><li>相当于把内联函数里面的内容写在调用内联函数处;</li><li>相当于不用执行进入函数的步骤,直接执行函数体;</li><li>相当于宏,却比宏多了类型检查,真正具有函数特性;</li><li>编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数;</li><li>在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。</li></ul><h4 id="使用-1"><a href="#使用-1" class="headerlink" title="使用"></a>使用</h4><p>inline 使用</p><pre><code class="hljs cpp"><span class="hljs-comment">// 声明1(加 inline,建议使用)</span><span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">int</span> <span class="hljs-title">functionName</span><span class="hljs-params">(<span class="hljs-keyword">int</span> first, <span class="hljs-keyword">int</span> second,...)</span></span>;<span class="hljs-comment">// 声明2(不加 inline)</span><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">functionName</span><span class="hljs-params">(<span class="hljs-keyword">int</span> first, <span class="hljs-keyword">int</span> second,...)</span></span>;<span class="hljs-comment">// 定义</span><span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">int</span> <span class="hljs-title">functionName</span><span class="hljs-params">(<span class="hljs-keyword">int</span> first, <span class="hljs-keyword">int</span> second,...)</span> </span>{<span class="hljs-comment">/****/</span>};<span class="hljs-comment">// 类内定义,隐式内联</span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span> {</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">doA</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; } <span class="hljs-comment">// 隐式内联</span>}<span class="hljs-comment">// 类外定义,需要显式内联</span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span> {</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">doA</span><span class="hljs-params">()</span></span>;}<span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">int</span> <span class="hljs-title">A::doA</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; } <span class="hljs-comment">// 需要显式内联</span></code></pre><h4 id="编译器对-inline-函数的处理步骤"><a href="#编译器对-inline-函数的处理步骤" class="headerlink" title="编译器对 inline 函数的处理步骤"></a>编译器对 inline 函数的处理步骤</h4><ol><li>将 inline 函数体复制到 inline 函数调用点处; </li><li>为所用 inline 函数中的局部变量分配内存空间; </li><li>将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中; </li><li>如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。</li></ol><h4 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h4><p>优点</p><ol><li>内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。</li><li>内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。 </li><li>在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。</li><li>内联函数在运行时可调试,而宏定义不可以。</li></ol><p>缺点</p><ol><li>代码膨胀。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。</li><li>inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译,不像 non-inline 可以直接链接。</li><li>是否内联,程序员不可控。内联函数只是对编译器的建议,是否对函数内联,决定权在于编译器。</li></ol><h4 id="虚函数(virtual)可以是内联函数(inline)吗?"><a href="#虚函数(virtual)可以是内联函数(inline)吗?" class="headerlink" title="虚函数(virtual)可以是内联函数(inline)吗?"></a>虚函数(virtual)可以是内联函数(inline)吗?</h4><blockquote><p><a href="http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html" target="_blank" rel="noopener">Are “inline virtual” member functions ever actually “inlined”?</a></p></blockquote><ul><li>虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。</li><li>内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。</li><li><code>inline virtual</code> 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 <code>Base::who()</code>),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。</li></ul><p>虚函数内联使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream> </span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span></span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>:<span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">who</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{<span class="hljs-built_in">cout</span> << <span class="hljs-string">"I am Base\n"</span>;}<span class="hljs-keyword">virtual</span> ~Base() {}};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Derived</span> :</span> <span class="hljs-keyword">public</span> Base{<span class="hljs-keyword">public</span>:<span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">void</span> <span class="hljs-title">who</span><span class="hljs-params">()</span> <span class="hljs-comment">// 不写inline时隐式内联</span></span><span class="hljs-function"></span>{<span class="hljs-built_in">cout</span> << <span class="hljs-string">"I am Derived\n"</span>;}};<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{<span class="hljs-comment">// 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 </span>Base b;b.who();<span class="hljs-comment">// 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 </span>Base *ptr = <span class="hljs-keyword">new</span> Derived();ptr->who();<span class="hljs-comment">// 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。</span><span class="hljs-keyword">delete</span> ptr;ptr = <span class="hljs-literal">nullptr</span>;system(<span class="hljs-string">"pause"</span>);<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h3><pre><code class="hljs cpp"><span class="hljs-keyword">volatile</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">10</span>;</code></pre><ul><li>volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。</li><li>volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)</li><li>const 可以是 volatile (如只读的状态寄存器)</li><li>指针可以是 volatile</li></ul><h3 id="assert"><a href="#assert" class="headerlink" title="assert()"></a>assert()</h3><p>断言,是宏,而非函数。assert 宏的原型定义在 <code><assert.h></code>(C)、<code><cassert></code>(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 <code>NDEBUG</code> 来关闭 assert,但是需要在源代码的开头,<code>include <assert.h></code> 之前。</p><p>assert() 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> NDEBUG <span class="hljs-comment">// 加上这行,则 assert 不可用</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><assert.h></span></span>assert( p != <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">// assert 不可用</span></code></pre><h3 id="sizeof"><a href="#sizeof" class="headerlink" title="sizeof()"></a>sizeof()</h3><ul><li>sizeof 对数组,得到整个数组所占空间大小。</li><li>sizeof 对指针,得到指针本身所占空间大小。</li></ul><h3 id="pragma-pack-n"><a href="#pragma-pack-n" class="headerlink" title="#pragma pack(n)"></a>#pragma pack(n)</h3><p>设定结构体、联合以及类成员变量以 n 字节方式对齐</p><p>#pragma pack(n) 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> pack(push) <span class="hljs-comment">// 保存对齐状态</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> pack(4) <span class="hljs-comment">// 设定为 4 字节对齐</span></span><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">test</span></span><span class="hljs-class">{</span> <span class="hljs-keyword">char</span> m1; <span class="hljs-keyword">double</span> m4; <span class="hljs-keyword">int</span> m3;};<span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> pack(pop) <span class="hljs-comment">// 恢复对齐状态</span></span></code></pre><h3 id="位域"><a href="#位域" class="headerlink" title="位域"></a>位域</h3><pre><code class="hljs cpp">Bit mode: <span class="hljs-number">2</span>; <span class="hljs-comment">// mode 占 2 位</span></code></pre><p>类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。</p><ul><li>位域在内存中的布局是与机器有关的</li><li>位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定</li><li>取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域</li></ul><h3 id="extern-“C”"><a href="#extern-“C”" class="headerlink" title="extern “C”"></a>extern “C”</h3><ul><li>被 extern 限定的函数或变量是 extern 类型的</li><li>被 <code>extern "C"</code> 修饰的变量和函数是按照 C 语言方式编译和链接的</li></ul><p><code>extern "C"</code> 的作用是让 C++ 编译器将 <code>extern "C"</code> 声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。</p><p>extern “C” 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> __cplusplus</span><span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span> {<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span><span class="hljs-function"><span class="hljs-keyword">void</span> *<span class="hljs-title">memset</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">size_t</span>)</span></span>;<span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> __cplusplus</span>}<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code></pre><h3 id="struct-和-typedef-struct"><a href="#struct-和-typedef-struct" class="headerlink" title="struct 和 typedef struct"></a>struct 和 typedef struct</h3><h4 id="C-中"><a href="#C-中" class="headerlink" title="C 中"></a>C 中</h4><pre><code class="hljs c"><span class="hljs-comment">// c</span><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> {</span> <span class="hljs-keyword">int</span> age; } S;</code></pre><p>等价于</p><pre><code class="hljs c"><span class="hljs-comment">// c</span><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> {</span> <span class="hljs-keyword">int</span> age; };<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> <span class="hljs-title">S</span>;</span></code></pre><p>此时 <code>S</code> 等价于 <code>struct Student</code>,但两个标识符名称空间不相同。</p><p>另外还可以定义与 <code>struct Student</code> 不冲突的 <code>void Student() {}</code>。</p><h4 id="C-中-1"><a href="#C-中-1" class="headerlink" title="C++ 中"></a>C++ 中</h4><p>由于编译器定位符号的规则(搜索规则)改变,导致不同于C语言。</p><p>一、如果在类标识符空间定义了 <code>struct Student {...};</code>,使用 <code>Student me;</code> 时,编译器将搜索全局标识符表,<code>Student</code> 未找到,则在类标识符内搜索。</p><p>即表现为可以使用 <code>Student</code> 也可以使用 <code>struct Student</code>,如下:</p><pre><code class="hljs cpp"><span class="hljs-comment">// cpp</span><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> {</span> <span class="hljs-keyword">int</span> age; };<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">f</span><span class="hljs-params">( Student me )</span></span>; <span class="hljs-comment">// 正确,"struct" 关键字可省略</span></code></pre><p>二、若定义了与 <code>Student</code> 同名函数之后,则 <code>Student</code> 只代表函数,不代表结构体,如下:</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> {</span> <span class="hljs-keyword">int</span> age; } S;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Student</span><span class="hljs-params">()</span> </span>{} <span class="hljs-comment">// 正确,定义后 "Student" 只代表此函数</span><span class="hljs-comment">//void S() {} // 错误,符号 "S" 已经被定义为一个 "struct Student" 的别名</span><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{ Student(); <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> <span class="hljs-title">me</span>;</span> <span class="hljs-comment">// 或者 "S me";</span> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="C-中-struct-和-class"><a href="#C-中-struct-和-class" class="headerlink" title="C++ 中 struct 和 class"></a>C++ 中 struct 和 class</h3><p>总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。</p><h4 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h4><ul><li>最本质的一个区别就是默认的访问控制<ol><li>默认的继承访问权限。struct 是 public 的,class 是 private 的。 </li><li>struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。</li></ol></li></ul><h3 id="union-联合"><a href="#union-联合" class="headerlink" title="union 联合"></a>union 联合</h3><p>联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点:</p><ul><li>默认访问控制符为 public</li><li>可以含有构造函数、析构函数</li><li>不能含有引用类型的成员</li><li>不能继承自其他类,不能作为基类</li><li>不能含有虚函数</li><li>匿名 union 在定义所在作用域可直接访问 union 成员</li><li>匿名 union 不能包含 protected 成员或 private 成员</li><li>全局匿名联合必须是静态(static)的</li></ul><p>union 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span><span class="hljs-meta-string"><iostream></span></span><span class="hljs-keyword">union</span> UnionTest { UnionTest() : i(<span class="hljs-number">10</span>) {}; <span class="hljs-keyword">int</span> i; <span class="hljs-keyword">double</span> d;};<span class="hljs-keyword">static</span> <span class="hljs-keyword">union</span> { <span class="hljs-keyword">int</span> i; <span class="hljs-keyword">double</span> d;};<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{ UnionTest u; <span class="hljs-keyword">union</span> { <span class="hljs-keyword">int</span> i; <span class="hljs-keyword">double</span> d; }; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << u.i << <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>; <span class="hljs-comment">// 输出 UnionTest 联合的 10</span> ::i = <span class="hljs-number">20</span>; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << ::i << <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>; <span class="hljs-comment">// 输出全局静态匿名联合的 20</span> i = <span class="hljs-number">30</span>; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << i << <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>; <span class="hljs-comment">// 输出局部匿名联合的 30</span> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="C-实现-C-类"><a href="#C-实现-C-类" class="headerlink" title="C 实现 C++ 类"></a>C 实现 C++ 类</h3><p>C 实现 C++ 的面向对象特性(封装、继承、多态)</p><ul><li>封装:使用函数指针把属性与方法封装到结构体中</li><li>继承:结构体嵌套</li><li>多态:父类与子类方法的函数指针不同</li></ul><blockquote><p><a href="https://stackoverflow.com/a/351745" target="_blank" rel="noopener">Can you write object-oriented code in C? [closed]</a></p></blockquote><h3 id="explicit(显式)关键字"><a href="#explicit(显式)关键字" class="headerlink" title="explicit(显式)关键字"></a>explicit(显式)关键字</h3><ul><li>explicit 修饰构造函数时,可以防止隐式转换和复制初始化</li><li>explicit 修饰转换函数时,可以防止隐式转换,但 <a href="https://zh.cppreference.com/w/cpp/language/implicit_conversion" target="_blank" rel="noopener">按语境转换</a> 除外</li></ul><p>explicit 使用</p><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">A</span></span><span class="hljs-class">{</span>A(<span class="hljs-keyword">int</span>) { }<span class="hljs-function"><span class="hljs-keyword">operator</span> <span class="hljs-title">bool</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; }};<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">B</span></span><span class="hljs-class">{</span><span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">B</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> </span>{}<span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">bool</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; }};<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">doA</span><span class="hljs-params">(A a)</span> </span>{}<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">doB</span><span class="hljs-params">(B b)</span> </span>{}<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{<span class="hljs-function">A <span class="hljs-title">a1</span><span class="hljs-params">(<span class="hljs-number">1</span>)</span></span>;<span class="hljs-comment">// OK:直接初始化</span>A a2 = <span class="hljs-number">1</span>;<span class="hljs-comment">// OK:复制初始化</span>A a3{ <span class="hljs-number">1</span> };<span class="hljs-comment">// OK:直接列表初始化</span>A a4 = { <span class="hljs-number">1</span> };<span class="hljs-comment">// OK:复制列表初始化</span>A a5 = (A)<span class="hljs-number">1</span>;<span class="hljs-comment">// OK:允许 static_cast 的显式转换 </span>doA(<span class="hljs-number">1</span>);<span class="hljs-comment">// OK:允许从 int 到 A 的隐式转换</span><span class="hljs-keyword">if</span> (a1);<span class="hljs-comment">// OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换</span><span class="hljs-keyword">bool</span> a6(a1);<span class="hljs-comment">// OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换</span><span class="hljs-keyword">bool</span> a7 = a1;<span class="hljs-comment">// OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换</span><span class="hljs-keyword">bool</span> a8 = <span class="hljs-keyword">static_cast</span><<span class="hljs-keyword">bool</span>>(a1); <span class="hljs-comment">// OK :static_cast 进行直接初始化</span><span class="hljs-function">B <span class="hljs-title">b1</span><span class="hljs-params">(<span class="hljs-number">1</span>)</span></span>;<span class="hljs-comment">// OK:直接初始化</span>B b2 = <span class="hljs-number">1</span>;<span class="hljs-comment">// 错误:被 explicit 修饰构造函数的对象不可以复制初始化</span>B b3{ <span class="hljs-number">1</span> };<span class="hljs-comment">// OK:直接列表初始化</span>B b4 = { <span class="hljs-number">1</span> };<span class="hljs-comment">// 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化</span>B b5 = (B)<span class="hljs-number">1</span>;<span class="hljs-comment">// OK:允许 static_cast 的显式转换</span>doB(<span class="hljs-number">1</span>);<span class="hljs-comment">// 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换</span><span class="hljs-keyword">if</span> (b1);<span class="hljs-comment">// OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换</span><span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">b6</span><span class="hljs-params">(b1)</span></span>;<span class="hljs-comment">// OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换</span><span class="hljs-keyword">bool</span> b7 = b1;<span class="hljs-comment">// 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换</span><span class="hljs-keyword">bool</span> b8 = <span class="hljs-keyword">static_cast</span><<span class="hljs-keyword">bool</span>>(b1); <span class="hljs-comment">// OK:static_cast 进行直接初始化</span><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="friend-友元类和友元函数"><a href="#friend-友元类和友元函数" class="headerlink" title="friend 友元类和友元函数"></a>friend 友元类和友元函数</h3><ul><li>能访问私有成员 </li><li>破坏封装性</li><li>友元关系不可传递</li><li>友元关系的单向性</li><li>友元声明的形式及数量不受限制</li></ul><h3 id="using"><a href="#using" class="headerlink" title="using"></a>using</h3><h4 id="using-声明"><a href="#using-声明" class="headerlink" title="using 声明"></a>using 声明</h4><p>一条 <code>using 声明</code> 语句一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字。如:</p><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> namespace_name::name;</code></pre><h4 id="构造函数的-using-声明"><a href="#构造函数的-using-声明" class="headerlink" title="构造函数的 using 声明"></a>构造函数的 using 声明</h4><p>在 C++11 中,派生类能够重用其直接基类定义的构造函数。</p><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Derived</span> :</span> Base {<span class="hljs-keyword">public</span>: <span class="hljs-keyword">using</span> Base::Base; <span class="hljs-comment">/* ... */</span>};</code></pre><p>如上 using 声明,对于基类的每个构造函数,编译器都生成一个与之对应(形参列表完全相同)的派生类构造函数。生成如下类型构造函数:</p><pre><code class="hljs cpp">Derived(parms) : Base(args) { }</code></pre><h4 id="using-指示"><a href="#using-指示" class="headerlink" title="using 指示"></a>using 指示</h4><p><code>using 指示</code> 使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。如:</p><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> namespace_name name;</code></pre><h4 id="尽量少使用-using-指示-污染命名空间"><a href="#尽量少使用-using-指示-污染命名空间" class="headerlink" title="尽量少使用 using 指示 污染命名空间"></a>尽量少使用 <code>using 指示</code> 污染命名空间</h4><blockquote><p>一般说来,使用 using 命令比使用 using 编译命令更安全,这是由于它<strong>只导入了指定的名称</strong>。如果该名称与局部名称发生冲突,编译器将<strong>发出指示</strong>。using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则<strong>局部名称将覆盖名称空间版本</strong>,而编译器<strong>并不会发出警告</strong>。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。</p></blockquote><p>using 使用</p><p>尽量少使用 <code>using 指示</code></p><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;</code></pre><p>应该多使用 <code>using 声明</code></p><pre><code class="hljs cpp"><span class="hljs-keyword">int</span> x;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">cin</span> >> x ;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << x << <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;</code></pre><p>或者</p><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cin</span>;<span class="hljs-keyword">using</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span>;<span class="hljs-keyword">using</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;<span class="hljs-keyword">int</span> x;<span class="hljs-built_in">cin</span> >> x;<span class="hljs-built_in">cout</span> << x << <span class="hljs-built_in">endl</span>;</code></pre><h3 id="范围解析运算符"><a href="#范围解析运算符" class="headerlink" title=":: 范围解析运算符"></a>:: 范围解析运算符</h3><h4 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h4><ol><li>全局作用域符(<code>::name</code>):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间</li><li>类作用域符(<code>class::name</code>):用于表示指定类型的作用域范围是具体某个类的</li><li>命名空间作用域符(<code>namespace::name</code>):用于表示指定类型的作用域范围是具体某个命名空间的</li></ol><p>:: 使用</p><pre><code class="hljs cpp"><span class="hljs-keyword">int</span> count = <span class="hljs-number">11</span>; <span class="hljs-comment">// 全局(::)的 count</span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span> {</span><span class="hljs-keyword">public</span>:<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> count; <span class="hljs-comment">// 类 A 的 count(A::count)</span>};<span class="hljs-keyword">int</span> A::count = <span class="hljs-number">21</span>;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">fun</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{<span class="hljs-keyword">int</span> count = <span class="hljs-number">31</span>; <span class="hljs-comment">// 初始化局部的 count 为 31</span>count = <span class="hljs-number">32</span>; <span class="hljs-comment">// 设置局部的 count 的值为 32</span>}<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{::count = <span class="hljs-number">12</span>; <span class="hljs-comment">// 测试 1:设置全局的 count 的值为 12</span>A::count = <span class="hljs-number">22</span>; <span class="hljs-comment">// 测试 2:设置类 A 的 count 为 22</span>fun(); <span class="hljs-comment">// 测试 3</span><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="enum-枚举类型"><a href="#enum-枚举类型" class="headerlink" title="enum 枚举类型"></a>enum 枚举类型</h3><h4 id="限定作用域的枚举类型"><a href="#限定作用域的枚举类型" class="headerlink" title="限定作用域的枚举类型"></a>限定作用域的枚举类型</h4><pre><code class="hljs cpp"><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">open_modes</span> {</span> input, output, append };</code></pre><h4 id="不限定作用域的枚举类型"><a href="#不限定作用域的枚举类型" class="headerlink" title="不限定作用域的枚举类型"></a>不限定作用域的枚举类型</h4><pre><code class="hljs cpp"><span class="hljs-keyword">enum</span> color { red, yellow, green };<span class="hljs-keyword">enum</span> { floatPrec = <span class="hljs-number">6</span>, doublePrec = <span class="hljs-number">10</span> };</code></pre><h3 id="decltype"><a href="#decltype" class="headerlink" title="decltype"></a>decltype</h3><p>decltype 关键字用于检查实体的声明类型或表达式的类型及值分类。语法:</p><pre><code class="hljs cpp"><span class="hljs-keyword">decltype</span> ( expression )</code></pre><p>decltype 使用</p><pre><code class="hljs cpp"><span class="hljs-comment">// 尾置返回允许我们在参数列表之后声明返回类型</span><span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> It>auto fcn(It beg, It end) -> decltype(*beg){ <span class="hljs-comment">// 处理序列</span> <span class="hljs-keyword">return</span> *beg; <span class="hljs-comment">// 返回序列中一个元素的引用</span>}<span class="hljs-comment">// 为了使用模板参数成员,必须用 typename</span><span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> It>auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type{ <span class="hljs-comment">// 处理序列</span> <span class="hljs-keyword">return</span> *beg; <span class="hljs-comment">// 返回序列中一个元素的拷贝</span>}</code></pre><h3 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h3><h4 id="左值引用"><a href="#左值引用" class="headerlink" title="左值引用"></a>左值引用</h4><p>常规引用,一般表示对象的身份。</p><h4 id="右值引用"><a href="#右值引用" class="headerlink" title="右值引用"></a>右值引用</h4><p>右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。</p><p>右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:</p><ul><li>消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。</li><li>能够更简洁明确地定义泛型函数。</li></ul><h4 id="引用折叠"><a href="#引用折叠" class="headerlink" title="引用折叠"></a>引用折叠</h4><ul><li><code>X& &</code>、<code>X& &&</code>、<code>X&& &</code> 可折叠成 <code>X&</code></li><li><code>X&& &&</code> 可折叠成 <code>X&&</code></li></ul><h3 id="宏"><a href="#宏" class="headerlink" title="宏"></a>宏</h3><ul><li>宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对 “参数” 进行的是一对一的替换。</li></ul><h3 id="成员初始化列表"><a href="#成员初始化列表" class="headerlink" title="成员初始化列表"></a>成员初始化列表</h3><p>好处</p><ul><li>更高效:少了一次调用默认构造函数的过程。</li><li>有些场合必须要用初始化列表:<ol><li>常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面</li><li>引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面</li><li>没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化</li></ol></li></ul><h3 id="initializer-list-列表初始化"><a href="#initializer-list-列表初始化" class="headerlink" title="initializer_list 列表初始化"></a>initializer_list 列表初始化</h3><p>用花括号初始化器列表初始化一个对象,其中对应构造函数接受一个 <code>std::initializer_list</code> 参数.</p><p>initializer_list 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><vector></span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><initializer_list></span></span> <span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>></span><span class="hljs-class"><span class="hljs-title">struct</span> <span class="hljs-title">S</span> {</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><T> v; S(<span class="hljs-built_in">std</span>::<span class="hljs-built_in">initializer_list</span><T> l) : v(l) { <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">"constructed with a "</span> << l.size() << <span class="hljs-string">"-element list\n"</span>; } <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">append</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::<span class="hljs-built_in">initializer_list</span><T> l)</span> </span>{ v.insert(v.end(), l.begin(), l.end()); } std::pair<const T*, std::size_t> c_arr() const { <span class="hljs-keyword">return</span> {&v[<span class="hljs-number">0</span>], v.size()}; <span class="hljs-comment">// 在 return 语句中复制列表初始化</span> <span class="hljs-comment">// 这不使用 std::initializer_list</span> }}; <span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> T><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">templated_fn</span><span class="hljs-params">(T)</span> </span>{} <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ S<<span class="hljs-keyword">int</span>> s = {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}; <span class="hljs-comment">// 复制初始化</span> s.append({<span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>}); <span class="hljs-comment">// 函数调用中的列表初始化</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">"The vector size is now "</span> << s.c_arr().second << <span class="hljs-string">" ints:\n"</span>; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> n : s.v) <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << n << <span class="hljs-string">' '</span>; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">'\n'</span>; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">"Range-for over brace-init-list: \n"</span>; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> x : {<span class="hljs-number">-1</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-3</span>}) <span class="hljs-comment">// auto 的规则令此带范围 for 工作</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << x << <span class="hljs-string">' '</span>; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">'\n'</span>; <span class="hljs-keyword">auto</span> al = {<span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>}; <span class="hljs-comment">// auto 的特殊规则</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">"The list bound to auto has size() = "</span> << al.size() << <span class="hljs-string">'\n'</span>; <span class="hljs-comment">// templated_fn({1, 2, 3}); // 编译错误!“ {1, 2, 3} ”不是表达式,</span> <span class="hljs-comment">// 它无类型,故 T 无法推导</span> templated_fn<<span class="hljs-built_in">std</span>::<span class="hljs-built_in">initializer_list</span><<span class="hljs-keyword">int</span>>>({<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}); <span class="hljs-comment">// OK</span> templated_fn<<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>>>({<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}); <span class="hljs-comment">// 也 OK</span>}</code></pre><h3 id="面向对象"><a href="#面向对象" class="headerlink" title="面向对象"></a>面向对象</h3><p>面向对象程序设计(Object-oriented programming,OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%9F%BA%E6%9C%AC%E7%89%B9%E5%BE%81.png" srcset="/img/loading.gif" alt="面向对象特征"></p><p>面向对象三大特征 —— 封装、继承、多态</p><h3 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h3><p>把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。关键字:public, protected, private。不写默认为 private。</p><ul><li><code>public</code> 成员:可以被任意实体访问</li><li><code>protected</code> 成员:只允许被子类及本类的成员函数访问</li><li><code>private</code> 成员:只允许被本类的成员函数、友元类或友元函数访问</li></ul><h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><ul><li>基类(父类)——> 派生类(子类)</li></ul><h3 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h3><ul><li>多态,即多种状态(形态)。简单来说,我们可以将多态定义为消息以多种形式显示的能力。</li><li>多态是以封装和继承为基础的。</li><li>C++ 多态分类及实现:<ol><li>重载多态(Ad-hoc Polymorphism,编译期):函数重载、运算符重载</li><li>子类型多态(Subtype Polymorphism,运行期):虚函数</li><li>参数多态性(Parametric Polymorphism,编译期):类模板、函数模板</li><li>强制多态(Coercion Polymorphism,编译期/运行期):基本类型转换、自定义类型转换</li></ol></li></ul><blockquote><p><a href="https://catonmat.net/cpp-polymorphism" target="_blank" rel="noopener">The Four Polymorphisms in C++</a></p></blockquote><h4 id="静态多态(编译期-早绑定)"><a href="#静态多态(编译期-早绑定)" class="headerlink" title="静态多态(编译期/早绑定)"></a>静态多态(编译期/早绑定)</h4><p>函数重载</p><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span></span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">do</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a)</span></span>; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">do</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span></span>;};</code></pre><h4 id="动态多态(运行期期-晚绑定)"><a href="#动态多态(运行期期-晚绑定)" class="headerlink" title="动态多态(运行期期/晚绑定)"></a>动态多态(运行期期/晚绑定)</h4><ul><li>虚函数:用 virtual 修饰成员函数,使其成为虚函数</li></ul><p><strong>注意:</strong></p><ul><li>普通函数(非类成员函数)不能是虚函数</li><li>静态函数(static)不能是虚函数</li><li>构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间中,必须要构造函数调用完成后才会形成虚表指针)</li><li>内联函数不能是表现多态性时的虚函数,解释见:<a href="https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97" target="_blank" rel="noopener">虚函数(virtual)可以是内联函数(inline)吗?</a></li></ul><p>动态多态使用</p><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span> // 形状类</span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calcArea</span><span class="hljs-params">()</span></span><span class="hljs-function"> </span>{ ... } <span class="hljs-keyword">virtual</span> ~Shape();};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> :</span> <span class="hljs-keyword">public</span> Shape <span class="hljs-comment">// 圆形类</span>{<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calcArea</span><span class="hljs-params">()</span></span>; ...};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rect</span> :</span> <span class="hljs-keyword">public</span> Shape <span class="hljs-comment">// 矩形类</span>{<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calcArea</span><span class="hljs-params">()</span></span>; ...};<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ Shape * shape1 = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">4.0</span>); Shape * shape2 = <span class="hljs-keyword">new</span> Rect(<span class="hljs-number">5.0</span>, <span class="hljs-number">6.0</span>); shape1->calcArea(); <span class="hljs-comment">// 调用圆形类里面的方法</span> shape2->calcArea(); <span class="hljs-comment">// 调用矩形类里面的方法</span> <span class="hljs-keyword">delete</span> shape1; shape1 = <span class="hljs-literal">nullptr</span>; <span class="hljs-keyword">delete</span> shape2; shape2 = <span class="hljs-literal">nullptr</span>; <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="虚析构函数"><a href="#虚析构函数" class="headerlink" title="虚析构函数"></a>虚析构函数</h3><p>虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。</p><p>虚析构函数使用</p><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span></span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>: Shape(); <span class="hljs-comment">// 构造函数不能是虚函数</span> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calcArea</span><span class="hljs-params">()</span></span>; <span class="hljs-keyword">virtual</span> ~Shape(); <span class="hljs-comment">// 虚析构函数</span>};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> :</span> <span class="hljs-keyword">public</span> Shape <span class="hljs-comment">// 圆形类</span>{<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calcArea</span><span class="hljs-params">()</span></span>; ...};<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ Shape * shape1 = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">4.0</span>); shape1->calcArea(); <span class="hljs-keyword">delete</span> shape1; <span class="hljs-comment">// 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。</span> shape1 = <span class="hljs-literal">NULL</span>; <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="纯虚函数"><a href="#纯虚函数" class="headerlink" title="纯虚函数"></a>纯虚函数</h3><p>纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。</p><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">int</span> <span class="hljs-title">A</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;</code></pre><h3 id="虚函数、纯虚函数"><a href="#虚函数、纯虚函数" class="headerlink" title="虚函数、纯虚函数"></a>虚函数、纯虚函数</h3><ul><li>类里如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。 </li><li>虚函数在子类里面可以不重写;但纯虚函数必须在子类实现才可以实例化子类。</li><li>虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。 </li><li>带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。</li><li>虚基类是虚继承中的基类,具体见下文虚继承。</li></ul><blockquote><p><a href="https://blog.csdn.net/u012260238/article/details/53610462" target="_blank" rel="noopener">CSDN . C++ 中的虚函数、纯虚函数区别和联系</a></p></blockquote><h3 id="虚函数指针、虚函数表"><a href="#虚函数指针、虚函数表" class="headerlink" title="虚函数指针、虚函数表"></a>虚函数指针、虚函数表</h3><ul><li>虚函数指针:在含有虚函数类的对象中,指向虚函数表,在运行时确定。</li><li>虚函数表:在程序只读数据段(<code>.rodata section</code>,见:<a href="#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">目标文件存储结构</a>),存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建。</li></ul><blockquote><p><a href="https://blog.twofei.com/496/" target="_blank" rel="noopener">C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现</a></p></blockquote><h3 id="虚继承"><a href="#虚继承" class="headerlink" title="虚继承"></a>虚继承</h3><p>虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。</p><p>底层实现原理与编译器相关,一般通过<strong>虚基类指针</strong>和<strong>虚基类表</strong>实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。</p><p>实际上,vbptr 指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。</p><h3 id="虚继承、虚函数"><a href="#虚继承、虚函数" class="headerlink" title="虚继承、虚函数"></a>虚继承、虚函数</h3><ul><li>相同之处:都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)</li><li>不同之处:<ul><li>虚继承<ul><li>虚基类依旧存在继承类中,只占用存储空间</li><li>虚基类表存储的是虚基类相对直接继承类的偏移</li></ul></li><li>虚函数<ul><li>虚函数不占用存储空间</li><li>虚函数表存储的是虚函数地址</li></ul></li></ul></li></ul><h3 id="模板类、成员模板、虚函数"><a href="#模板类、成员模板、虚函数" class="headerlink" title="模板类、成员模板、虚函数"></a>模板类、成员模板、虚函数</h3><ul><li>模板类中可以使用虚函数</li><li>一个类(无论是普通类还是类模板)的成员模板(本身是模板的成员函数)不能是虚函数</li></ul><h3 id="抽象类、接口类、聚合类"><a href="#抽象类、接口类、聚合类" class="headerlink" title="抽象类、接口类、聚合类"></a>抽象类、接口类、聚合类</h3><ul><li>抽象类:含有纯虚函数的类</li><li>接口类:仅含有纯虚函数的抽象类</li><li>聚合类:用户可以直接访问其成员,并且具有特殊的初始化语法形式。满足如下特点:<ul><li>所有成员都是 public</li><li>没有定义任何构造函数</li><li>没有类内初始化</li><li>没有基类,也没有 virtual 函数</li></ul></li></ul><h3 id="内存分配和管理"><a href="#内存分配和管理" class="headerlink" title="内存分配和管理"></a>内存分配和管理</h3><h4 id="malloc、calloc、realloc、alloca"><a href="#malloc、calloc、realloc、alloca" class="headerlink" title="malloc、calloc、realloc、alloca"></a>malloc、calloc、realloc、alloca</h4><ol><li>malloc:申请指定字节数的内存。申请到的内存中的初始值不确定。</li><li>calloc:为指定长度的对象,分配能容纳其指定个数的内存。申请到的内存的每一位(bit)都初始化为 0。</li><li>realloc:更改以前分配的内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定。</li><li>alloca:在栈上申请内存。程序在出栈的时候,会自动释放内存。但是需要注意的是,alloca 不具可移植性, 而且在没有传统堆栈的机器上很难实现。alloca 不宜使用在必须广泛移植的程序中。C99 中支持变长数组 (VLA),可以用来替代 alloca。</li></ol><h4 id="malloc、free"><a href="#malloc、free" class="headerlink" title="malloc、free"></a>malloc、free</h4><p>用于分配、释放内存</p><p>malloc、free 使用</p><p>申请内存,确认是否申请成功</p><pre><code class="hljs cpp"><span class="hljs-keyword">char</span> *str = (<span class="hljs-keyword">char</span>*) <span class="hljs-built_in">malloc</span>(<span class="hljs-number">100</span>);assert(str != <span class="hljs-literal">nullptr</span>);</code></pre><p>释放内存后指针置空</p><pre><code class="hljs cpp"><span class="hljs-built_in">free</span>(p); p = <span class="hljs-literal">nullptr</span>;</code></pre><h4 id="new、delete"><a href="#new、delete" class="headerlink" title="new、delete"></a>new、delete</h4><ol><li>new / new[]:完成两件事,先底层调用 malloc 分配了内存,然后调用构造函数(创建对象)。</li><li>delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用 free 释放空间。</li><li>new 在申请内存时会自动计算所需字节数,而 malloc 则需我们自己输入申请内存空间的字节数。</li></ol><p>new、delete 使用</p><p>申请内存,确认是否申请成功</p><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{ T* t = <span class="hljs-keyword">new</span> T(); <span class="hljs-comment">// 先内存分配 ,再构造函数</span> <span class="hljs-keyword">delete</span> t; <span class="hljs-comment">// 先析构函数,再内存释放</span> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h4 id="定位-new"><a href="#定位-new" class="headerlink" title="定位 new"></a>定位 new</h4><p>定位 new(placement new)允许我们向 new 传递额外的地址参数,从而在预先指定的内存区域创建对象。</p><pre><code class="hljs cpp"><span class="hljs-keyword">new</span> (place_address) type<span class="hljs-keyword">new</span> (place_address) type (initializers)<span class="hljs-keyword">new</span> (place_address) type [size]<span class="hljs-keyword">new</span> (place_address) type [size] { braced initializer <span class="hljs-built_in">list</span> }</code></pre><ul><li><code>place_address</code> 是个指针</li><li><code>initializers</code> 提供一个(可能为空的)以逗号分隔的初始值列表</li></ul><h3 id="delete-this-合法吗?"><a href="#delete-this-合法吗?" class="headerlink" title="delete this 合法吗?"></a>delete this 合法吗?</h3><blockquote><p><a href="https://isocpp.org/wiki/faq/freestore-mgmt#delete-this" target="_blank" rel="noopener">Is it legal (and moral) for a member function to say delete this?</a></p></blockquote><p>合法,但:</p><ol><li>必须保证 this 对象是通过 <code>new</code>(不是 <code>new[]</code>、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的</li><li>必须保证调用 <code>delete this</code> 的成员函数是最后一个调用 this 的成员函数</li><li>必须保证成员函数的 <code>delete this</code> 后面没有调用 this 了</li><li>必须保证 <code>delete this</code> 后没有人使用了</li></ol><h3 id="如何定义一个只能在堆上(栈上)生成对象的类?"><a href="#如何定义一个只能在堆上(栈上)生成对象的类?" class="headerlink" title="如何定义一个只能在堆上(栈上)生成对象的类?"></a>如何定义一个只能在堆上(栈上)生成对象的类?</h3><blockquote><p><a href="https://www.nowcoder.com/questionTerminal/0a584aa13f804f3ea72b442a065a7618" target="_blank" rel="noopener">如何定义一个只能在堆上(栈上)生成对象的类?</a></p></blockquote><h4 id="只能在堆上"><a href="#只能在堆上" class="headerlink" title="只能在堆上"></a>只能在堆上</h4><p>方法:将析构函数设置为私有</p><p>原因:C++ 是静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。</p><h4 id="只能在栈上"><a href="#只能在栈上" class="headerlink" title="只能在栈上"></a>只能在栈上</h4><p>方法:将 new 和 delete 重载为私有</p><p>原因:在堆上生成对象,使用 new 关键词操作,其过程分为两阶段:第一阶段,使用 new 在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。将 new 操作设置为私有,那么第一阶段就无法完成,就不能够在堆上生成对象。</p><h3 id="智能指针"><a href="#智能指针" class="headerlink" title="智能指针"></a>智能指针</h3><h4 id="C-标准库(STL)中"><a href="#C-标准库(STL)中" class="headerlink" title="C++ 标准库(STL)中"></a>C++ 标准库(STL)中</h4><p>头文件:<code>#include <memory></code></p><h4 id="C-98"><a href="#C-98" class="headerlink" title="C++ 98"></a>C++ 98</h4><pre><code class="hljs cpp">std::auto_ptr<std::string> ps (new std::string(str));</code></pre><h4 id="C-11"><a href="#C-11" class="headerlink" title="C++ 11"></a>C++ 11</h4><ol><li>shared_ptr</li><li>unique_ptr</li><li>weak_ptr</li><li>auto_ptr(被 C++11 弃用)</li></ol><ul><li>Class shared_ptr 实现共享式拥有(shared ownership)概念。多个智能指针指向相同对象,该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作,标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。</li><li>Class unique_ptr 实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏(resource leak)——如 new 后忘记 delete ——特别有用。</li></ul><h5 id="shared-ptr"><a href="#shared-ptr" class="headerlink" title="shared_ptr"></a>shared_ptr</h5><p>多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。</p><ul><li>支持定制型删除器(custom deleter),可防范 Cross-DLL 问题(对象在动态链接库(DLL)中被 new 创建,却在另一个 DLL 内被 delete 销毁)、自动解除互斥锁</li></ul><h5 id="weak-ptr"><a href="#weak-ptr" class="headerlink" title="weak_ptr"></a>weak_ptr</h5><p>weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。</p><ul><li>可打破环状引用(cycles of references,两个其实已经没有被使用的对象彼此互指,使之看似还在 “被使用” 的状态)的问题</li></ul><h5 id="unique-ptr"><a href="#unique-ptr" class="headerlink" title="unique_ptr"></a>unique_ptr</h5><p>unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有着被销毁或编程 empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。</p><ul><li>unique_ptr 用于取代 auto_ptr</li></ul><h5 id="auto-ptr"><a href="#auto-ptr" class="headerlink" title="auto_ptr"></a>auto_ptr</h5><p>被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 <code>std::move</code> 语义,以及其他瑕疵。</p><h5 id="auto-ptr-与-unique-ptr-比较"><a href="#auto-ptr-与-unique-ptr-比较" class="headerlink" title="auto_ptr 与 unique_ptr 比较"></a>auto_ptr 与 unique_ptr 比较</h5><ul><li>auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了<code>move</code> 语义;</li><li>auto_ptr 对象不能管理数组(析构调用 <code>delete</code>),unique_ptr 可以管理数组(析构调用 <code>delete[]</code> );</li></ul><h3 id="强制类型转换运算符"><a href="#强制类型转换运算符" class="headerlink" title="强制类型转换运算符"></a>强制类型转换运算符</h3><blockquote><p><a href="https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx" target="_blank" rel="noopener">MSDN . 强制转换运算符</a></p></blockquote><h4 id="static-cast"><a href="#static-cast" class="headerlink" title="static_cast"></a>static_cast</h4><ul><li>用于非多态类型的转换</li><li>不执行运行时类型检查(转换安全性不如 dynamic_cast)</li><li>通常用于转换数值数据类型(如 float -> int)</li><li>可以在整个类层次结构中移动指针,子类转化为父类安全(向上转换),父类转化为子类不安全(因为子类可能有不在父类的字段或方法)</li></ul><blockquote><p>向上转换是一种隐式转换。</p></blockquote><h4 id="dynamic-cast"><a href="#dynamic-cast" class="headerlink" title="dynamic_cast"></a>dynamic_cast</h4><ul><li>用于多态类型的转换</li><li>执行行运行时类型检查</li><li>只适用于指针或引用</li><li>对不明确的指针的转换将失败(返回 nullptr),但不引发异常</li><li>可以在整个类层次结构中移动指针,包括向上转换、向下转换</li></ul><h4 id="const-cast"><a href="#const-cast" class="headerlink" title="const_cast"></a>const_cast</h4><ul><li>用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 )</li></ul><h4 id="reinterpret-cast"><a href="#reinterpret-cast" class="headerlink" title="reinterpret_cast"></a>reinterpret_cast</h4><ul><li>用于位的简单重新解释</li><li>滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。</li><li>允许将任何指针转换为任何其他指针类型(如 <code>char*</code> 到 <code>int*</code> 或 <code>One_class*</code> 到 <code>Unrelated_class*</code> 之类的转换,但其本身并不安全)</li><li>也允许将任何整数类型转换为任何指针类型以及反向转换。</li><li>reinterpret_cast 运算符不能丢掉 const、volatile 或 __unaligned 特性。 </li><li>reinterpret_cast 的一个实际用途是在哈希函数中,即,通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引。</li></ul><h4 id="bad-cast"><a href="#bad-cast" class="headerlink" title="bad_cast"></a>bad_cast</h4><ul><li>由于强制转换为引用类型失败,dynamic_cast 运算符引发 bad_cast 异常。</li></ul><p>bad_cast 使用</p><pre><code class="hljs cpp"><span class="hljs-keyword">try</span> { Circle& ref_circle = <span class="hljs-keyword">dynamic_cast</span><Circle&>(ref_shape); } <span class="hljs-keyword">catch</span> (bad_cast b) { <span class="hljs-built_in">cout</span> << <span class="hljs-string">"Caught: "</span> << b.what(); }</code></pre><h3 id="运行时类型信息-RTTI"><a href="#运行时类型信息-RTTI" class="headerlink" title="运行时类型信息 (RTTI)"></a>运行时类型信息 (RTTI)</h3><h4 id="dynamic-cast-1"><a href="#dynamic-cast-1" class="headerlink" title="dynamic_cast"></a>dynamic_cast</h4><ul><li>用于多态类型的转换</li></ul><h4 id="typeid"><a href="#typeid" class="headerlink" title="typeid"></a>typeid</h4><ul><li>typeid 运算符允许在运行时确定对象的类型</li><li>type_id 返回一个 type_info 对象的引用</li><li>如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数</li><li>只能获取对象的实际类型</li></ul><h4 id="type-info"><a href="#type-info" class="headerlink" title="type_info"></a>type_info</h4><ul><li>type_info 类描述编译器在程序中生成的类型信息。 此类的对象可以有效存储指向类型的名称的指针。 type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。 类型的编码规则和排列顺序是未指定的,并且可能因程序而异。</li><li>头文件:<code>typeinfo</code></li></ul><p>typeid、type_info 使用</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Flyable</span> // 能飞的</span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">takeoff</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>; <span class="hljs-comment">// 起飞</span> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">land</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>; <span class="hljs-comment">// 降落</span>};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bird</span> :</span> <span class="hljs-keyword">public</span> Flyable <span class="hljs-comment">// 鸟</span>{<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">foraging</span><span class="hljs-params">()</span> </span>{...} <span class="hljs-comment">// 觅食</span> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">takeoff</span><span class="hljs-params">()</span> </span>{...} <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">land</span><span class="hljs-params">()</span> </span>{...} <span class="hljs-keyword">virtual</span> ~Bird(){}};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Plane</span> :</span> <span class="hljs-keyword">public</span> Flyable <span class="hljs-comment">// 飞机</span>{<span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">carry</span><span class="hljs-params">()</span> </span>{...} <span class="hljs-comment">// 运输</span> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">takeoff</span><span class="hljs-params">()</span> </span>{...} <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">land</span><span class="hljs-params">()</span> </span>{...}};<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">type_info</span></span><span class="hljs-class">{</span><span class="hljs-keyword">public</span>: <span class="hljs-function"><span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* <span class="hljs-title">name</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>; <span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span> == (<span class="hljs-keyword">const</span> type_info & rhs) <span class="hljs-keyword">const</span>; <span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span> != (<span class="hljs-keyword">const</span> type_info & rhs) <span class="hljs-keyword">const</span>; <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">before</span><span class="hljs-params">(<span class="hljs-keyword">const</span> type_info & rhs)</span> <span class="hljs-keyword">const</span></span>; <span class="hljs-keyword">virtual</span> ~type_info();<span class="hljs-keyword">private</span>: ...};<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">doSomething</span><span class="hljs-params">(Flyable *obj)</span> <span class="hljs-comment">// 做些事情</span></span><span class="hljs-function"></span>{ obj->takeoff(); <span class="hljs-built_in">cout</span> << <span class="hljs-keyword">typeid</span>(*obj).name() << <span class="hljs-built_in">endl</span>; <span class="hljs-comment">// 输出传入对象类型("class Bird" or "class Plane")</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeid</span>(*obj) == <span class="hljs-keyword">typeid</span>(Bird)) <span class="hljs-comment">// 判断对象类型</span> { Bird *bird = <span class="hljs-keyword">dynamic_cast</span><Bird *>(obj); <span class="hljs-comment">// 对象转化</span> bird->foraging(); } obj->land();}<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{Bird *b = <span class="hljs-keyword">new</span> Bird();doSomething(b);<span class="hljs-keyword">delete</span> b;b = <span class="hljs-literal">nullptr</span>;<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><p><a id="effective"></a></p><h2 id="⭐️-Effective"><a href="#⭐️-Effective" class="headerlink" title="⭐️ Effective"></a>⭐️ Effective</h2><h3 id="Effective-C"><a href="#Effective-C" class="headerlink" title="Effective C++"></a>Effective C++</h3><ol><li>视 C++ 为一个语言联邦(C、Object-Oriented C++、Template C++、STL)</li><li>宁可以编译器替换预处理器(尽量以 <code>const</code>、<code>enum</code>、<code>inline</code> 替换 <code>#define</code>)</li><li>尽可能使用 const</li><li>确定对象被使用前已先被初始化(构造时赋值(copy 构造函数)比 default 构造后赋值(copy assignment)效率高)</li><li>了解 C++ 默默编写并调用哪些函数(编译器暗自为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符、析构函数)</li><li>若不想使用编译器自动生成的函数,就应该明确拒绝(将不想使用的成员函数声明为 private,并且不予实现)</li><li>为多态基类声明 virtual 析构函数(如果 class 带有任何 virtual 函数,它就应该拥有一个 virtual 析构函数)</li><li>别让异常逃离析构函数(析构函数应该吞下不传播异常,或者结束程序,而不是吐出异常;如果要处理异常应该在非析构的普通函数处理)</li><li>绝不在构造和析构过程中调用 virtual 函数(因为这类调用从不下降至 derived class)</li><li>令 <code>operator=</code> 返回一个 <code>reference to *this</code> (用于连锁赋值)</li><li>在 <code>operator=</code> 中处理 “自我赋值”</li><li>赋值对象时应确保复制 “对象内的所有成员变量” 及 “所有 base class 成分”(调用基类复制构造函数)</li><li>以对象管理资源(资源在构造函数获得,在析构函数释放,建议使用智能指针,资源取得时机便是初始化时机(Resource Acquisition Is Initialization,RAII))</li><li>在资源管理类中小心 copying 行为(普遍的 RAII class copying 行为是:抑制 copying、引用计数、深度拷贝、转移底部资源拥有权(类似 auto_ptr))</li><li>在资源管理类中提供对原始资源(raw resources)的访问(对原始资源的访问可能经过显式转换或隐式转换,一般而言显示转换比较安全,隐式转换对客户比较方便)</li><li>成对使用 new 和 delete 时要采取相同形式(<code>new</code> 中使用 <code>[]</code> 则 <code>delete []</code>,<code>new</code> 中不使用 <code>[]</code> 则 <code>delete</code>)</li><li>以独立语句将 newed 对象存储于(置入)智能指针(如果不这样做,可能会因为编译器优化,导致难以察觉的资源泄漏)</li><li>让接口容易被正确使用,不易被误用(促进正常使用的办法:接口的一致性、内置类型的行为兼容;阻止误用的办法:建立新类型,限制类型上的操作,约束对象值、消除客户的资源管理责任)</li><li>设计 class 犹如设计 type,需要考虑对象创建、销毁、初始化、赋值、值传递、合法值、继承关系、转换、一般化等等。</li><li>宁以 pass-by-reference-to-const 替换 pass-by-value (前者通常更高效、避免切割问题(slicing problem),但不适用于内置类型、STL迭代器、函数对象)</li><li>必须返回对象时,别妄想返回其 reference(绝不返回 pointer 或 reference 指向一个 local stack 对象,或返回 reference 指向一个 heap-allocated 对象,或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象。)</li><li>将成员变量声明为 private(为了封装、一致性、对其读写精确控制等)</li><li>宁以 non-member、non-friend 替换 member 函数(可增加封装性、包裹弹性(packaging flexibility)、机能扩充性)</li><li>若所有参数(包括被this指针所指的那个隐喻参数)皆须要类型转换,请为此采用 non-member 函数</li><li>考虑写一个不抛异常的 swap 函数</li><li>尽可能延后变量定义式的出现时间(可增加程序清晰度并改善程序效率)</li><li>尽量少做转型动作(旧式:<code>(T)expression</code>、<code>T(expression)</code>;新式:<code>const_cast<T>(expression)</code>、<code>dynamic_cast<T>(expression)</code>、<code>reinterpret_cast<T>(expression)</code>、<code>static_cast<T>(expression)</code>、;尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型)</li><li>避免使用 handles(包括 引用、指针、迭代器)指向对象内部(以增加封装性、使 const 成员函数的行为更像 const、降低 “虚吊号码牌”(dangling handles,如悬空指针等)的可能性)</li><li>为 “异常安全” 而努力是值得的(异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏,分为三种可能的保证:基本型、强列型、不抛异常型)</li><li>透彻了解 inlining 的里里外外(inlining 在大多数 C++ 程序中是编译期的行为;inline 函数是否真正 inline,取决于编译器;大部分编译器拒绝太过复杂(如带有循环或递归)的函数 inlining,而所有对 virtual 函数的调用(除非是最平淡无奇的)也都会使 inlining 落空;inline 造成的代码膨胀可能带来效率损失;inline 函数无法随着程序库的升级而升级)</li><li>将文件间的编译依存关系降至最低(如果使用 object references 或 object pointers 可以完成任务,就不要使用 objects;如果能够,尽量以 class 声明式替换 class 定义式;为声明式和定义式提供不同的头文件)</li><li>确定你的 public 继承塑模出 is-a(是一种)关系(适用于 base classes 身上的每一件事情一定适用于 derived classes 身上,因为每一个 derived class 对象也都是一个 base class 对象)</li><li>避免遮掩继承而来的名字(可使用 using 声明式或转交函数(forwarding functions)来让被遮掩的名字再见天日)</li><li>区分接口继承和实现继承(在 public 继承之下,derived classes 总是继承 base class 的接口;pure virtual 函数只具体指定接口继承;非纯 impure virtual 函数具体指定接口继承及缺省实现继承;non-virtual 函数具体指定接口继承以及强制性实现继承)</li><li>考虑 virtual 函数以外的其他选择(如 Template Method 设计模式的 non-virtual interface(NVI)手法,将 virtual 函数替换为 “函数指针成员变量”,以 <code>tr1::function</code> 成员变量替换 virtual 函数,将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数)</li><li>绝不重新定义继承而来的 non-virtual 函数</li><li>绝不重新定义继承而来的缺省参数值,因为缺省参数值是静态绑定(statically bound),而 virtual 函数却是动态绑定(dynamically bound)</li><li>通过复合塑模 has-a(有一个)或 “根据某物实现出”(在应用域(application domain),复合意味 has-a(有一个);在实现域(implementation domain),复合意味着 is-implemented-in-terms-of(根据某物实现出))</li><li>明智而审慎地使用 private 继承(private 继承意味着 is-implemented-in-terms-of(根据某物实现出),尽可能使用复合,当 derived class 需要访问 protected base class 的成员,或需要重新定义继承而来的时候 virtual 函数,或需要 empty base 最优化时,才使用 private 继承)</li><li>明智而审慎地使用多重继承(多继承比单一继承复杂,可能导致新的歧义性,以及对 virtual 继承的需要,但确有正当用途,如 “public 继承某个 interface class” 和 “private 继承某个协助实现的 class”;virtual 继承可解决多继承下菱形继承的二义性问题,但会增加大小、速度、初始化及赋值的复杂度等等成本)</li><li>了解隐式接口和编译期多态(class 和 templates 都支持接口(interfaces)和多态(polymorphism);class 的接口是以签名为中心的显式的(explicit),多态则是通过 virtual 函数发生于运行期;template 的接口是奠基于有效表达式的隐式的(implicit),多态则是通过 template 具现化和函数重载解析(function overloading resolution)发生于编译期)</li><li>了解 typename 的双重意义(声明 template 类型参数是,前缀关键字 class 和 typename 的意义完全相同;请使用关键字 typename 标识嵌套从属类型名称,但不得在基类列(base class lists)或成员初值列(member initialization list)内以它作为 base class 修饰符)</li><li>学习处理模板化基类内的名称(可在 derived class templates 内通过 <code>this-></code> 指涉 base class templates 内的成员名称,或藉由一个明白写出的 “base class 资格修饰符” 完成)</li><li>将与参数无关的代码抽离 templates(因类型模板参数(non-type template parameters)而造成代码膨胀往往可以通过函数参数或 class 成员变量替换 template 参数来消除;因类型参数(type parameters)而造成的代码膨胀往往可以通过让带有完全相同二进制表述(binary representations)的实现类型(instantiation types)共享实现码)</li><li>运用成员函数模板接受所有兼容类型(请使用成员函数模板(member function templates)生成 “可接受所有兼容类型” 的函数;声明 member templates 用于 “泛化 copy 构造” 或 “泛化 assignment 操作” 时还需要声明正常的 copy 构造函数和 copy assignment 操作符)</li><li>需要类型转换时请为模板定义非成员函数(当我们编写一个 class template,而它所提供之 “与此 template 相关的” 函数支持 “所有参数之隐式类型转换” 时,请将那些函数定义为 “class template 内部的 friend 函数”)</li><li>请使用 traits classes 表现类型信息(traits classes 通过 templates 和 “templates 特化” 使得 “类型相关信息” 在编译期可用,通过重载技术(overloading)实现在编译期对类型执行 if…else 测试)</li><li>认识 template 元编程(模板元编程(TMP,template metaprogramming)可将工作由运行期移往编译期,因此得以实现早期错误侦测和更高的执行效率;TMP 可被用来生成 “给予政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码)</li><li>了解 new-handler 的行为(set_new_handler 允许客户指定一个在内存分配无法获得满足时被调用的函数;nothrow new 是一个颇具局限的工具,因为它只适用于内存分配(operator new),后继的构造函数调用还是可能抛出异常)</li><li>了解 new 和 delete 的合理替换时机(为了检测运用错误、收集动态分配内存之使用统计信息、增加分配和归还速度、降低缺省内存管理器带来的空间额外开销、弥补缺省分配器中的非最佳齐位、将相关对象成簇集中、获得非传统的行为)</li><li>编写 new 和 delete 时需固守常规(operator new 应该内涵一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就应该调用 new-handler,它也应该有能力处理 0 bytes 申请,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”;operator delete 应该在收到 null 指针时不做任何事,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”)</li><li>写了 placement new 也要写 placement delete(当你写一个 placement operator new,请确定也写出了对应的 placement operator delete,否则可能会发生隐微而时断时续的内存泄漏;当你声明 placement new 和 placement delete,请确定不要无意识(非故意)地遮掩了它们地正常版本)</li><li>不要轻忽编译器的警告</li><li>让自己熟悉包括 TR1 在内的标准程序库(TR1,C++ Technical Report 1,C++11 标准的草稿文件)</li><li>让自己熟悉 Boost(准标准库)</li></ol><h3 id="More-Effective-c"><a href="#More-Effective-c" class="headerlink" title="More Effective c++"></a>More Effective c++</h3><ol><li>仔细区别 pointers 和 references(当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由 pointers 达成,你就应该选择 references;任何其他时候,请采用 pointers)</li><li>最好使用 C++ 转型操作符(<code>static_cast</code>、<code>const_cast</code>、<code>dynamic_cast</code>、<code>reinterpret_cast</code>)</li><li>绝不要以多态(polymorphically)方式处理数组(多态(polymorphism)和指针算术不能混用;数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用)</li><li>非必要不提供 default constructor(避免对象中的字段被无意义地初始化)</li><li>对定制的 “类型转换函数” 保持警觉(单自变量 constructors 可通过简易法(explicit 关键字)或代理类(proxy classes)来避免编译器误用;隐式类型转换操作符可改为显式的 member function 来避免非预期行为)</li><li>区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式(前置式累加后取出,返回一个 reference;后置式取出后累加,返回一个 const 对象;处理用户定制类型时,应该尽可能使用前置式 increment;后置式的实现应以其前置式兄弟为基础)</li><li>千万不要重载 <code>&&</code>,<code>||</code> 和 <code>,</code> 操作符(<code>&&</code> 与 <code>||</code> 的重载会用 “函数调用语义” 取代 “骤死式语义”;<code>,</code> 的重载导致不能保证左侧表达式一定比右侧表达式更早被评估)</li><li>了解各种不同意义的 new 和 delete(<code>new operator</code>、<code>operator new</code>、<code>placement new</code>、<code>operator new[]</code>;<code>delete operator</code>、<code>operator delete</code>、<code>destructor</code>、<code>operator delete[]</code>)</li><li>利用 destructors 避免泄漏资源(在 destructors 释放资源可以避免异常时的资源泄漏)</li><li>在 constructors 内阻止资源泄漏(由于 C++ 只会析构已构造完成的对象,因此在构造函数可以使用 try…catch 或者 auto_ptr(以及与之相似的 classes) 处理异常时资源泄露问题)</li><li>禁止异常流出 destructors 之外(原因:一、避免 terminate 函数在 exception 传播过程的栈展开(stack-unwinding)机制种被调用;二、协助确保 destructors 完成其应该完成的所有事情)</li><li>了解 “抛出一个 exception” 与 “传递一个参数” 或 “调用一个虚函数” 之间的差异(第一,exception objects 总是会被复制(by pointer 除外),如果以 by value 方式捕捉甚至被复制两次,而传递给函数参数的对象则不一定得复制;第二,“被抛出成为 exceptions” 的对象,其被允许的类型转换动作比 “被传递到函数去” 的对象少;第三,catch 子句以其 “出现于源代码的顺序” 被编译器检验对比,其中第一个匹配成功者便执行,而调用一个虚函数,被选中执行的是那个 “与对象类型最佳吻合” 的函数)</li><li>以 by reference 方式捕获 exceptions(可避免对象删除问题、exception objects 的切割问题,可保留捕捉标准 exceptions 的能力,可约束 exception object 需要复制的次数)</li><li>明智运用 exception specifications(exception specifications 对 “函数希望抛出什么样的 exceptions” 提供了卓越的说明;也有一些缺点,包括编译器只对它们做局部性检验而很容易不经意地违反,与可能会妨碍更上层的 exception 处理函数处理未预期的 exceptions)</li><li>了解异常处理的成本(粗略估计,如果使用 try 语句块,代码大约整体膨胀 5%-10%,执行速度亦大约下降这个数;因此请将你对 try 语句块和 exception specifications 的使用限制于非用不可的地点,并且在真正异常的情况下才抛出 exceptions)</li><li>谨记 80-20 法则(软件的整体性能几乎总是由其构成要素(代码)的一小部分决定的,可使用程序分析器(program profiler)识别出消耗资源的代码)</li><li>考虑使用 lazy evaluation(缓式评估)(可应用于:Reference Counting(引用计数)来避免非必要的对象复制、区分 operator[] 的读和写动作来做不同的事情、Lazy Fetching(缓式取出)来避免非必要的数据库读取动作、Lazy Expression Evaluation(表达式缓评估)来避免非必要的数值计算动作)</li><li>分期摊还预期的计算成本(当你必须支持某些运算而其结构几乎总是被需要,或其结果常常被多次需要的时候,over-eager evaluation(超急评估)可以改善程序效率)</li></ol><h3 id="Google-C-Style-Guide"><a href="#Google-C-Style-Guide" class="headerlink" title="Google C++ Style Guide"></a>Google C++ Style Guide</h3><ul><li>英文:<a href="https://google.github.io/styleguide/cppguide.html" target="_blank" rel="noopener">Google C++ Style Guide</a></li><li>中文:<a href="https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/" target="_blank" rel="noopener">C++ 风格指南</a></li></ul><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><ul><li><a href="http://www.stroustrup.com/bs_faq.html" target="_blank" rel="noopener">Bjarne Stroustrup 的常见问题</a></li><li><a href="http://www.stroustrup.com/bs_faq2.html" target="_blank" rel="noopener">Bjarne Stroustrup 的 C++ 风格和技巧常见问题</a></li></ul><p><a id="stl"></a></p><h2 id="📦-STL"><a href="#📦-STL" class="headerlink" title="📦 STL"></a>📦 STL</h2><h3 id="STL-索引"><a href="#STL-索引" class="headerlink" title="STL 索引"></a>STL 索引</h3><p><a href="https://github.com/huihut/interview/tree/master/STL" target="_blank" rel="noopener">STL 方法含义索引</a></p><h3 id="STL-容器"><a href="#STL-容器" class="headerlink" title="STL 容器"></a>STL 容器</h3><table><thead><tr><th>容器</th><th>底层数据结构</th><th>时间复杂度</th><th>有无序</th><th>可不可重复</th><th>其他</th></tr></thead><tbody><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#array" target="_blank" rel="noopener">array</a></td><td>数组</td><td>随机读改 O(1)</td><td>无序</td><td>可重复</td><td>支持随机访问</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#vector" target="_blank" rel="noopener">vector</a></td><td>数组</td><td>随机读改、尾部插入、尾部删除 O(1)<br/>头部插入、头部删除 O(n)</td><td>无序</td><td>可重复</td><td>支持随机访问</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#deque" target="_blank" rel="noopener">deque</a></td><td>双端队列</td><td>头尾插入、头尾删除 O(1)</td><td>无序</td><td>可重复</td><td>一个中央控制器 + 多个缓冲区,支持首尾快速增删,支持随机访问</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#forward_list" target="_blank" rel="noopener">forward_list</a></td><td>单向链表</td><td>插入、删除 O(1)</td><td>无序</td><td>可重复</td><td>不支持随机访问</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#list" target="_blank" rel="noopener">list</a></td><td>双向链表</td><td>插入、删除 O(1)</td><td>无序</td><td>可重复</td><td>不支持随机访问</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#stack" target="_blank" rel="noopener">stack</a></td><td>deque / list</td><td>顶部插入、顶部删除 O(1)</td><td>无序</td><td>可重复</td><td>deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#queue" target="_blank" rel="noopener">queue</a></td><td>deque / list</td><td>尾部插入、头部删除 O(1)</td><td>无序</td><td>可重复</td><td>deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#priority_queue" target="_blank" rel="noopener">priority_queue</a></td><td>vector + max-heap</td><td>插入、删除 O(log<sub>2</sub>n)</td><td>有序</td><td>可重复</td><td>vector容器+heap处理规则</td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#set" target="_blank" rel="noopener">set</a></td><td>红黑树</td><td>插入、删除、查找 O(log<sub>2</sub>n)</td><td>有序</td><td>不可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#multiset" target="_blank" rel="noopener">multiset</a></td><td>红黑树</td><td>插入、删除、查找 O(log<sub>2</sub>n)</td><td>有序</td><td>可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#map" target="_blank" rel="noopener">map</a></td><td>红黑树</td><td>插入、删除、查找 O(log<sub>2</sub>n)</td><td>有序</td><td>不可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#multimap" target="_blank" rel="noopener">multimap</a></td><td>红黑树</td><td>插入、删除、查找 O(log<sub>2</sub>n)</td><td>有序</td><td>可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#unordered_set" target="_blank" rel="noopener">unordered_set</a></td><td>哈希表</td><td>插入、删除、查找 O(1) 最差 O(n)</td><td>无序</td><td>不可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#unordered_multiset" target="_blank" rel="noopener">unordered_multiset</a></td><td>哈希表</td><td>插入、删除、查找 O(1) 最差 O(n)</td><td>无序</td><td>可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#unordered_map" target="_blank" rel="noopener">unordered_map</a></td><td>哈希表</td><td>插入、删除、查找 O(1) 最差 O(n)</td><td>无序</td><td>不可重复</td><td></td></tr><tr><td><a href="https://github.com/huihut/interview/tree/master/STL#unordered_multimap" target="_blank" rel="noopener">unordered_multimap</a></td><td>哈希表</td><td>插入、删除、查找 O(1) 最差 O(n)</td><td>无序</td><td>可重复</td><td></td></tr></tbody></table><h3 id="STL-算法"><a href="#STL-算法" class="headerlink" title="STL 算法"></a>STL 算法</h3><table><thead><tr><th>算法</th><th>底层算法</th><th>时间复杂度</th><th>可不可重复</th></tr></thead><tbody><tr><td><a href="http://www.cplusplus.com/reference/algorithm/find/" target="_blank" rel="noopener">find</a></td><td>顺序查找</td><td>O(n)</td><td>可重复</td></tr><tr><td><a href="https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808" target="_blank" rel="noopener">sort</a></td><td><a href="https://en.wikipedia.org/wiki/Introsort" target="_blank" rel="noopener">内省排序</a></td><td>O(n*log<sub>2</sub>n)</td><td>可重复</td></tr></tbody></table><p><a id="data-structure"></a></p><h2 id="〽️-数据结构"><a href="#〽️-数据结构" class="headerlink" title="〽️ 数据结构"></a>〽️ 数据结构</h2><h3 id="顺序结构"><a href="#顺序结构" class="headerlink" title="顺序结构"></a>顺序结构</h3><h4 id="顺序栈(Sequence-Stack)"><a href="#顺序栈(Sequence-Stack)" class="headerlink" title="顺序栈(Sequence Stack)"></a>顺序栈(Sequence Stack)</h4><p><a href="DataStructure/SqStack.cpp">SqStack.cpp</a></p><p>顺序栈数据结构和图片</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>ElemType *elem;<span class="hljs-keyword">int</span> top;<span class="hljs-keyword">int</span> size;<span class="hljs-keyword">int</span> increment;} SqStack;</code></pre><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/SqStack.png" srcset="/img/loading.gif" alt=""></p><h4 id="队列(Sequence-Queue)"><a href="#队列(Sequence-Queue)" class="headerlink" title="队列(Sequence Queue)"></a>队列(Sequence Queue)</h4><p>队列数据结构</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>ElemType * elem;<span class="hljs-keyword">int</span> front;<span class="hljs-keyword">int</span> rear;<span class="hljs-keyword">int</span> maxSize;}SqQueue;</code></pre><h5 id="非循环队列"><a href="#非循环队列" class="headerlink" title="非循环队列"></a>非循环队列</h5><p>非循环队列图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/SqQueue.png" srcset="/img/loading.gif" alt=""></p><p><code>SqQueue.rear++</code></p><h5 id="循环队列"><a href="#循环队列" class="headerlink" title="循环队列"></a>循环队列</h5><p>循环队列图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/SqLoopStack.png" srcset="/img/loading.gif" alt=""></p><p><code>SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize</code></p><h4 id="顺序表(Sequence-List)"><a href="#顺序表(Sequence-List)" class="headerlink" title="顺序表(Sequence List)"></a>顺序表(Sequence List)</h4><p><a href="DataStructure/SqList.cpp">SqList.cpp</a></p><p>顺序表数据结构和图片</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>ElemType *elem;<span class="hljs-keyword">int</span> length;<span class="hljs-keyword">int</span> size;<span class="hljs-keyword">int</span> increment;} SqList;</code></pre><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/SqList.png" srcset="/img/loading.gif" alt=""></p><h3 id="链式结构"><a href="#链式结构" class="headerlink" title="链式结构"></a>链式结构</h3><p><a href="DataStructure/LinkList.cpp">LinkList.cpp</a></p><p><a href="DataStructure/LinkList_with_head.cpp">LinkList_with_head.cpp</a></p><p>链式数据结构</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LNode</span> {</span> ElemType data; <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LNode</span> *<span class="hljs-title">next</span>;</span>} LNode, *LinkList;</code></pre><h4 id="链队列(Link-Queue)"><a href="#链队列(Link-Queue)" class="headerlink" title="链队列(Link Queue)"></a>链队列(Link Queue)</h4><p>链队列图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/LinkQueue.png" srcset="/img/loading.gif" alt=""></p><h4 id="线性表的链式表示"><a href="#线性表的链式表示" class="headerlink" title="线性表的链式表示"></a>线性表的链式表示</h4><h5 id="单链表(Link-List)"><a href="#单链表(Link-List)" class="headerlink" title="单链表(Link List)"></a>单链表(Link List)</h5><p>单链表图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/LinkList.png" srcset="/img/loading.gif" alt=""></p><h5 id="双向链表(Du-Link-List)"><a href="#双向链表(Du-Link-List)" class="headerlink" title="双向链表(Du-Link-List)"></a>双向链表(Du-Link-List)</h5><p>双向链表图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/DuLinkList.png" srcset="/img/loading.gif" alt=""></p><h5 id="循环链表(Cir-Link-List)"><a href="#循环链表(Cir-Link-List)" class="headerlink" title="循环链表(Cir-Link-List)"></a>循环链表(Cir-Link-List)</h5><p>循环链表图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/CirLinkList.png" srcset="/img/loading.gif" alt=""></p><h3 id="哈希表"><a href="#哈希表" class="headerlink" title="哈希表"></a>哈希表</h3><p><a href="DataStructure/HashTable.cpp">HashTable.cpp</a></p><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><p>哈希函数:<code>H(key): K -> D , key ∈ K</code></p><h4 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h4><ul><li>直接定址法</li><li>除留余数法</li><li>数字分析法</li><li>折叠法</li><li>平方取中法</li></ul><h4 id="冲突处理方法"><a href="#冲突处理方法" class="headerlink" title="冲突处理方法"></a>冲突处理方法</h4><ul><li>链地址法:key 相同的用单链表链接</li><li>开放定址法<ul><li>线性探测法:key 相同 -> 放到 key 的下一个位置,<code>Hi = (H(key) + i) % m</code></li><li>二次探测法:key 相同 -> 放到 <code>Di = 1^2, -1^2, ..., ±(k)^2,(k<=m/2)</code></li><li>随机探测法:<code>H = (H(key) + 伪随机数) % m</code></li></ul></li></ul><h4 id="线性探测的哈希表数据结构"><a href="#线性探测的哈希表数据结构" class="headerlink" title="线性探测的哈希表数据结构"></a>线性探测的哈希表数据结构</h4><p>线性探测的哈希表数据结构和图片</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">char</span> KeyType;<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>KeyType key;}RcdType;<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>RcdType *rcd;<span class="hljs-keyword">int</span> size;<span class="hljs-keyword">int</span> count;<span class="hljs-keyword">bool</span> *tag;}HashTable;</code></pre><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/HashTable.png" srcset="/img/loading.gif" alt=""></p><h3 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h3><h4 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h4><p>函数直接或间接地调用自身</p><h4 id="递归与分治"><a href="#递归与分治" class="headerlink" title="递归与分治"></a>递归与分治</h4><ul><li>分治法<ul><li>问题的分解</li><li>问题规模的分解</li></ul></li><li>折半查找(递归)</li><li>归并排序(递归)</li><li>快速排序(递归)</li></ul><h4 id="递归与迭代"><a href="#递归与迭代" class="headerlink" title="递归与迭代"></a>递归与迭代</h4><ul><li>迭代:反复利用变量旧值推出新值</li><li>折半查找(迭代)</li><li>归并排序(迭代)</li></ul><h4 id="广义表"><a href="#广义表" class="headerlink" title="广义表"></a>广义表</h4><h5 id="头尾链表存储表示"><a href="#头尾链表存储表示" class="headerlink" title="头尾链表存储表示"></a>头尾链表存储表示</h5><p>广义表的头尾链表存储表示和图片</p><pre><code class="hljs cpp"><span class="hljs-comment">// 广义表的头尾链表存储表示</span><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">enum</span> {ATOM, LIST} ElemTag;<span class="hljs-comment">// ATOM==0:原子,LIST==1:子表</span><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GLNode</span> {</span> ElemTag tag; <span class="hljs-comment">// 公共部分,用于区分原子结点和表结点</span> <span class="hljs-keyword">union</span> { <span class="hljs-comment">// 原子结点和表结点的联合部分</span> AtomType atom; <span class="hljs-comment">// atom 是原子结点的值域,AtomType 由用户定义</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GLNode</span> *<span class="hljs-title">hp</span>, *<span class="hljs-title">tp</span>;</span> } ptr; <span class="hljs-comment">// ptr 是表结点的指针域,prt.hp 和 ptr.tp 分别指向表头和表尾</span> } a;} *GList, GLNode;</code></pre><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList1.png" srcset="/img/loading.gif" alt=""></p><h5 id="扩展线性链表存储表示"><a href="#扩展线性链表存储表示" class="headerlink" title="扩展线性链表存储表示"></a>扩展线性链表存储表示</h5><p>扩展线性链表存储表示和图片</p><pre><code class="hljs cpp"><span class="hljs-comment">// 广义表的扩展线性链表存储表示</span><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">enum</span> {ATOM, LIST} ElemTag;<span class="hljs-comment">// ATOM==0:原子,LIST==1:子表</span><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GLNode1</span> {</span> ElemTag tag; <span class="hljs-comment">// 公共部分,用于区分原子结点和表结点</span> <span class="hljs-keyword">union</span> { <span class="hljs-comment">// 原子结点和表结点的联合部分</span> AtomType atom; <span class="hljs-comment">// 原子结点的值域</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GLNode1</span> *<span class="hljs-title">hp</span>;</span> <span class="hljs-comment">// 表结点的表头指针</span> } a; <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GLNode1</span> *<span class="hljs-title">tp</span>;</span> <span class="hljs-comment">// 相当于线性链表的 next,指向下一个元素结点</span>} *GList1, GLNode1;</code></pre><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList2.png" srcset="/img/loading.gif" alt=""></p><h3 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h3><p><a href="DataStructure/BinaryTree.cpp">BinaryTree.cpp</a></p><h4 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h4><ol><li>非空二叉树第 i 层最多 2<sup>(i-1)</sup> 个结点 (i >= 1)</li><li>深度为 k 的二叉树最多 2<sup>k</sup> - 1 个结点 (k >= 1)</li><li>度为 0 的结点数为 n<sub>0</sub>,度为 2 的结点数为 n<sub>2</sub>,则 n<sub>0</sub> = n<sub>2</sub> + 1</li><li>有 n 个结点的完全二叉树深度 k = ⌊ log<sub>2</sub>(n) ⌋ + 1 </li><li>对于含 n 个结点的完全二叉树中编号为 i (1 <= i <= n) 的结点<ol><li>若 i = 1,为根,否则双亲为 ⌊ i / 2 ⌋</li><li>若 2i > n,则 i 结点没有左孩子,否则孩子编号为 2i</li><li>若 2i + 1 > n,则 i 结点没有右孩子,否则孩子编号为 2i + 1</li></ol></li></ol><h4 id="存储结构"><a href="#存储结构" class="headerlink" title="存储结构"></a>存储结构</h4><p>二叉树数据结构</p><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span></span><span class="hljs-class">{</span> TElemType data; <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">lchild</span>, *<span class="hljs-title">rchild</span>;</span>}BiTNode, *BiTree;</code></pre><h5 id="顺序存储"><a href="#顺序存储" class="headerlink" title="顺序存储"></a>顺序存储</h5><p>二叉树顺序存储图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/SqBinaryTree.png" srcset="/img/loading.gif" alt=""></p><h5 id="链式存储"><a href="#链式存储" class="headerlink" title="链式存储"></a>链式存储</h5><p>二叉树链式存储图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/LinkBinaryTree.png" srcset="/img/loading.gif" alt=""></p><h4 id="遍历方式"><a href="#遍历方式" class="headerlink" title="遍历方式"></a>遍历方式</h4><ul><li>先序遍历</li><li>中序遍历</li><li>后续遍历</li><li>层次遍历</li></ul><h4 id="分类-1"><a href="#分类-1" class="headerlink" title="分类"></a>分类</h4><ul><li>满二叉树</li><li>完全二叉树(堆)<ul><li>大顶堆:根 >= 左 && 根 >= 右</li><li>小顶堆:根 <= 左 && 根 <= 右</li></ul></li><li>二叉查找树(二叉排序树):左 < 根 < 右</li><li>平衡二叉树(AVL树):| 左子树树高 - 右子树树高 | <= 1</li><li>最小失衡树:平衡二叉树插入新结点导致失衡的子树:调整:<ul><li>LL型:根的左孩子右旋</li><li>RR型:根的右孩子左旋</li><li>LR型:根的左孩子左旋,再右旋</li><li>RL型:右孩子的左子树,先右旋,再左旋</li></ul></li></ul><h3 id="其他树及森林"><a href="#其他树及森林" class="headerlink" title="其他树及森林"></a>其他树及森林</h3><h4 id="树的存储结构"><a href="#树的存储结构" class="headerlink" title="树的存储结构"></a>树的存储结构</h4><ul><li>双亲表示法</li><li>双亲孩子表示法</li><li>孩子兄弟表示法</li></ul><h4 id="并查集"><a href="#并查集" class="headerlink" title="并查集"></a>并查集</h4><p>一种不相交的子集所构成的集合 S = {S1, S2, …, Sn}</p><h4 id="平衡二叉树(AVL树)"><a href="#平衡二叉树(AVL树)" class="headerlink" title="平衡二叉树(AVL树)"></a>平衡二叉树(AVL树)</h4><h5 id="性质-1"><a href="#性质-1" class="headerlink" title="性质"></a>性质</h5><ul><li>| 左子树树高 - 右子树树高 | <= 1</li><li>平衡二叉树必定是二叉搜索树,反之则不一定</li><li>最小二叉平衡树的节点的公式:<code>F(n)=F(n-1)+F(n-2)+1</code> (1 是根节点,F(n-1) 是左子树的节点数量,F(n-2) 是右子树的节点数量)</li></ul><p>平衡二叉树图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/Self-balancingBinarySearchTree.png" srcset="/img/loading.gif" alt=""></p><h5 id="最小失衡树"><a href="#最小失衡树" class="headerlink" title="最小失衡树"></a>最小失衡树</h5><p>平衡二叉树插入新结点导致失衡的子树</p><p>调整:</p><ul><li>LL 型:根的左孩子右旋</li><li>RR 型:根的右孩子左旋</li><li>LR 型:根的左孩子左旋,再右旋</li><li>RL 型:右孩子的左子树,先右旋,再左旋</li></ul><h4 id="红黑树"><a href="#红黑树" class="headerlink" title="红黑树"></a>红黑树</h4><p><a href="DataStructure/RedBlackTree.cpp">RedBlackTree.cpp</a></p><h5 id="红黑树的特征是什么?"><a href="#红黑树的特征是什么?" class="headerlink" title="红黑树的特征是什么?"></a>红黑树的特征是什么?</h5><ol><li>节点是红色或黑色。</li><li>根是黑色。</li><li>所有叶子都是黑色(叶子是 NIL 节点)。</li><li>每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)(新增节点的父节点必须相同)</li><li>从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。(新增节点必须为红)</li></ol><h5 id="调整"><a href="#调整" class="headerlink" title="调整"></a>调整</h5><ol><li>变色</li><li>左旋</li><li>右旋</li></ol><h5 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h5><ul><li>关联数组:如 STL 中的 map、set</li></ul><h5 id="红黑树、B-树、B-树的区别?"><a href="#红黑树、B-树、B-树的区别?" class="headerlink" title="红黑树、B 树、B+ 树的区别?"></a>红黑树、B 树、B+ 树的区别?</h5><ul><li>红黑树的深度比较大,而 B 树和 B+ 树的深度则相对要小一些</li><li>B+ 树则将数据都保存在叶子节点,同时通过链表的形式将他们连接在一起。</li></ul><h4 id="B-树(B-tree)、B-树(B-tree)"><a href="#B-树(B-tree)、B-树(B-tree)" class="headerlink" title="B 树(B-tree)、B+ 树(B+-tree)"></a>B 树(B-tree)、B+ 树(B+-tree)</h4><p>B 树、B+ 树图片</p><p><img src="https://i.stack.imgur.com/l6UyF.png" srcset="/img/loading.gif" alt="B 树(B-tree)、B+ 树(B+-tree)"></p><h5 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h5><ul><li>一般化的二叉查找树(binary search tree)</li><li>“矮胖”,内部(非叶子)节点可以拥有可变数量的子节点(数量范围预先定义好)</li></ul><h5 id="应用-1"><a href="#应用-1" class="headerlink" title="应用"></a>应用</h5><ul><li>大部分文件系统、数据库系统都采用B树、B+树作为索引结构</li></ul><h5 id="区别-1"><a href="#区别-1" class="headerlink" title="区别"></a>区别</h5><ul><li>B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中。</li><li>B+树中所有叶子节点都是通过指针连接在一起,而B树不会。</li></ul><h5 id="B树的优点"><a href="#B树的优点" class="headerlink" title="B树的优点"></a>B树的优点</h5><p>对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。</p><h5 id="B-树的优点"><a href="#B-树的优点" class="headerlink" title="B+树的优点"></a>B+树的优点</h5><ul><li>非叶子节点不会带上 ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。</li><li>叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。</li></ul><blockquote><p>B 树、B+ 树区别来自:<a href="https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees" target="_blank" rel="noopener">differences-between-b-trees-and-b-trees</a>、<a href="https://www.cnblogs.com/ivictor/p/5849061.html" target="_blank" rel="noopener">B树和B+树的区别</a></p></blockquote><h4 id="八叉树"><a href="#八叉树" class="headerlink" title="八叉树"></a>八叉树</h4><p>八叉树图片</p><p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png" srcset="/img/loading.gif" alt=""></p><p>八叉树(octree),或称八元树,是一种用于描述三维空间(划分空间)的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。</p><h5 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h5><ul><li>三维计算机图形</li><li>最邻近搜索</li></ul><p><a id="algorithm"></a></p><h2 id="⚡️-算法"><a href="#⚡️-算法" class="headerlink" title="⚡️ 算法"></a>⚡️ 算法</h2><h3 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h3><table><thead><tr><th>排序算法</th><th>平均时间复杂度</th><th>最差时间复杂度</th><th>空间复杂度</th><th>数据对象稳定性</th></tr></thead><tbody><tr><td><a href="Algorithm/BubbleSort.h">冒泡排序</a></td><td>O(n<sup>2</sup>)</td><td>O(n<sup>2</sup>)</td><td>O(1)</td><td>稳定</td></tr><tr><td><a href="Algorithm/SelectionSort.h">选择排序</a></td><td>O(n<sup>2</sup>)</td><td>O(n<sup>2</sup>)</td><td>O(1)</td><td>数组不稳定、链表稳定</td></tr><tr><td><a href="Algorithm/InsertSort.h">插入排序</a></td><td>O(n<sup>2</sup>)</td><td>O(n<sup>2</sup>)</td><td>O(1)</td><td>稳定</td></tr><tr><td><a href="Algorithm/QuickSort.h">快速排序</a></td><td>O(n*log<sub>2</sub>n)</td><td>O(n<sup>2</sup>)</td><td>O(log<sub>2</sub>n)</td><td>不稳定</td></tr><tr><td><a href="Algorithm/HeapSort.cpp">堆排序</a></td><td>O(n*log<sub>2</sub>n)</td><td>O(n*log<sub>2</sub>n)</td><td>O(1)</td><td>不稳定</td></tr><tr><td><a href="Algorithm/MergeSort.h">归并排序</a></td><td>O(n*log<sub>2</sub>n)</td><td>O(n*log<sub>2</sub>n)</td><td>O(n)</td><td>稳定</td></tr><tr><td><a href="Algorithm/ShellSort.h">希尔排序</a></td><td>O(n*log<sup>2</sup>n)</td><td>O(n<sup>2</sup>)</td><td>O(1)</td><td>不稳定</td></tr><tr><td><a href="Algorithm/CountSort.cpp">计数排序</a></td><td>O(n+m)</td><td>O(n+m)</td><td>O(n+m)</td><td>稳定</td></tr><tr><td><a href="Algorithm/BucketSort.cpp">桶排序</a></td><td>O(n)</td><td>O(n)</td><td>O(m)</td><td>稳定</td></tr><tr><td><a href="Algorithm/RadixSort.h">基数排序</a></td><td>O(k*n)</td><td>O(n<sup>2</sup>)</td><td></td><td>稳定</td></tr></tbody></table><blockquote><ul><li>均按从小到大排列</li><li>k:代表数值中的 “数位” 个数</li><li>n:代表数据规模</li><li>m:代表数据的最大值减最小值</li><li>来自:<a href="https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95" target="_blank" rel="noopener">wikipedia . 排序算法</a></li></ul></blockquote><h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><table><thead><tr><th>查找算法</th><th>平均时间复杂度</th><th>空间复杂度</th><th>查找条件</th></tr></thead><tbody><tr><td><a href="Algorithm/SequentialSearch.h">顺序查找</a></td><td>O(n)</td><td>O(1)</td><td>无序或有序</td></tr><tr><td><a href="Algorithm/BinarySearch.h">二分查找(折半查找)</a></td><td>O(log<sub>2</sub>n)</td><td>O(1)</td><td>有序</td></tr><tr><td><a href="Algorithm/InsertionSearch.h">插值查找</a></td><td>O(log<sub>2</sub>(log<sub>2</sub>n))</td><td>O(1)</td><td>有序</td></tr><tr><td><a href="Algorithm/FibonacciSearch.cpp">斐波那契查找</a></td><td>O(log<sub>2</sub>n)</td><td>O(1)</td><td>有序</td></tr><tr><td><a href="DataStructure/HashTable.cpp">哈希查找</a></td><td>O(1)</td><td>O(n)</td><td>无序或有序</td></tr><tr><td><a href="Algorithm/BSTSearch.h">二叉查找树(二叉搜索树查找)</a></td><td>O(log<sub>2</sub>n)</td><td></td><td></td></tr><tr><td><a href="DataStructure/RedBlackTree.cpp">红黑树</a></td><td>O(log<sub>2</sub>n)</td><td></td><td></td></tr><tr><td>2-3树</td><td>O(log<sub>2</sub>n - log<sub>3</sub>n)</td><td></td><td></td></tr><tr><td>B树/B+树</td><td>O(log<sub>2</sub>n)</td><td></td><td></td></tr></tbody></table><h3 id="图搜索算法"><a href="#图搜索算法" class="headerlink" title="图搜索算法"></a>图搜索算法</h3><table><thead><tr><th>图搜索算法</th><th>数据结构</th><th>遍历时间复杂度</th><th>空间复杂度</th></tr></thead><tbody><tr><td><a href="https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2" target="_blank" rel="noopener">BFS广度优先搜索</a></td><td>邻接矩阵<br/>邻接链表</td><td>O(|v|<sup>2</sup>)<br/>O(|v|+|E|)</td><td>O(|v|<sup>2</sup>)<br/>O(|v|+|E|)</td></tr><tr><td><a href="https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2" target="_blank" rel="noopener">DFS深度优先搜索</a></td><td>邻接矩阵<br/>邻接链表</td><td>O(|v|<sup>2</sup>)<br/>O(|v|+|E|)</td><td>O(|v|<sup>2</sup>)<br/>O(|v|+|E|)</td></tr></tbody></table><h3 id="其他算法"><a href="#其他算法" class="headerlink" title="其他算法"></a>其他算法</h3><table><thead><tr><th>算法</th><th>思想</th><th>应用</th></tr></thead><tbody><tr><td><a href="https://zh.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95" target="_blank" rel="noopener">分治法</a></td><td>把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并</td><td><a href="https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem" target="_blank" rel="noopener">循环赛日程安排问题</a>、排序算法(快速排序、归并排序)</td></tr><tr><td><a href="https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92" target="_blank" rel="noopener">动态规划</a></td><td>通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,适用于有重叠子问题和最优子结构性质的问题</td><td><a href="https://github.com/huihut/interview/tree/master/Problems/KnapsackProblem" target="_blank" rel="noopener">背包问题</a>、斐波那契数列</td></tr><tr><td><a href="https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95" target="_blank" rel="noopener">贪心法</a></td><td>一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法</td><td>旅行推销员问题(最短路径问题)、最小生成树、哈夫曼编码</td></tr></tbody></table><p><a id="problems"></a></p><h2 id="❓-Problems"><a href="#❓-Problems" class="headerlink" title="❓ Problems"></a>❓ Problems</h2><h3 id="Single-Problem"><a href="#Single-Problem" class="headerlink" title="Single Problem"></a>Single Problem</h3><ul><li><a href="Problems/ChessboardCoverageProblem">Chessboard Coverage Problem(棋盘覆盖问题)</a></li><li><a href="Problems/KnapsackProblem">Knapsack Problem(背包问题)</a></li><li><a href="Problems/NeumannNeighborProblem">Neumann Neighbor Problem(冯诺依曼邻居问题)</a></li><li><a href="Problems/RoundRobinProblem">Round Robin Problem(循环赛日程安排问题)</a></li><li><a href="Problems/TubingProblem">Tubing Problem(输油管道问题)</a></li></ul><h3 id="Leetcode-Problems"><a href="#Leetcode-Problems" class="headerlink" title="Leetcode Problems"></a>Leetcode Problems</h3><ul><li><a href="https://github.com/haoel/leetcode" target="_blank" rel="noopener">Github . haoel/leetcode</a></li><li><a href="https://github.com/pezy/LeetCode" target="_blank" rel="noopener">Github . pezy/LeetCode</a></li></ul><h3 id="剑指-Offer"><a href="#剑指-Offer" class="headerlink" title="剑指 Offer"></a>剑指 Offer</h3><ul><li><a href="https://github.com/zhedahht/CodingInterviewChinese2" target="_blank" rel="noopener">Github . zhedahht/CodingInterviewChinese2</a></li><li><a href="https://github.com/gatieme/CodingInterviews" target="_blank" rel="noopener">Github . gatieme/CodingInterviews</a></li></ul><h3 id="Cracking-the-Coding-Interview-程序员面试金典"><a href="#Cracking-the-Coding-Interview-程序员面试金典" class="headerlink" title="Cracking the Coding Interview 程序员面试金典"></a>Cracking the Coding Interview 程序员面试金典</h3><ul><li><a href="https://github.com/careercup/ctci" target="_blank" rel="noopener">Github . careercup/ctci</a></li><li><a href="https://www.nowcoder.com/ta/cracking-the-coding-interview" target="_blank" rel="noopener">牛客网 . 程序员面试金典</a></li></ul><h3 id="牛客网"><a href="#牛客网" class="headerlink" title="牛客网"></a>牛客网</h3><ul><li><a href="https://www.nowcoder.com/activity/oj" target="_blank" rel="noopener">牛客网 . 在线编程专题</a></li></ul><p><a id="os"></a></p><h2 id="💻-操作系统"><a href="#💻-操作系统" class="headerlink" title="💻 操作系统"></a>💻 操作系统</h2><h3 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h3><p>对于有线程系统:</p><ul><li>进程是资源分配的独立单位</li><li>线程是资源调度的独立单位</li></ul><p>对于无线程系统:</p><ul><li>进程是资源调度、分配的独立单位</li></ul><h4 id="进程之间的通信方式以及优缺点"><a href="#进程之间的通信方式以及优缺点" class="headerlink" title="进程之间的通信方式以及优缺点"></a>进程之间的通信方式以及优缺点</h4><ul><li>管道(PIPE)<ul><li>有名管道:一种半双工的通信方式,它允许无亲缘关系进程间的通信<ul><li>优点:可以实现任意关系的进程间的通信</li><li>缺点:<ol><li>长期存于系统中,使用不当容易出错</li><li>缓冲区有限</li></ol></li></ul></li><li>无名管道:一种半双工的通信方式,只能在具有亲缘关系的进程间使用(父子进程)<ul><li>优点:简单方便</li><li>缺点:<ol><li>局限于单向通信 </li><li>只能创建在它的进程以及其有亲缘关系的进程之间</li><li>缓冲区有限</li></ol></li></ul></li></ul></li><li>信号量(Semaphore):一个计数器,可以用来控制多个线程对共享资源的访问<ul><li>优点:可以同步进程</li><li>缺点:信号量有限</li></ul></li><li>信号(Signal):一种比较复杂的通信方式,用于通知接收进程某个事件已经发生</li><li>消息队列(Message Queue):是消息的链表,存放在内核中并由消息队列标识符标识<ul><li>优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便</li><li>缺点:信息的复制需要额外消耗 CPU 的时间,不适宜于信息量大或操作频繁的场合</li></ul></li><li>共享内存(Shared Memory):映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问<ul><li>优点:无须复制,快捷,信息量大</li><li>缺点:<ol><li>通信是通过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,因此进程间的读写操作的同步问题</li><li>利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信</li></ol></li></ul></li><li>套接字(Socket):可用于不同计算机间的进程通信<ul><li>优点:<ol><li>传输数据为字节级,传输数据可自定义,数据量小效率高</li><li>传输数据时间短,性能高</li><li>适合于客户端和服务器端之间信息实时交互</li><li>可以加密,数据安全性强</li></ol></li><li>缺点:需对传输的数据进行解析,转化成应用级的数据。</li></ul></li></ul><h4 id="线程之间的通信方式"><a href="#线程之间的通信方式" class="headerlink" title="线程之间的通信方式"></a>线程之间的通信方式</h4><ul><li>锁机制:包括互斥锁/量(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)、条件变量(condition)<ul><li>互斥锁/量(mutex):提供了以排他方式防止数据结构被并发修改的方法。</li><li>读写锁(reader-writer lock):允许多个线程同时读共享数据,而对写操作是互斥的。</li><li>自旋锁(spin lock)与互斥锁类似,都是为了保护共享资源。互斥锁是当资源被占用,申请者进入睡眠状态;而自旋锁则循环检测保持者是否已经释放锁。</li><li>条件变量(condition):可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。</li></ul></li><li>信号量机制(Semaphore)<ul><li>无名线程信号量</li><li>命名线程信号量</li></ul></li><li>信号机制(Signal):类似进程间的信号处理</li><li>屏障(barrier):屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。</li></ul><p>线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制 </p><blockquote><p>进程之间的通信方式以及优缺点来源于:<a href="http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977" target="_blank" rel="noopener">进程线程面试题总结</a></p></blockquote><h4 id="进程之间私有和共享的资源"><a href="#进程之间私有和共享的资源" class="headerlink" title="进程之间私有和共享的资源"></a>进程之间私有和共享的资源</h4><ul><li>私有:地址空间、堆、全局变量、栈、寄存器</li><li>共享:代码段,公共数据,进程目录,进程 ID</li></ul><h4 id="线程之间私有和共享的资源"><a href="#线程之间私有和共享的资源" class="headerlink" title="线程之间私有和共享的资源"></a>线程之间私有和共享的资源</h4><ul><li>私有:线程栈,寄存器,程序计数器</li><li>共享:堆,地址空间,全局变量,静态变量</li></ul><h4 id="多进程与多线程间的对比、优劣与选择"><a href="#多进程与多线程间的对比、优劣与选择" class="headerlink" title="多进程与多线程间的对比、优劣与选择"></a>多进程与多线程间的对比、优劣与选择</h4><h5 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h5><table><thead><tr><th>对比维度</th><th>多进程</th><th>多线程</th><th>总结</th></tr></thead><tbody><tr><td>数据共享、同步</td><td>数据共享复杂,需要用 IPC;数据是分开的,同步简单</td><td>因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂</td><td>各有优势</td></tr><tr><td>内存、CPU</td><td>占用内存多,切换复杂,CPU 利用率低</td><td>占用内存少,切换简单,CPU 利用率高</td><td>线程占优</td></tr><tr><td>创建销毁、切换</td><td>创建销毁、切换复杂,速度慢</td><td>创建销毁、切换简单,速度很快</td><td>线程占优</td></tr><tr><td>编程、调试</td><td>编程简单,调试简单</td><td>编程复杂,调试复杂</td><td>进程占优</td></tr><tr><td>可靠性</td><td>进程间不会互相影响</td><td>一个线程挂掉将导致整个进程挂掉</td><td>进程占优</td></tr><tr><td>分布式</td><td>适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单</td><td>适应于多核分布式</td><td>进程占优</td></tr></tbody></table><h5 id="优劣"><a href="#优劣" class="headerlink" title="优劣"></a>优劣</h5><table><thead><tr><th>优劣</th><th>多进程</th><th>多线程</th></tr></thead><tbody><tr><td>优点</td><td>编程、调试简单,可靠性较高</td><td>创建、销毁、切换速度快,内存、资源占用小</td></tr><tr><td>缺点</td><td>创建、销毁、切换速度慢,内存、资源占用大</td><td>编程、调试复杂,可靠性较差</td></tr></tbody></table><h5 id="选择"><a href="#选择" class="headerlink" title="选择"></a>选择</h5><ul><li>需要频繁创建销毁的优先用线程</li><li>需要进行大量计算的优先使用线程</li><li>强相关的处理用线程,弱相关的处理用进程</li><li>可能要扩展到多机分布的用进程,多核分布的用线程</li><li>都满足需求的情况下,用你最熟悉、最拿手的方式</li></ul><blockquote><p>多进程与多线程间的对比、优劣与选择来自:<a href="https://blog.csdn.net/lishenglong666/article/details/8557215" target="_blank" rel="noopener">多线程还是多进程的选择及区别</a></p></blockquote><h3 id="Linux-内核的同步方式"><a href="#Linux-内核的同步方式" class="headerlink" title="Linux 内核的同步方式"></a>Linux 内核的同步方式</h3><h4 id="原因"><a href="#原因" class="headerlink" title="原因"></a>原因</h4><p>在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。</p><h4 id="同步方式"><a href="#同步方式" class="headerlink" title="同步方式"></a>同步方式</h4><ul><li>原子操作</li><li>信号量(semaphore)</li><li>读写信号量(rw_semaphore)</li><li>自旋锁(spinlock)</li><li>大内核锁(BKL,Big Kernel Lock)</li><li>读写锁(rwlock)</li><li>大读者锁(brlock-Big Reader Lock)</li><li>读-拷贝修改(RCU,Read-Copy Update)</li><li>顺序锁(seqlock)</li></ul><blockquote><p>来自:<a href="https://www.ibm.com/developerworks/cn/linux/l-synch/part1/" target="_blank" rel="noopener">Linux 内核的同步机制,第 1 部分</a>、<a href="https://www.ibm.com/developerworks/cn/linux/l-synch/part2/" target="_blank" rel="noopener">Linux 内核的同步机制,第 2 部分</a></p></blockquote><h3 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h3><h4 id="原因-1"><a href="#原因-1" class="headerlink" title="原因"></a>原因</h4><ul><li>系统资源不足</li><li>资源分配不当</li><li>进程运行推进顺序不合适</li></ul><h4 id="产生条件"><a href="#产生条件" class="headerlink" title="产生条件"></a>产生条件</h4><ul><li>互斥</li><li>请求和保持</li><li>不剥夺</li><li>环路</li></ul><h4 id="预防"><a href="#预防" class="headerlink" title="预防"></a>预防</h4><ul><li>打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。</li><li>打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。</li><li>打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。</li><li>打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。</li><li>有序资源分配法</li><li>银行家算法</li></ul><h3 id="文件系统"><a href="#文件系统" class="headerlink" title="文件系统"></a>文件系统</h3><ul><li>Windows:FCB 表 + FAT + 位图</li><li>Unix:inode + 混合索引 + 成组链接</li></ul><h3 id="主机字节序与网络字节序"><a href="#主机字节序与网络字节序" class="headerlink" title="主机字节序与网络字节序"></a>主机字节序与网络字节序</h3><h4 id="主机字节序(CPU-字节序)"><a href="#主机字节序(CPU-字节序)" class="headerlink" title="主机字节序(CPU 字节序)"></a>主机字节序(CPU 字节序)</h4><h5 id="概念-2"><a href="#概念-2" class="headerlink" title="概念"></a>概念</h5><p>主机字节序又叫 CPU 字节序,其不是由操作系统决定的,而是由 CPU 指令集架构决定的。主机字节序分为两种:</p><ul><li>大端字节序(Big Endian):高序字节存储在低位地址,低序字节存储在高位地址</li><li>小端字节序(Little Endian):高序字节存储在高位地址,低序字节存储在低位地址</li></ul><h5 id="存储方式"><a href="#存储方式" class="headerlink" title="存储方式"></a>存储方式</h5><p>32 位整数 <code>0x12345678</code> 是从起始位置为 <code>0x00</code> 的地址开始存放,则:</p><table><thead><tr><th>内存地址</th><th>0x00</th><th>0x01</th><th>0x02</th><th>0x03</th></tr></thead><tbody><tr><td>大端</td><td>12</td><td>34</td><td>56</td><td>78</td></tr><tr><td>小端</td><td>78</td><td>56</td><td>34</td><td>12</td></tr></tbody></table><p>大端小端图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Big-Endian.svg.png" srcset="/img/loading.gif" alt="大端序"><br><img src="https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Little-Endian.svg.png" srcset="/img/loading.gif" alt="小端序"></p><h5 id="判断大端小端"><a href="#判断大端小端" class="headerlink" title="判断大端小端"></a>判断大端小端</h5><p>判断大端小端</p><p>可以这样判断自己 CPU 字节序是大端还是小端:</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>{<span class="hljs-keyword">int</span> i = <span class="hljs-number">0x12345678</span>;<span class="hljs-keyword">if</span> (*((<span class="hljs-keyword">char</span>*)&i) == <span class="hljs-number">0x12</span>)<span class="hljs-built_in">cout</span> << <span class="hljs-string">"大端"</span> << <span class="hljs-built_in">endl</span>;<span class="hljs-keyword">else</span><span class="hljs-built_in">cout</span> << <span class="hljs-string">"小端"</span> << <span class="hljs-built_in">endl</span>;<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h5 id="各架构处理器的字节序"><a href="#各架构处理器的字节序" class="headerlink" title="各架构处理器的字节序"></a>各架构处理器的字节序</h5><ul><li>x86(Intel、AMD)、MOS Technology 6502、Z80、VAX、PDP-11 等处理器为小端序;</li><li>Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除 V9 外)等处理器为大端序;</li><li>ARM(默认小端序)、PowerPC(除 PowerPC 970 外)、DEC Alpha、SPARC V9、MIPS、PA-RISC 及 IA64 的字节序是可配置的。</li></ul><h4 id="网络字节序"><a href="#网络字节序" class="headerlink" title="网络字节序"></a>网络字节序</h4><p>网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。</p><p>网络字节顺序采用:大端(Big Endian)排列方式。</p><h3 id="页面置换算法"><a href="#页面置换算法" class="headerlink" title="页面置换算法"></a>页面置换算法</h3><p>在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。</p><h4 id="分类-2"><a href="#分类-2" class="headerlink" title="分类"></a>分类</h4><ul><li>全局置换:在整个内存空间置换</li><li>局部置换:在本进程中进行置换</li></ul><h4 id="算法"><a href="#算法" class="headerlink" title="算法"></a>算法</h4><p>全局:</p><ul><li>工作集算法</li><li>缺页率置换算法</li></ul><p>局部:</p><ul><li>最佳置换算法(OPT)</li><li>先进先出置换算法(FIFO)</li><li>最近最久未使用(LRU)算法</li><li>时钟(Clock)置换算法</li></ul><p><a id="computer-network"></a></p><h2 id="☁️-计算机网络"><a href="#☁️-计算机网络" class="headerlink" title="☁️ 计算机网络"></a>☁️ 计算机网络</h2><blockquote><p>本节部分知识点来自《计算机网络(第 7 版)》</p></blockquote><p>计算机网络体系结构:</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png" srcset="/img/loading.gif" alt="计算机网络体系结构"></p><h3 id="各层作用及协议"><a href="#各层作用及协议" class="headerlink" title="各层作用及协议"></a>各层作用及协议</h3><table><thead><tr><th>分层</th><th>作用</th><th>协议</th></tr></thead><tbody><tr><td>物理层</td><td>通过媒介传输比特,确定机械及电气规范(比特 Bit)</td><td>RJ45、CLOCK、IEEE802.3(中继器,集线器)</td></tr><tr><td>数据链路层</td><td>将比特组装成帧和点到点的传递(帧 Frame)</td><td>PPP、FR、HDLC、VLAN、MAC(网桥,交换机)</td></tr><tr><td>网络层</td><td>负责数据包从源到宿的传递和网际互连(包 Packet)</td><td>IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP(路由器)</td></tr><tr><td>运输层</td><td>提供端到端的可靠报文传递和错误恢复( 段Segment)</td><td>TCP、UDP、SPX</td></tr><tr><td>会话层</td><td>建立、管理和终止会话(会话协议数据单元 SPDU)</td><td>NFS、SQL、NETBIOS、RPC</td></tr><tr><td>表示层</td><td>对数据进行翻译、加密和压缩(表示协议数据单元 PPDU)</td><td>JPEG、MPEG、ASII</td></tr><tr><td>应用层</td><td>允许访问OSI环境的手段(应用协议数据单元 APDU)</td><td>FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS</td></tr></tbody></table><h3 id="物理层"><a href="#物理层" class="headerlink" title="物理层"></a>物理层</h3><ul><li>传输数据的单位:比特</li><li>数据传输系统:源系统(源点、发送器) –> 传输系统 –> 目的系统(接收器、终点)</li></ul><p>通道:</p><ul><li>单向通道(单工通道):只有一个方向通信,没有反方向交互,如广播</li><li>双向交替通信(半双工通信):通信双方都可发消息,但不能同时发送或接收</li><li>双向同时通信(全双工通信):通信双方可以同时发送和接收信息</li></ul><p>通道复用技术:</p><ul><li>频分复用(FDM,Frequency Division Multiplexing):不同用户在不同频带,所用用户在同样时间占用不同带宽资源</li><li>时分复用(TDM,Time Division Multiplexing):不同用户在同一时间段的不同时间片,所有用户在不同时间占用同样的频带宽度</li><li>波分复用(WDM,Wavelength Division Multiplexing):光的频分复用</li><li>码分复用(CDM,Code Division Multiplexing):不同用户使用不同的码,可以在同样时间使用同样频带通信</li></ul><h3 id="数据链路层"><a href="#数据链路层" class="headerlink" title="数据链路层"></a>数据链路层</h3><p>主要信道:</p><ul><li>点对点信道</li><li>广播信道</li></ul><h4 id="点对点信道"><a href="#点对点信道" class="headerlink" title="点对点信道"></a>点对点信道</h4><ul><li>数据单元:帧</li></ul><p>三个基本问题:</p><ul><li>封装成帧:把网络层的 IP 数据报封装成帧,<code>SOH - 数据部分 - EOT</code></li><li>透明传输:不管数据部分什么字符,都能传输出去;可以通过字节填充方法解决(冲突字符前加转义字符)</li><li>差错检测:降低误码率(BER,Bit Error Rate),广泛使用循环冗余检测(CRC,Cyclic Redundancy Check)</li></ul><p>点对点协议(Point-to-Point Protocol):</p><ul><li>点对点协议(Point-to-Point Protocol):用户计算机和 ISP 通信时所使用的协议</li></ul><h4 id="广播信道"><a href="#广播信道" class="headerlink" title="广播信道"></a>广播信道</h4><p>广播通信:</p><ul><li>硬件地址(物理地址、MAC 地址)</li><li>单播(unicast)帧(一对一):收到的帧的 MAC 地址与本站的硬件地址相同</li><li>广播(broadcast)帧(一对全体):发送给本局域网上所有站点的帧</li><li>多播(multicast)帧(一对多):发送给本局域网上一部分站点的帧</li></ul><h3 id="网络层"><a href="#网络层" class="headerlink" title="网络层"></a>网络层</h3><ul><li>IP(Internet Protocol,网际协议)是为计算机网络相互连接进行通信而设计的协议。</li><li>ARP(Address Resolution Protocol,地址解析协议)</li><li>ICMP(Internet Control Message Protocol,网际控制报文协议)</li><li>IGMP(Internet Group Management Protocol,网际组管理协议)</li></ul><h4 id="IP-网际协议"><a href="#IP-网际协议" class="headerlink" title="IP 网际协议"></a>IP 网际协议</h4><p>IP 地址分类:</p><ul><li><code>IP 地址 ::= {<网络号>,<主机号>}</code></li></ul><table><thead><tr><th>IP 地址类别</th><th>网络号</th><th>网络范围</th><th>主机号</th><th>IP 地址范围</th></tr></thead><tbody><tr><td>A 类</td><td>8bit,第一位固定为 0</td><td>0 —— 127</td><td>24bit</td><td>1.0.0.0 —— 127.255.255.255</td></tr><tr><td>B 类</td><td>16bit,前两位固定为 10</td><td>128.0 —— 191.255</td><td>16bit</td><td>128.0.0.0 —— 191.255.255.255</td></tr><tr><td>C 类</td><td>24bit,前三位固定为 110</td><td>192.0.0 —— 223.255.255</td><td>8bit</td><td>192.0.0.0 —— 223.255.255.255</td></tr><tr><td>D 类</td><td>前四位固定为 1110,后面为多播地址</td><td></td><td></td><td></td></tr><tr><td>E 类</td><td>前五位固定为 11110,后面保留为今后所用</td><td></td><td></td><td></td></tr></tbody></table><p>IP 数据报格式:</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/IP%E6%95%B0%E6%8D%AE%E6%8A%A5%E6%A0%BC%E5%BC%8F.png" srcset="/img/loading.gif" alt="IP 数据报格式"></p><h4 id="ICMP-网际控制报文协议"><a href="#ICMP-网际控制报文协议" class="headerlink" title="ICMP 网际控制报文协议"></a>ICMP 网际控制报文协议</h4><p>ICMP 报文格式:</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/ICMP%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F.png" srcset="/img/loading.gif" alt="ICMP 报文格式"></p><p>应用:</p><ul><li>PING(Packet InterNet Groper,分组网间探测)测试两个主机之间的连通性</li><li>TTL(Time To Live,生存时间)该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量</li></ul><h4 id="内部网关协议"><a href="#内部网关协议" class="headerlink" title="内部网关协议"></a>内部网关协议</h4><ul><li>RIP(Routing Information Protocol,路由信息协议)</li><li>OSPF(Open Sortest Path First,开放最短路径优先)</li></ul><h4 id="外部网关协议"><a href="#外部网关协议" class="headerlink" title="外部网关协议"></a>外部网关协议</h4><ul><li>BGP(Border Gateway Protocol,边界网关协议)</li></ul><h4 id="IP多播"><a href="#IP多播" class="headerlink" title="IP多播"></a>IP多播</h4><ul><li>IGMP(Internet Group Management Protocol,网际组管理协议)</li><li>多播路由选择协议</li></ul><h4 id="VPN-和-NAT"><a href="#VPN-和-NAT" class="headerlink" title="VPN 和 NAT"></a>VPN 和 NAT</h4><ul><li>VPN(Virtual Private Network,虚拟专用网)</li><li>NAT(Network Address Translation,网络地址转换)</li></ul><h4 id="路由表包含什么?"><a href="#路由表包含什么?" class="headerlink" title="路由表包含什么?"></a>路由表包含什么?</h4><ol><li>网络 ID(Network ID, Network number):就是目标地址的网络 ID。</li><li>子网掩码(subnet mask):用来判断 IP 所属网络</li><li>下一跳地址/接口(Next hop / interface):就是数据在发送到目标地址的旅途中下一站的地址。其中 interface 指向 next hop(即为下一个 route)。一个自治系统(AS, Autonomous system)中的 route 应该包含区域内所有的子网络,而默认网关(Network id: <code>0.0.0.0</code>, Netmask: <code>0.0.0.0</code>)指向自治系统的出口。</li></ol><p>根据应用和执行的不同,路由表可能含有如下附加信息:</p><ol><li>花费(Cost):就是数据发送过程中通过路径所需要的花费。</li><li>路由的服务质量</li><li>路由中需要过滤的出/入连接列表</li></ol><h3 id="运输层"><a href="#运输层" class="headerlink" title="运输层"></a>运输层</h3><p>协议:</p><ul><li>TCP(Transmission Control Protocol,传输控制协议)</li><li>UDP(User Datagram Protocol,用户数据报协议)</li></ul><p>端口:</p><table><thead><tr><th>应用程序</th><th>FTP</th><th>TELNET</th><th>SMTP</th><th>DNS</th><th>TFTP</th><th>HTTP</th><th>HTTPS</th><th>SNMP</th></tr></thead><tbody><tr><td>端口号</td><td>21</td><td>23</td><td>25</td><td>53</td><td>69</td><td>80</td><td>443</td><td>161</td></tr></tbody></table><h4 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h4><ul><li>TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,其传输的单位是报文段。</li></ul><p>特征:</p><ul><li>面向连接</li><li>只能点对点(一对一)通信</li><li>可靠交互</li><li>全双工通信</li><li>面向字节流</li></ul><p>TCP 如何保证可靠传输:</p><ul><li>确认和超时重传</li><li>数据合理分片和排序</li><li>流量控制</li><li>拥塞控制</li><li>数据校验</li></ul><p>TCP 报文结构</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E6%8A%A5%E6%96%87.png" srcset="/img/loading.gif" alt="TCP 报文"></p><p>TCP 首部</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E9%A6%96%E9%83%A8.png" srcset="/img/loading.gif" alt="TCP 首部"></p><p>TCP:状态控制码(Code,Control Flag),占 6 比特,含义如下:</p><ul><li>URG:紧急比特(urgent),当 <code>URG=1</code> 时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。</li><li>ACK:确认比特(Acknowledge)。只有当 <code>ACK=1</code> 时确认号字段才有效,代表这个封包为确认封包。当 <code>ACK=0</code> 时,确认号无效。</li><li>PSH:(Push function)若为 1 时,代表要求对方立即传送缓冲区内的其他对应封包,而无需等缓冲满了才送。</li><li>RST:复位比特(Reset),当 <code>RST=1</code> 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。</li><li>SYN:同步比特(Synchronous),SYN 置为 1,就表示这是一个连接请求或连接接受报文,通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。</li><li>FIN:终止比特(Final),用来释放一个连接。当 <code>FIN=1</code> 时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。</li></ul><h4 id="UDP"><a href="#UDP" class="headerlink" title="UDP"></a>UDP</h4><ul><li>UDP(User Datagram Protocol,用户数据报协议)是 OSI(Open System Interconnection 开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,其传输的单位是用户数据报。</li></ul><p>特征:</p><ul><li>无连接</li><li>尽最大努力交付</li><li>面向报文</li><li>没有拥塞控制</li><li>支持一对一、一对多、多对一、多对多的交互通信</li><li>首部开销小</li></ul><p>UDP 报文结构</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/UDP%E6%8A%A5%E6%96%87.png" srcset="/img/loading.gif" alt="UDP 报文"></p><p>UDP 首部</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/UDP%E9%A6%96%E9%83%A8.png" srcset="/img/loading.gif" alt="UDP 首部"></p><blockquote><p>TCP/UDP 图片来源于:<a href="https://github.com/JerryC8080/understand-tcp-udp" target="_blank" rel="noopener">https://github.com/JerryC8080/understand-tcp-udp</a></p></blockquote><h4 id="TCP-与-UDP-的区别"><a href="#TCP-与-UDP-的区别" class="headerlink" title="TCP 与 UDP 的区别"></a>TCP 与 UDP 的区别</h4><ol><li>TCP 面向连接,UDP 是无连接的;</li><li>TCP 提供可靠的服务,也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付</li><li>TCP 的逻辑通信信道是全双工的可靠信道;UDP 则是不可靠信道</li><li>每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信</li><li>TCP 面向字节流(可能出现黏包问题),实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的(不会出现黏包问题)</li><li>UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等)</li><li>TCP 首部开销20字节;UDP 的首部开销小,只有 8 个字节</li></ol><h4 id="TCP-黏包问题"><a href="#TCP-黏包问题" class="headerlink" title="TCP 黏包问题"></a>TCP 黏包问题</h4><h5 id="原因-2"><a href="#原因-2" class="headerlink" title="原因"></a>原因</h5><p>TCP 是一个基于字节流的传输服务(UDP 基于报文的),“流” 意味着 TCP 所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况。</p><h5 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h5><ul><li>发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。</li><li>包头加上包体长度。包头是定长的 4 个字节,说明了包体的长度。接收对等方先接收包头长度,依据包头长度来接收包体。</li><li>在数据包之间设置边界,如添加特殊符号 <code>\r\n</code> 标记。FTP 协议正是这么做的。但问题在于如果数据正文中也含有 <code>\r\n</code>,则会误判为消息的边界。</li><li>使用更加复杂的应用层协议。</li></ul><h4 id="TCP-流量控制"><a href="#TCP-流量控制" class="headerlink" title="TCP 流量控制"></a>TCP 流量控制</h4><h5 id="概念-3"><a href="#概念-3" class="headerlink" title="概念"></a>概念</h5><p>流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。</p><h5 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h5><p>利用可变窗口进行流量控制</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/%E5%88%A9%E7%94%A8%E5%8F%AF%E5%8F%98%E7%AA%97%E5%8F%A3%E8%BF%9B%E8%A1%8C%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6%E4%B8%BE%E4%BE%8B.png" srcset="/img/loading.gif" alt=""></p><h4 id="TCP-拥塞控制"><a href="#TCP-拥塞控制" class="headerlink" title="TCP 拥塞控制"></a>TCP 拥塞控制</h4><h5 id="概念-4"><a href="#概念-4" class="headerlink" title="概念"></a>概念</h5><p>拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。</p><h5 id="方法-1"><a href="#方法-1" class="headerlink" title="方法"></a>方法</h5><ul><li>慢开始( slow-start )</li><li>拥塞避免( congestion avoidance )</li><li>快重传( fast retransmit )</li><li>快恢复( fast recovery )</li></ul><p>TCP的拥塞控制图</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E6%8B%A5%E5%A1%9E%E7%AA%97%E5%8F%A3cwnd%E5%9C%A8%E6%8B%A5%E5%A1%9E%E6%8E%A7%E5%88%B6%E6%97%B6%E7%9A%84%E5%8F%98%E5%8C%96%E6%83%85%E5%86%B5.png" srcset="/img/loading.gif" alt=""><br><img src="https://raw.githubusercontent.com/huihut/interview/master/images/%E5%BF%AB%E9%87%8D%E4%BC%A0%E7%A4%BA%E6%84%8F%E5%9B%BE.png" srcset="/img/loading.gif" alt=""><br><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E7%9A%84%E6%8B%A5%E5%A1%9E%E6%8E%A7%E5%88%B6%E6%B5%81%E7%A8%8B%E5%9B%BE.png" srcset="/img/loading.gif" alt=""></p><h4 id="TCP-传输连接管理"><a href="#TCP-传输连接管理" class="headerlink" title="TCP 传输连接管理"></a>TCP 传输连接管理</h4><blockquote><p>因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述:<a href="https://raw.githubusercontent.com/huihut/interview/master/images/TCP-transport-connection-management.png" target="_blank" rel="noopener">https://raw.githubusercontent.com/huihut/interview/master/images/TCP-transport-connection-management.png</a></p></blockquote><h5 id="TCP-三次握手建立连接"><a href="#TCP-三次握手建立连接" class="headerlink" title="TCP 三次握手建立连接"></a>TCP 三次握手建立连接</h5><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5.png" srcset="/img/loading.gif" alt="UDP 报文"></p><p>【TCP 建立连接全过程解释】</p><ol><li>客户端发送 SYN 给服务器,说明客户端请求建立连接;</li><li>服务端收到客户端发的 SYN,并回复 SYN+ACK 给客户端(同意建立连接);</li><li>客户端收到服务端的 SYN+ACK 后,回复 ACK 给服务端(表示客户端收到了服务端发的同意报文);</li><li>服务端收到客户端的 ACK,连接已建立,可以数据传输。</li></ol><h5 id="TCP-为什么要进行三次握手?"><a href="#TCP-为什么要进行三次握手?" class="headerlink" title="TCP 为什么要进行三次握手?"></a>TCP 为什么要进行三次握手?</h5><p>【答案一】因为信道不可靠,而 TCP 想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值。(而 UDP 则不需建立可靠传输,因此 UDP 不需要三次握手。)</p><blockquote><p><a href="https://groups.google.com/forum/#!msg/pongba/kF6O7-MFxM0/5S7zIJ4yqKUJ" target="_blank" rel="noopener">Google Groups . TCP 建立连接为什么是三次握手?{技术}{网络通信}</a></p></blockquote><p>【答案二】因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信。</p><blockquote><p><a href="https://www.zhihu.com/question/24853633/answer/115173386" target="_blank" rel="noopener">知乎 . TCP 为什么是三次握手,而不是两次或四次?</a></p></blockquote><p>【答案三】为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。</p><blockquote><p><a href="https://raw.githubusercontent.com/huihut/interview/master/images/TCP-transport-connection-management.png" target="_blank" rel="noopener">《计算机网络(第 7 版)-谢希仁》</a></p></blockquote><h5 id="TCP-四次挥手释放连接"><a href="#TCP-四次挥手释放连接" class="headerlink" title="TCP 四次挥手释放连接"></a>TCP 四次挥手释放连接</h5><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E9%87%8A%E6%94%BE%E8%BF%9E%E6%8E%A5.png" srcset="/img/loading.gif" alt="UDP 报文"></p><p>【TCP 释放连接全过程解释】</p><ol><li>客户端发送 FIN 给服务器,说明客户端不必发送数据给服务器了(请求释放从客户端到服务器的连接);</li><li>服务器接收到客户端发的 FIN,并回复 ACK 给客户端(同意释放从客户端到服务器的连接);</li><li>客户端收到服务端回复的 ACK,此时从客户端到服务器的连接已释放(但服务端到客户端的连接还未释放,并且客户端还可以接收数据);</li><li>服务端继续发送之前没发完的数据给客户端;</li><li>服务端发送 FIN+ACK 给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放);</li><li>客户端收到服务端的 FIN+ACK,并回复 ACK 给客户端(同意释放从服务端到客户端的连接);</li><li>服务端收到客户端的 ACK 后,释放从服务端到客户端的连接。</li></ol><h5 id="TCP-为什么要进行四次挥手?"><a href="#TCP-为什么要进行四次挥手?" class="headerlink" title="TCP 为什么要进行四次挥手?"></a>TCP 为什么要进行四次挥手?</h5><p>【问题一】TCP 为什么要进行四次挥手? / 为什么 TCP 建立连接需要三次,而释放连接则需要四次?</p><p>【答案一】因为 TCP 是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向客户端的连接关闭(三四次挥手)。所以 TCP 释放连接时服务器的 ACK 和 FIN 是分开发送的(中间隔着数据传输),而 TCP 建立连接时服务器的 ACK 和 SYN 是一起发送的(第二次握手),所以 TCP 建立连接需要三次,而释放连接则需要四次。</p><p>【问题二】为什么 TCP 连接时可以 ACK 和 SYN 一起发送,而释放时则 ACK 和 FIN 分开发送呢?(ACK 和 FIN 分开是指第二次和第三次挥手)</p><p>【答案二】因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端 FIN 请求(服务端发送 ACK),然后数据传输,传输完成后,服务端再提出 FIN 请求(服务端发送 FIN);而连接时则没有中间的数据传输,因此连接时可以 ACK 和 SYN 一起发送。</p><p>【问题三】为什么客户端释放最后需要 TIME-WAIT 等待 2MSL 呢?</p><p>【答案三】</p><ol><li>为了保证客户端发送的最后一个 ACK 报文能够到达服务端。若未成功到达,则服务端超时重传 FIN+ACK 报文段,客户端再重传 ACK,并重新计时。</li><li>防止已失效的连接请求报文段出现在本连接中。TIME-WAIT 持续 2MSL 可使本连接持续的时间内所产生的所有报文段都从网络中消失,这样可使下次连接中不会出现旧的连接报文段。</li></ol><h4 id="TCP-有限状态机"><a href="#TCP-有限状态机" class="headerlink" title="TCP 有限状态机"></a>TCP 有限状态机</h4><p>TCP 有限状态机图片</p><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/TCP%E7%9A%84%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA.png" srcset="/img/loading.gif" alt="TCP 的有限状态机"></p><h3 id="应用层"><a href="#应用层" class="headerlink" title="应用层"></a>应用层</h3><h4 id="DNS"><a href="#DNS" class="headerlink" title="DNS"></a>DNS</h4><ul><li>DNS(Domain Name System,域名系统)是互联网的一项服务。它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS 使用 TCP 和 UDP 端口 53。当前,对于每一级域名长度的限制是 63 个字符,域名总长度则不能超过 253 个字符。</li></ul><p>域名:</p><ul><li><code>域名 ::= {<三级域名>.<二级域名>.<顶级域名>}</code>,如:<code>blog.huihut.com</code></li></ul><h4 id="FTP"><a href="#FTP" class="headerlink" title="FTP"></a>FTP</h4><ul><li>FTP(File Transfer Protocol,文件传输协议)是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式,使用 TCP 数据报,提供交互式访问,双向传输。</li><li>TFTP(Trivial File Transfer Protocol,简单文件传输协议)一个小且易实现的文件传输协议,也使用客户-服务器方式,使用UDP数据报,只支持文件传输而不支持交互,没有列目录,不能对用户进行身份鉴定</li></ul><h4 id="TELNET"><a href="#TELNET" class="headerlink" title="TELNET"></a>TELNET</h4><ul><li><p>TELNET 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。</p></li><li><p>HTTP(HyperText Transfer Protocol,超文本传输协议)是用于从 WWW(World Wide Web,万维网)服务器传输超文本到本地浏览器的传送协议。</p></li><li><p>SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。</p></li><li><p>Socket 建立网络通信连接至少要一对端口号(Socket)。Socket 本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。</p></li></ul><h4 id="WWW"><a href="#WWW" class="headerlink" title="WWW"></a>WWW</h4><ul><li>WWW(World Wide Web,环球信息网,万维网)是一个由许多互相链接的超文本组成的系统,通过互联网访问</li></ul><h5 id="URL"><a href="#URL" class="headerlink" title="URL"></a>URL</h5><ul><li>URL(Uniform Resource Locator,统一资源定位符)是因特网上标准的资源的地址(Address)</li></ul><p>标准格式:</p><ul><li><code>协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]</code></li></ul><p>完整格式:</p><ul><li><code>协议类型:[//[访问资源需要的凭证信息@]服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]</code></li></ul><blockquote><p>其中【访问凭证信息@;:端口号;?查询;#片段ID】都属于选填项<br>如:<code>https://github.com/huihut/interview#cc</code></p></blockquote><h5 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h5><p>HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。</p><p>请求方法</p><table><thead><tr><th>方法</th><th>意义</th></tr></thead><tbody><tr><td>OPTIONS</td><td>请求一些选项信息,允许客户端查看服务器的性能</td></tr><tr><td>GET</td><td>请求指定的页面信息,并返回实体主体</td></tr><tr><td>HEAD</td><td>类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头</td></tr><tr><td>POST</td><td>向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改</td></tr><tr><td>PUT</td><td>从客户端向服务器传送的数据取代指定的文档的内容</td></tr><tr><td>DELETE</td><td>请求服务器删除指定的页面</td></tr><tr><td>TRACE</td><td>回显服务器收到的请求,主要用于测试或诊断</td></tr></tbody></table><p>状态码(Status-Code)</p><ul><li>1xx:表示通知信息,如请求收到了或正在进行处理<ul><li>100 Continue:继续,客户端应继续其请求</li><li>101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议</li></ul></li><li>2xx:表示成功,如接收或知道了<ul><li>200 OK: 请求成功</li></ul></li><li>3xx:表示重定向,如要完成请求还必须采取进一步的行动<ul><li>301 Moved Permanently: 永久移动。请求的资源已被永久的移动到新 URL,返回信息会包括新的 URL,浏览器会自动定向到新 URL。今后任何新的请求都应使用新的 URL 代替</li></ul></li><li>4xx:表示客户的差错,如请求中有错误的语法或不能完成<ul><li>400 Bad Request: 客户端请求的语法错误,服务器无法理解</li><li>401 Unauthorized: 请求要求用户的身份认证</li><li>403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求(权限不够)</li><li>404 Not Found: 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 “您所请求的资源无法找到” 的个性页面</li><li>408 Request Timeout: 服务器等待客户端发送的请求时间过长,超时</li></ul></li><li>5xx:表示服务器的差错,如服务器失效无法完成请求<ul><li>500 Internal Server Error: 服务器内部错误,无法完成请求</li><li>503 Service Unavailable: 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中</li><li>504 Gateway Timeout: 充当网关或代理的服务器,未及时从远端服务器获取请求</li></ul></li></ul><blockquote><p>更多状态码:<a href="http://www.runoob.com/http/http-status-codes.html" target="_blank" rel="noopener">菜鸟教程 . HTTP状态码</a></p></blockquote><h5 id="其他协议"><a href="#其他协议" class="headerlink" title="其他协议"></a>其他协议</h5><ul><li>SMTP(Simple Main Transfer Protocol,简单邮件传输协议)是在 Internet 传输 Email 的标准,是一个相对简单的基于文本的协议。在其之上指定了一条消息的一个或多个接收者(在大多数情况下被确认是存在的),然后消息文本会被传输。可以很简单地通过 Telnet 程序来测试一个 SMTP 服务器。SMTP 使用 TCP 端口 25。</li><li>DHCP(Dynamic Host Configuration Protocol,动态主机设置协议)是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途:<ul><li>用于内部网络或网络服务供应商自动分配 IP 地址给用户</li><li>用于内部网络管理员作为对所有电脑作中央管理的手段</li></ul></li><li>SNMP(Simple Network Management Protocol,简单网络管理协议)构成了互联网工程工作小组(IETF,Internet Engineering Task Force)定义的 Internet 协议族的一部分。该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。</li></ul><p><a id="network-programming"></a></p><h2 id="🌩-网络编程"><a href="#🌩-网络编程" class="headerlink" title="🌩 网络编程"></a>🌩 网络编程</h2><h3 id="Socket"><a href="#Socket" class="headerlink" title="Socket"></a>Socket</h3><blockquote><p><a href="https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html" target="_blank" rel="noopener">Linux Socket 编程(不限 Linux)</a></p></blockquote><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/socket%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%80%9A%E8%AE%AF.jpg" srcset="/img/loading.gif" alt="Socket 客户端服务器通讯"></p><h4 id="Socket-中的-read-、write-函数"><a href="#Socket-中的-read-、write-函数" class="headerlink" title="Socket 中的 read()、write() 函数"></a>Socket 中的 read()、write() 函数</h4><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">ssize_t</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-keyword">int</span> fd, <span class="hljs-keyword">void</span> *buf, <span class="hljs-keyword">size_t</span> count)</span></span>;<span class="hljs-function"><span class="hljs-keyword">ssize_t</span> <span class="hljs-title">write</span><span class="hljs-params">(<span class="hljs-keyword">int</span> fd, <span class="hljs-keyword">const</span> <span class="hljs-keyword">void</span> *buf, <span class="hljs-keyword">size_t</span> count)</span></span>;</code></pre><h5 id="read"><a href="#read" class="headerlink" title="read()"></a>read()</h5><ul><li>read 函数是负责从 fd 中读取内容。</li><li>当读成功时,read 返回实际所读的字节数。</li><li>如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。</li><li>如果错误为 EINTR 说明读是由中断引起的;如果是 ECONNREST 表示网络连接出了问题。</li></ul><h5 id="write"><a href="#write" class="headerlink" title="write()"></a>write()</h5><ul><li>write 函数将 buf 中的 nbytes 字节内容写入文件描述符 fd。</li><li>成功时返回写的字节数。失败时返回 -1,并设置 errno 变量。</li><li>在网络程序中,当我们向套接字文件描述符写时有俩种可能。</li><li>(1)write 的返回值大于 0,表示写了部分或者是全部的数据。</li><li>(2)返回的值小于 0,此时出现了错误。</li><li>如果错误为 EINTR 表示在写的时候出现了中断错误;如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。</li></ul><h4 id="Socket-中-TCP-的三次握手建立连接"><a href="#Socket-中-TCP-的三次握手建立连接" class="headerlink" title="Socket 中 TCP 的三次握手建立连接"></a>Socket 中 TCP 的三次握手建立连接</h4><p>我们知道 TCP 建立连接要进行 “三次握手”,即交换三个分组。大致流程如下:</p><ol><li>客户端向服务器发送一个 SYN J</li><li>服务器向客户端响应一个 SYN K,并对 SYN J 进行确认 ACK J+1</li><li>客户端再想服务器发一个确认 ACK K+1</li></ol><p>只有就完了三次握手,但是这个三次握手发生在 Socket 的那几个函数中呢?请看下图:</p><p><img src="http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png" srcset="/img/loading.gif" alt="socket 中发送的 TCP 三次握手"></p><p>从图中可以看出:</p><ol><li>当客户端调用 connect 时,触发了连接请求,向服务器发送了 SYN J 包,这时 connect 进入阻塞状态; </li><li>服务器监听到连接请求,即收到 SYN J 包,调用 accept 函数接收请求向客户端发送 SYN K ,ACK J+1,这时 accept 进入阻塞状态; </li><li>客户端收到服务器的 SYN K ,ACK J+1 之后,这时 connect 返回,并对 SYN K 进行确认; </li><li>服务器收到 ACK K+1 时,accept 返回,至此三次握手完毕,连接建立。</li></ol><h4 id="Socket-中-TCP-的四次握手释放连接"><a href="#Socket-中-TCP-的四次握手释放连接" class="headerlink" title="Socket 中 TCP 的四次握手释放连接"></a>Socket 中 TCP 的四次握手释放连接</h4><p>上面介绍了 socket 中 TCP 的三次握手建立过程,及其涉及的 socket 函数。现在我们介绍 socket 中的四次握手释放连接的过程,请看下图:</p><p><img src="http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png" srcset="/img/loading.gif" alt="socket 中发送的 TCP 四次握手"></p><p>图示过程如下:</p><ol><li>某个应用进程首先调用 close 主动关闭连接,这时 TCP 发送一个 FIN M;</li><li>另一端接收到 FIN M 之后,执行被动关闭,对这个 FIN 进行确认。它的接收也作为文件结束符传递给应用进程,因为 FIN 的接收意味着应用进程在相应的连接上再也接收不到额外数据;</li><li>一段时间之后,接收到文件结束符的应用进程调用 close 关闭它的 socket。这导致它的 TCP 也发送一个 FIN N;</li><li>接收到这个 FIN 的源发送端 TCP 对它进行确认。</li></ol><p>这样每个方向上都有一个 FIN 和 ACK。</p><p><a id="database"></a></p><h2 id="💾-数据库"><a href="#💾-数据库" class="headerlink" title="💾 数据库"></a>💾 数据库</h2><blockquote><p>本节部分知识点来自《数据库系统概论(第 5 版)》</p></blockquote><h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><ul><li>数据(data):描述事物的符号记录称为数据。</li><li>数据库(DataBase,DB):是长期存储在计算机内、有组织的、可共享的大量数据的集合,具有永久存储、有组织、可共享三个基本特点。</li><li>数据库管理系统(DataBase Management System,DBMS):是位于用户与操作系统之间的一层数据管理软件。</li><li>数据库系统(DataBase System,DBS):是有数据库、数据库管理系统(及其应用开发工具)、应用程序和数据库管理员(DataBase Administrator DBA)组成的存储、管理、处理和维护数据的系统。</li><li>实体(entity):客观存在并可相互区别的事物称为实体。</li><li>属性(attribute):实体所具有的某一特性称为属性。</li><li>码(key):唯一标识实体的属性集称为码。</li><li>实体型(entity type):用实体名及其属性名集合来抽象和刻画同类实体,称为实体型。</li><li>实体集(entity set):同一实体型的集合称为实体集。</li><li>联系(relationship):实体之间的联系通常是指不同实体集之间的联系。</li><li>模式(schema):模式也称逻辑模式,是数据库全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。</li><li>外模式(external schema):外模式也称子模式(subschema)或用户模式,它是数据库用户(包括应用程序员和最终用户)能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图,是与某一应用有关的数据的逻辑表示。</li><li>内模式(internal schema):内模式也称为存储模式(storage schema),一个数据库只有一个内模式。他是数据物理结构和存储方式的描述,是数据库在数据库内部的组织方式。</li></ul><h3 id="常用数据模型"><a href="#常用数据模型" class="headerlink" title="常用数据模型"></a>常用数据模型</h3><ul><li>层次模型(hierarchical model)</li><li>网状模型(network model)</li><li>关系模型(relational model)<ul><li>关系(relation):一个关系对应通常说的一张表</li><li>元组(tuple):表中的一行即为一个元组</li><li>属性(attribute):表中的一列即为一个属性</li><li>码(key):表中可以唯一确定一个元组的某个属性组</li><li>域(domain):一组具有相同数据类型的值的集合</li><li>分量:元组中的一个属性值</li><li>关系模式:对关系的描述,一般表示为 <code>关系名(属性1, 属性2, ..., 属性n)</code></li></ul></li><li>面向对象数据模型(object oriented data model)</li><li>对象关系数据模型(object relational data model)</li><li>半结构化数据模型(semistructure data model)</li></ul><h3 id="常用-SQL-操作"><a href="#常用-SQL-操作" class="headerlink" title="常用 SQL 操作"></a>常用 SQL 操作</h3><table> <tr> <th>对象类型</th> <th>对象</th> <th>操作类型</th> </tr> <tr> <td rowspan="4">数据库模式</td> <td>模式</td> <td><code>CREATE SCHEMA</code></td> </tr> <tr> <td>基本表</td> <td><code>CREATE SCHEMA</code>,<code>ALTER TABLE</code></td> </tr> <tr> <td>视图</td> <td><code>CREATE VIEW</code></td> </tr> <tr> <td>索引</td> <td><code>CREATE INDEX</code></td> </tr> <tr> <td rowspan="2">数据</td> <td>基本表和视图</td> <td><code>SELECT</code>,<code>INSERT</code>,<code>UPDATE</code>,<code>DELETE</code>,<code>REFERENCES</code>,<code>ALL PRIVILEGES</code></td> </tr> <tr> <td>属性列</td> <td><code>SELECT</code>,<code>INSERT</code>,<code>UPDATE</code>,<code>REFERENCES</code>,<code>ALL PRIVILEGES</code></td> </tr></table><blockquote><p>SQL 语法教程:<a href="http://www.runoob.com/sql/sql-tutorial.html" target="_blank" rel="noopener">runoob . SQL 教程</a></p></blockquote><h3 id="关系型数据库"><a href="#关系型数据库" class="headerlink" title="关系型数据库"></a>关系型数据库</h3><ul><li>基本关系操作:查询(选择、投影、连接(等值连接、自然连接、外连接(左外连接、右外连接))、除、并、差、交、笛卡尔积等)、插入、删除、修改</li><li>关系模型中的三类完整性约束:实体完整性、参照完整性、用户定义的完整性</li></ul><h4 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h4><ul><li>数据库索引:顺序索引、B+ 树索引、hash 索引</li><li><a href="http://blog.codinglabs.org/articles/theory-of-mysql-index.html" target="_blank" rel="noopener">MySQL 索引背后的数据结构及算法原理</a></li></ul><h3 id="数据库完整性"><a href="#数据库完整性" class="headerlink" title="数据库完整性"></a>数据库完整性</h3><ul><li>数据库的完整性是指数据的正确性和相容性。<ul><li>完整性:为了防止数据库中存在不符合语义(不正确)的数据。</li><li>安全性:为了保护数据库防止恶意破坏和非法存取。</li></ul></li><li>触发器:是用户定义在关系表中的一类由事件驱动的特殊过程。</li></ul><h3 id="关系数据理论"><a href="#关系数据理论" class="headerlink" title="关系数据理论"></a>关系数据理论</h3><ul><li>数据依赖是一个关系内部属性与属性之间的一种约束关系,是通过属性间值的相等与否体现出来的数据间相关联系。</li><li>最重要的数据依赖:函数依赖、多值依赖。</li></ul><h4 id="范式"><a href="#范式" class="headerlink" title="范式"></a>范式</h4><ul><li>第一范式(1NF):属性(字段)是最小单位不可再分。</li><li>第二范式(2NF):满足 1NF,每个非主属性完全依赖于主键(消除 1NF 非主属性对码的部分函数依赖)。</li><li>第三范式(3NF):满足 2NF,任何非主属性不依赖于其他非主属性(消除 2NF 非主属性对码的传递函数依赖)。</li><li>鲍依斯-科得范式(BCNF):满足 3NF,任何非主属性不能对主键子集依赖(消除 3NF 主属性对码的部分和传递函数依赖)。</li><li>第四范式(4NF):满足 3NF,属性之间不能有非平凡且非函数依赖的多值依赖(消除 3NF 非平凡且非函数依赖的多值依赖)。</li></ul><h3 id="数据库恢复"><a href="#数据库恢复" class="headerlink" title="数据库恢复"></a>数据库恢复</h3><ul><li>事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。</li><li>事物的 ACID 特性:原子性、一致性、隔离性、持续性。</li><li>恢复的实现技术:建立冗余数据 -> 利用冗余数据实施数据库恢复。</li><li>建立冗余数据常用技术:数据转储(动态海量转储、动态增量转储、静态海量转储、静态增量转储)、登记日志文件。</li></ul><h3 id="并发控制"><a href="#并发控制" class="headerlink" title="并发控制"></a>并发控制</h3><ul><li>事务是并发控制的基本单位。</li><li>并发操作带来的数据不一致性包括:丢失修改、不可重复读、读 “脏” 数据。</li><li>并发控制主要技术:封锁、时间戳、乐观控制法、多版本并发控制等。</li><li>基本封锁类型:排他锁(X 锁 / 写锁)、共享锁(S 锁 / 读锁)。</li><li>活锁死锁:<ul><li>活锁:事务永远处于等待状态,可通过先来先服务的策略避免。</li><li>死锁:事物永远不能结束<ul><li>预防:一次封锁法、顺序封锁法;</li><li>诊断:超时法、等待图法;</li><li>解除:撤销处理死锁代价最小的事务,并释放此事务的所有的锁,使其他事务得以继续运行下去。</li></ul></li></ul></li><li>可串行化调度:多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同。可串行性时并发事务正确调度的准则。</li></ul><p><a id="design-pattern"></a></p><h2 id="📏-设计模式"><a href="#📏-设计模式" class="headerlink" title="📏 设计模式"></a>📏 设计模式</h2><blockquote><p>各大设计模式例子参考:<a href="https://blog.csdn.net/liang19890820/article/details/66974516" target="_blank" rel="noopener">CSDN专栏 . C++ 设计模式</a> 系列博文</p></blockquote><p><a href="DesignPattern">设计模式工程目录</a></p><h3 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h3><p><a href="DesignPattern/SingletonPattern">单例模式例子</a></p><h3 id="抽象工厂模式"><a href="#抽象工厂模式" class="headerlink" title="抽象工厂模式"></a>抽象工厂模式</h3><p><a href="DesignPattern/AbstractFactoryPattern">抽象工厂模式例子</a></p><h3 id="适配器模式"><a href="#适配器模式" class="headerlink" title="适配器模式"></a>适配器模式</h3><p><a href="DesignPattern/AdapterPattern">适配器模式例子</a></p><h3 id="桥接模式"><a href="#桥接模式" class="headerlink" title="桥接模式"></a>桥接模式</h3><p><a href="DesignPattern/BridgePattern">桥接模式例子</a></p><h3 id="观察者模式"><a href="#观察者模式" class="headerlink" title="观察者模式"></a>观察者模式</h3><p><a href="DesignPattern/ObserverPattern">观察者模式例子</a></p><h3 id="设计模式的六大原则"><a href="#设计模式的六大原则" class="headerlink" title="设计模式的六大原则"></a>设计模式的六大原则</h3><ul><li>单一职责原则(SRP,Single Responsibility Principle)</li><li>里氏替换原则(LSP,Liskov Substitution Principle)</li><li>依赖倒置原则(DIP,Dependence Inversion Principle)</li><li>接口隔离原则(ISP,Interface Segregation Principle)</li><li>迪米特法则(LoD,Law of Demeter)</li><li>开放封闭原则(OCP,Open Close Principle)</li></ul><p><a id="link-loading-library"></a></p><h2 id="⚙️-链接装载库"><a href="#⚙️-链接装载库" class="headerlink" title="⚙️ 链接装载库"></a>⚙️ 链接装载库</h2><blockquote><p>本节部分知识点来自《程序员的自我修养——链接装载库》</p></blockquote><h3 id="内存、栈、堆"><a href="#内存、栈、堆" class="headerlink" title="内存、栈、堆"></a>内存、栈、堆</h3><p>一般应用程序内存空间有如下区域:</p><ul><li>栈:由操作系统自动分配释放,存放函数的参数值、局部变量等的值,用于维护函数调用的上下文</li><li>堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收,用来容纳应用程序动态分配的内存区域</li><li>可执行文件映像:存储着可执行文件在内存中的映像,由装载器装载是将可执行文件的内存读取或映射到这里</li><li>保留区:保留区并不是一个单一的内存区域,而是对内存中受到保护而禁止访问的内存区域的总称,如通常 C 语言讲无效指针赋值为 0(NULL),因此 0 地址正常情况下不可能有效的访问数据</li></ul><h4 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h4><p>栈保存了一个函数调用所需要的维护信息,常被称为堆栈帧(Stack Frame)或活动记录(Activate Record),一般包含以下几方面:</p><ul><li>函数的返回地址和参数</li><li>临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量</li><li>保存上下文:包括函数调用前后需要保持不变的寄存器</li></ul><h4 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h4><p>堆分配算法:</p><ul><li>空闲链表(Free List)</li><li>位图(Bitmap)</li><li>对象池</li></ul><h4 id="“段错误(segment-fault)”-或-“非法操作,该内存地址不能-read-write”"><a href="#“段错误(segment-fault)”-或-“非法操作,该内存地址不能-read-write”" class="headerlink" title="“段错误(segment fault)” 或 “非法操作,该内存地址不能 read/write”"></a>“段错误(segment fault)” 或 “非法操作,该内存地址不能 read/write”</h4><p>典型的非法指针解引用造成的错误。当指针指向一个不允许读写的内存地址,而程序却试图利用指针来读或写该地址时,会出现这个错误。</p><p>普遍原因:</p><ul><li>将指针初始化为 NULL,之后没有给它一个合理的值就开始使用指针</li><li>没用初始化栈中的指针,指针的值一般会是随机数,之后就直接开始使用指针</li></ul><h3 id="编译链接"><a href="#编译链接" class="headerlink" title="编译链接"></a>编译链接</h3><h4 id="各平台文件格式"><a href="#各平台文件格式" class="headerlink" title="各平台文件格式"></a>各平台文件格式</h4><table><thead><tr><th>平台</th><th>可执行文件</th><th>目标文件</th><th>动态库/共享对象</th><th>静态库</th></tr></thead><tbody><tr><td>Windows</td><td>exe</td><td>obj</td><td>dll</td><td>lib</td></tr><tr><td>Unix/Linux</td><td>ELF、out</td><td>o</td><td>so</td><td>a</td></tr><tr><td>Mac</td><td>Mach-O</td><td>o</td><td>dylib、tbd、framework</td><td>a、framework</td></tr></tbody></table><h4 id="编译链接过程"><a href="#编译链接过程" class="headerlink" title="编译链接过程"></a>编译链接过程</h4><ol><li>预编译(预编译器处理如 <code>#include</code>、<code>#define</code> 等预编译指令,生成 <code>.i</code> 或 <code>.ii</code> 文件)</li><li>编译(编译器进行词法分析、语法分析、语义分析、中间代码生成、目标代码生成、优化,生成 <code>.s</code> 文件)</li><li>汇编(汇编器把汇编码翻译成机器码,生成 <code>.o</code> 文件)</li><li>链接(连接器进行地址和空间分配、符号决议、重定位,生成 <code>.out</code> 文件)</li></ol><blockquote><p>现在版本 GCC 把预编译和编译合成一步,预编译编译程序 cc1、汇编器 as、连接器 ld</p></blockquote><blockquote><p>MSVC 编译环境,编译器 cl、连接器 link、可执行文件查看器 dumpbin</p></blockquote><h4 id="目标文件"><a href="#目标文件" class="headerlink" title="目标文件"></a>目标文件</h4><p>编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。</p><blockquote><p>可执行文件(Windows 的 <code>.exe</code> 和 Linux 的 <code>ELF</code>)、动态链接库(Windows 的 <code>.dll</code> 和 Linux 的 <code>.so</code>)、静态链接库(Windows 的 <code>.lib</code> 和 Linux 的 <code>.a</code>)都是按照可执行文件格式存储(Windows 按照 PE-COFF,Linux 按照 ELF)</p></blockquote><h5 id="目标文件格式"><a href="#目标文件格式" class="headerlink" title="目标文件格式"></a>目标文件格式</h5><ul><li>Windows 的 PE(Portable Executable),或称为 PE-COFF,<code>.obj</code> 格式</li><li>Linux 的 ELF(Executable Linkable Format),<code>.o</code> 格式</li><li>Intel/Microsoft 的 OMF(Object Module Format)</li><li>Unix 的 <code>a.out</code> 格式</li><li>MS-DOS 的 <code>.COM</code> 格式</li></ul><blockquote><p>PE 和 ELF 都是 COFF(Common File Format)的变种</p></blockquote><h5 id="目标文件存储结构"><a href="#目标文件存储结构" class="headerlink" title="目标文件存储结构"></a>目标文件存储结构</h5><table><thead><tr><th>段</th><th>功能</th></tr></thead><tbody><tr><td>File Header</td><td>文件头,描述整个文件的文件属性(包括文件是否可执行、是静态链接或动态连接及入口地址、目标硬件、目标操作系统等)</td></tr><tr><td>.text section</td><td>代码段,执行语句编译成的机器代码</td></tr><tr><td>.data section</td><td>数据段,已初始化的全局变量和局部静态变量</td></tr><tr><td>.bss section</td><td>BSS 段(Block Started by Symbol),未初始化的全局变量和局部静态变量(因为默认值为 0,所以只是在此预留位置,不占空间)</td></tr><tr><td>.rodata section</td><td>只读数据段,存放只读数据,一般是程序里面的只读变量(如 const 修饰的变量)和字符串常量</td></tr><tr><td>.comment section</td><td>注释信息段,存放编译器版本信息</td></tr><tr><td>.note.GNU-stack section</td><td>堆栈提示段</td></tr></tbody></table><blockquote><p>其他段略</p></blockquote><h4 id="链接的接口————符号"><a href="#链接的接口————符号" class="headerlink" title="链接的接口————符号"></a>链接的接口————符号</h4><p>在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。</p><p>如下符号表(Symbol Table):</p><table><thead><tr><th>Symbol(符号名)</th><th>Symbol Value (地址)</th></tr></thead><tbody><tr><td>main</td><td>0x100</td></tr><tr><td>Add</td><td>0x123</td></tr><tr><td>…</td><td>…</td></tr></tbody></table><h3 id="Linux-的共享库(Shared-Library)"><a href="#Linux-的共享库(Shared-Library)" class="headerlink" title="Linux 的共享库(Shared Library)"></a>Linux 的共享库(Shared Library)</h3><p>Linux 下的共享库就是普通的 ELF 共享对象。</p><p>共享库版本更新应该保证二进制接口 ABI(Application Binary Interface)的兼容</p><h4 id="命名"><a href="#命名" class="headerlink" title="命名"></a>命名</h4><p><code>libname.so.x.y.z</code></p><ul><li>x:主版本号,不同主版本号的库之间不兼容,需要重新编译</li><li>y:次版本号,高版本号向后兼容低版本号</li><li>z:发布版本号,不对接口进行更改,完全兼容</li></ul><h4 id="路径"><a href="#路径" class="headerlink" title="路径"></a>路径</h4><p>大部分包括 Linux 在内的开源系统遵循 FHS(File Hierarchy Standard)的标准,这标准规定了系统文件如何存放,包括各个目录结构、组织和作用。</p><ul><li><code>/lib</code>:存放系统最关键和最基础的共享库,如动态链接器、C 语言运行库、数学库等</li><li><code>/usr/lib</code>:存放非系统运行时所需要的关键性的库,主要是开发库</li><li><code>/usr/local/lib</code>:存放跟操作系统本身并不十分相关的库,主要是一些第三方应用程序的库</li></ul><blockquote><p>动态链接器会在 <code>/lib</code>、<code>/usr/lib</code> 和由 <code>/etc/ld.so.conf</code> 配置文件指定的,目录中查找共享库</p></blockquote><h4 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h4><ul><li><code>LD_LIBRARY_PATH</code>:临时改变某个应用程序的共享库查找路径,而不会影响其他应用程序</li><li><code>LD_PRELOAD</code>:指定预先装载的一些共享库甚至是目标文件</li><li><code>LD_DEBUG</code>:打开动态链接器的调试功能</li></ul><h4 id="so-共享库的编写"><a href="#so-共享库的编写" class="headerlink" title="so 共享库的编写"></a>so 共享库的编写</h4><p>使用 CLion 编写共享库</p><p>创建一个名为 MySharedLib 的共享库</p><p>CMakeLists.txt</p><pre><code class="hljs cmake"><span class="hljs-keyword">cmake_minimum_required</span>(VERSION <span class="hljs-number">3.10</span>)<span class="hljs-keyword">project</span>(MySharedLib)<span class="hljs-keyword">set</span>(CMAKE_CXX_STANDARD <span class="hljs-number">11</span>)<span class="hljs-keyword">add_library</span>(MySharedLib SHARED library.cpp library.h)</code></pre><p>library.h</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> MYSHAREDLIB_LIBRARY_H</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYSHAREDLIB_LIBRARY_H</span><span class="hljs-comment">// 打印 Hello World!</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span></span>;<span class="hljs-comment">// 使用可变模版参数求和</span><span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> T><span class="hljs-function">T <span class="hljs-title">sum</span><span class="hljs-params">(T t)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">return</span> t;}<span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> T, <span class="hljs-keyword">typename</span> ...Types><span class="hljs-function">T <span class="hljs-title">sum</span><span class="hljs-params">(T first, Types ... rest)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">return</span> first + sum<T>(rest...);}<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code></pre><p>library.cpp</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"library.h"</span></span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span> </span>{ <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> << <span class="hljs-string">"Hello, World!"</span> << <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;}</code></pre><h4 id="so-共享库的使用(被可执行项目调用)"><a href="#so-共享库的使用(被可执行项目调用)" class="headerlink" title="so 共享库的使用(被可执行项目调用)"></a>so 共享库的使用(被可执行项目调用)</h4><p>使用 CLion 调用共享库</p><p>创建一个名为 TestSharedLib 的可执行项目</p><p>CMakeLists.txt</p><pre><code class="hljs cmake"><span class="hljs-keyword">cmake_minimum_required</span>(VERSION <span class="hljs-number">3.10</span>)<span class="hljs-keyword">project</span>(TestSharedLib)<span class="hljs-comment"># C++11 编译</span><span class="hljs-keyword">set</span>(CMAKE_CXX_STANDARD <span class="hljs-number">11</span>)<span class="hljs-comment"># 头文件路径</span><span class="hljs-keyword">set</span>(INC_DIR /home/xx/code/clion/MySharedLib)<span class="hljs-comment"># 库文件路径</span><span class="hljs-keyword">set</span>(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug)<span class="hljs-keyword">include_directories</span>(<span class="hljs-variable">${INC_DIR}</span>)<span class="hljs-keyword">link_directories</span>(<span class="hljs-variable">${LIB_DIR}</span>)<span class="hljs-keyword">link_libraries</span>(MySharedLib)<span class="hljs-keyword">add_executable</span>(TestSharedLib main.cpp)<span class="hljs-comment"># 链接 MySharedLib 库</span><span class="hljs-keyword">target_link_libraries</span>(TestSharedLib MySharedLib)</code></pre><p>main.cpp</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"library.h"</span></span><span class="hljs-keyword">using</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span>;<span class="hljs-keyword">using</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{ hello(); <span class="hljs-built_in">cout</span> << <span class="hljs-string">"1 + 2 = "</span> << sum(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>) << <span class="hljs-built_in">endl</span>; <span class="hljs-built_in">cout</span> << <span class="hljs-string">"1 + 2 + 3 = "</span> << sum(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>) << <span class="hljs-built_in">endl</span>; <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><p>执行结果</p><pre><code class="hljs plain">Hello, World!1 + 2 = 31 + 2 + 3 = 6</code></pre><h3 id="Windows-应用程序入口函数"><a href="#Windows-应用程序入口函数" class="headerlink" title="Windows 应用程序入口函数"></a>Windows 应用程序入口函数</h3><ul><li>GUI(Graphical User Interface)应用,链接器选项:<code>/SUBSYSTEM:WINDOWS</code></li><li>CUI(Console User Interface)应用,链接器选项:<code>/SUBSYSTEM:CONSOLE</code></li></ul><p>_tWinMain 与 _tmain 函数声明</p><pre><code class="hljs cpp">Int WINAPI _tWinMain( HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, <span class="hljs-keyword">int</span> nCmdShow);<span class="hljs-keyword">int</span> _tmain( <span class="hljs-keyword">int</span> argc, TCHAR *argv[], TCHAR *envp[]);</code></pre><table><thead><tr><th>应用程序类型</th><th>入口点函数</th><th>嵌入可执行文件的启动函数</th></tr></thead><tbody><tr><td>处理ANSI字符(串)的GUI应用程序</td><td>_tWinMain(WinMain)</td><td>WinMainCRTSartup</td></tr><tr><td>处理Unicode字符(串)的GUI应用程序</td><td>_tWinMain(wWinMain)</td><td>wWinMainCRTSartup</td></tr><tr><td>处理ANSI字符(串)的CUI应用程序</td><td>_tmain(Main)</td><td>mainCRTSartup</td></tr><tr><td>处理Unicode字符(串)的CUI应用程序</td><td>_tmain(wMain)</td><td>wmainCRTSartup</td></tr><tr><td>动态链接库(Dynamic-Link Library)</td><td>DllMain</td><td>_DllMainCRTStartup</td></tr></tbody></table><h3 id="Windows-的动态链接库(Dynamic-Link-Library)"><a href="#Windows-的动态链接库(Dynamic-Link-Library)" class="headerlink" title="Windows 的动态链接库(Dynamic-Link Library)"></a>Windows 的动态链接库(Dynamic-Link Library)</h3><blockquote><p>部分知识点来自《Windows 核心编程(第五版)》</p></blockquote><h4 id="用处"><a href="#用处" class="headerlink" title="用处"></a>用处</h4><ul><li>扩展了应用程序的特性</li><li>简化了项目管理</li><li>有助于节省内存</li><li>促进了资源的共享</li><li>促进了本地化</li><li>有助于解决平台间的差异</li><li>可以用于特殊目的</li></ul><h4 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h4><ul><li>创建 DLL,事实上是在创建可供一个可执行模块调用的函数</li><li>当一个模块提供一个内存分配函数(malloc、new)的时候,它必须同时提供另一个内存释放函数(free、delete)</li><li>在使用 C 和 C++ 混编的时候,要使用 extern “C” 修饰符</li><li>一个 DLL 可以导出函数、变量(避免导出)、C++ 类(导出导入需要同编译器,否则避免导出)</li><li>DLL 模块:cpp 文件中的 __declspec(dllexport) 写在 include 头文件之前</li><li>调用 DLL 的可执行模块:cpp 文件的 __declspec(dllimport) 之前不应该定义 MYLIBAPI</li></ul><h4 id="加载-Windows-程序的搜索顺序"><a href="#加载-Windows-程序的搜索顺序" class="headerlink" title="加载 Windows 程序的搜索顺序"></a>加载 Windows 程序的搜索顺序</h4><ol><li>包含可执行文件的目录</li><li>Windows 的系统目录,可以通过 GetSystemDirectory 得到</li><li>16 位的系统目录,即 Windows 目录中的 System 子目录</li><li>Windows 目录,可以通过 GetWindowsDirectory 得到</li><li>进程的当前目录</li><li>PATH 环境变量中所列出的目录</li></ol><h4 id="DLL-入口函数"><a href="#DLL-入口函数" class="headerlink" title="DLL 入口函数"></a>DLL 入口函数</h4><p>DllMain 函数</p><pre><code class="hljs cpp"><span class="hljs-function">BOOL WINAPI <span class="hljs-title">DllMain</span><span class="hljs-params">(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)</span></span><span class="hljs-function"></span>{ <span class="hljs-keyword">switch</span>(fdwReason) { <span class="hljs-keyword">case</span> DLL_PROCESS_ATTACH: <span class="hljs-comment">// 第一次将一个DLL映射到进程地址空间时调用</span> <span class="hljs-comment">// The DLL is being mapped into the process' address space.</span> <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> DLL_THREAD_ATTACH: <span class="hljs-comment">// 当进程创建一个线程的时候,用于告诉DLL执行与线程相关的初始化(非主线程执行)</span> <span class="hljs-comment">// A thread is bing created.</span> <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> DLL_THREAD_DETACH: <span class="hljs-comment">// 系统调用 ExitThread 线程退出前,即将终止的线程通过告诉DLL执行与线程相关的清理</span> <span class="hljs-comment">// A thread is exiting cleanly.</span> <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> DLL_PROCESS_DETACH: <span class="hljs-comment">// 将一个DLL从进程的地址空间时调用</span> <span class="hljs-comment">// The DLL is being unmapped from the process' address space.</span> <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">return</span> (TRUE); <span class="hljs-comment">// Used only for DLL_PROCESS_ATTACH</span>}</code></pre><h4 id="载入卸载库"><a href="#载入卸载库" class="headerlink" title="载入卸载库"></a>载入卸载库</h4><p>LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread 函数声明</p><pre><code class="hljs cpp"><span class="hljs-comment">// 载入库</span><span class="hljs-function">HMODULE WINAPI <span class="hljs-title">LoadLibrary</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> _In_ LPCTSTR lpFileName</span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;<span class="hljs-function">HMODULE <span class="hljs-title">LoadLibraryExA</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> LPCSTR lpLibFileName,</span></span><span class="hljs-function"><span class="hljs-params"> HANDLE hFile,</span></span><span class="hljs-function"><span class="hljs-params"> DWORD dwFlags</span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;<span class="hljs-comment">// 若要在通用 Windows 平台(UWP)应用中加载 Win32 DLL,需要调用 LoadPackagedLibrary,而不是 LoadLibrary 或 LoadLibraryEx</span><span class="hljs-function">HMODULE <span class="hljs-title">LoadPackagedLibrary</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> LPCWSTR lpwLibFileName,</span></span><span class="hljs-function"><span class="hljs-params"> DWORD Reserved</span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;<span class="hljs-comment">// 卸载库</span><span class="hljs-function">BOOL WINAPI <span class="hljs-title">FreeLibrary</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> _In_ HMODULE hModule</span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;<span class="hljs-comment">// 卸载库和退出线程</span><span class="hljs-function">VOID WINAPI <span class="hljs-title">FreeLibraryAndExitThread</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> _In_ HMODULE hModule,</span></span><span class="hljs-function"><span class="hljs-params"> _In_ DWORD dwExitCode</span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;</code></pre><h4 id="显示地链接到导出符号"><a href="#显示地链接到导出符号" class="headerlink" title="显示地链接到导出符号"></a>显示地链接到导出符号</h4><p>GetProcAddress 函数声明</p><pre><code class="hljs cpp"><span class="hljs-function">FARPROC <span class="hljs-title">GetProcAddress</span><span class="hljs-params">(</span></span><span class="hljs-function"><span class="hljs-params"> HMODULE hInstDll,</span></span><span class="hljs-function"><span class="hljs-params"> PCSTR pszSymbolName <span class="hljs-comment">// 只能接受 ANSI 字符串,不能是 Unicode</span></span></span><span class="hljs-function"><span class="hljs-params">)</span></span>;</code></pre><h4 id="DumpBin-exe-查看-DLL-信息"><a href="#DumpBin-exe-查看-DLL-信息" class="headerlink" title="DumpBin.exe 查看 DLL 信息"></a>DumpBin.exe 查看 DLL 信息</h4><p>在 <code>VS 的开发人员命令提示符</code> 使用 <code>DumpBin.exe</code> 可查看 DLL 库的导出段(导出的变量、函数、类名的符号)、相对虚拟地址(RVA,relative virtual address)。如:</p><pre><code class="hljs plain">DUMPBIN -exports D:\mydll.dll</code></pre><h4 id="LoadLibrary-与-FreeLibrary-流程图"><a href="#LoadLibrary-与-FreeLibrary-流程图" class="headerlink" title="LoadLibrary 与 FreeLibrary 流程图"></a>LoadLibrary 与 FreeLibrary 流程图</h4><p>LoadLibrary 与 FreeLibrary 流程图</p><h5 id="LoadLibrary"><a href="#LoadLibrary" class="headerlink" title="LoadLibrary"></a>LoadLibrary</h5><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/WindowsLoadLibrary.png" srcset="/img/loading.gif" alt="WindowsLoadLibrary"></p><h5 id="FreeLibrary"><a href="#FreeLibrary" class="headerlink" title="FreeLibrary"></a>FreeLibrary</h5><p><img src="https://raw.githubusercontent.com/huihut/interview/master/images/WindowsFreeLibrary.png" srcset="/img/loading.gif" alt="WindowsFreeLibrary"></p><h4 id="DLL-库的编写(导出一个-DLL-模块)"><a href="#DLL-库的编写(导出一个-DLL-模块)" class="headerlink" title="DLL 库的编写(导出一个 DLL 模块)"></a>DLL 库的编写(导出一个 DLL 模块)</h4><p>DLL 库的编写(导出一个 DLL 模块)<br>DLL 头文件</p><pre><code class="hljs cpp"><span class="hljs-comment">// MyLib.h</span><span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> MYLIBAPI</span><span class="hljs-comment">// MYLIBAPI 应该在全部 DLL 源文件的 include "Mylib.h" 之前被定义</span><span class="hljs-comment">// 全部函数/变量正在被导出</span><span class="hljs-meta">#<span class="hljs-meta-keyword">else</span></span><span class="hljs-comment">// 这个头文件被一个exe源代码模块包含,意味着全部函数/变量被导入</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYLIBAPI extern <span class="hljs-meta-string">"C"</span> __declspec(dllimport)</span><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span><span class="hljs-comment">// 这里定义任何的数据结构和符号</span><span class="hljs-comment">// 定义导出的变量(避免导出变量)</span>MYLIBAPI <span class="hljs-keyword">int</span> g_nResult;<span class="hljs-comment">// 定义导出函数原型</span><span class="hljs-function">MYLIBAPI <span class="hljs-keyword">int</span> <span class="hljs-title">Add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> nLeft, <span class="hljs-keyword">int</span> nRight)</span></span>;</code></pre><p>DLL 源文件</p><pre><code class="hljs cpp"><span class="hljs-comment">// MyLibFile1.cpp</span><span class="hljs-comment">// 包含标准Windows和C运行时头文件</span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><windows.h></span></span><span class="hljs-comment">// DLL源码文件导出的函数和变量</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYLIBAPI extern <span class="hljs-meta-string">"C"</span> __declspec(dllexport)</span><span class="hljs-comment">// 包含导出的数据结构、符号、函数、变量</span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"MyLib.h"</span></span><span class="hljs-comment">// 将此DLL源代码文件的代码放在此处</span><span class="hljs-keyword">int</span> g_nResult;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> nLeft, <span class="hljs-keyword">int</span> nRight)</span></span><span class="hljs-function"></span>{ g_nResult = nLeft + nRight; <span class="hljs-keyword">return</span> g_nResult;}</code></pre><h4 id="DLL-库的使用(运行时动态链接-DLL)"><a href="#DLL-库的使用(运行时动态链接-DLL)" class="headerlink" title="DLL 库的使用(运行时动态链接 DLL)"></a>DLL 库的使用(运行时动态链接 DLL)</h4><p>DLL 库的使用(运行时动态链接 DLL)</p><pre><code class="hljs cpp"><span class="hljs-comment">// A simple program that uses LoadLibrary and </span><span class="hljs-comment">// GetProcAddress to access myPuts from Myputs.dll. </span> <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><windows.h> </span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><stdio.h> </span></span> <span class="hljs-function"><span class="hljs-keyword">typedef</span> <span class="hljs-title">int</span> <span class="hljs-params">(__cdecl *MYPROC)</span><span class="hljs-params">(LPWSTR)</span></span>; <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">( <span class="hljs-keyword">void</span> )</span> </span><span class="hljs-function"></span>{ HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; <span class="hljs-comment">// Get a handle to the DLL module.</span> hinstLib = LoadLibrary(TEXT(<span class="hljs-string">"MyPuts.dll"</span>)); <span class="hljs-comment">// If the handle is valid, try to get the function address.</span> <span class="hljs-keyword">if</span> (hinstLib != <span class="hljs-literal">NULL</span>) { ProcAdd = (MYPROC) GetProcAddress(hinstLib, <span class="hljs-string">"myPuts"</span>); <span class="hljs-comment">// If the function address is valid, call the function.</span> <span class="hljs-keyword">if</span> (<span class="hljs-literal">NULL</span> != ProcAdd) { fRunTimeLinkSuccess = TRUE; (ProcAdd) (<span class="hljs-string">L"Message sent to the DLL function\n"</span>); } <span class="hljs-comment">// Free the DLL module.</span> fFreeResult = FreeLibrary(hinstLib); } <span class="hljs-comment">// If unable to call the DLL function, use an alternative.</span> <span class="hljs-keyword">if</span> (! fRunTimeLinkSuccess) <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Message printed from executable\n"</span>); <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;}</code></pre><h3 id="运行库(Runtime-Library)"><a href="#运行库(Runtime-Library)" class="headerlink" title="运行库(Runtime Library)"></a>运行库(Runtime Library)</h3><h4 id="典型程序运行步骤"><a href="#典型程序运行步骤" class="headerlink" title="典型程序运行步骤"></a>典型程序运行步骤</h4><ol><li>操作系统创建进程,把控制权交给程序的入口(往往是运行库中的某个入口函数)</li><li>入口函数对运行库和程序运行环境进行初始化(包括堆、I/O、线程、全局变量构造等等)。</li><li>入口函数初始化后,调用 main 函数,正式开始执行程序主体部分。</li><li>main 函数执行完毕后,返回到入口函数进行清理工作(包括全局变量析构、堆销毁、关闭I/O等),然后进行系统调用结束进程。</li></ol><blockquote><p>一个程序的 I/O 指代程序与外界的交互,包括文件、管程、网络、命令行、信号等。更广义地讲,I/O 指代操作系统理解为 “文件” 的事物。</p></blockquote><h4 id="glibc-入口"><a href="#glibc-入口" class="headerlink" title="glibc 入口"></a>glibc 入口</h4><p><code>_start -> __libc_start_main -> exit -> _exit</code></p><p>其中 <code>main(argc, argv, __environ)</code> 函数在 <code>__libc_start_main</code> 里执行。</p><h4 id="MSVC-CRT-入口"><a href="#MSVC-CRT-入口" class="headerlink" title="MSVC CRT 入口"></a>MSVC CRT 入口</h4><p><code>int mainCRTStartup(void)</code></p><p>执行如下操作:</p><ol><li>初始化和 OS 版本有关的全局变量。</li><li>初始化堆。</li><li>初始化 I/O。</li><li>获取命令行参数和环境变量。</li><li>初始化 C 库的一些数据。</li><li>调用 main 并记录返回值。</li><li>检查错误并将 main 的返回值返回。</li></ol><h4 id="C-语言运行库(CRT)"><a href="#C-语言运行库(CRT)" class="headerlink" title="C 语言运行库(CRT)"></a>C 语言运行库(CRT)</h4><p>大致包含如下功能:</p><ul><li>启动与退出:包括入口函数及入口函数所依赖的其他函数等。</li><li>标准函数:有 C 语言标准规定的C语言标准库所拥有的函数实现。</li><li>I/O:I/O 功能的封装和实现。</li><li>堆:堆的封装和实现。</li><li>语言实现:语言中一些特殊功能的实现。</li><li>调试:实现调试功能的代码。</li></ul><h4 id="C语言标准库(ANSI-C)"><a href="#C语言标准库(ANSI-C)" class="headerlink" title="C语言标准库(ANSI C)"></a>C语言标准库(ANSI C)</h4><p>包含:</p><ul><li>标准输入输出(stdio.h)</li><li>文件操作(stdio.h)</li><li>字符操作(ctype.h)</li><li>字符串操作(string.h)</li><li>数学函数(math.h)</li><li>资源管理(stdlib.h)</li><li>格式转换(stdlib.h)</li><li>时间/日期(time.h)</li><li>断言(assert.h)</li><li>各种类型上的常数(limits.h & float.h)</li><li>变长参数(stdarg.h)</li><li>非局部跳转(setjmp.h)</li></ul><p><a id="books"></a></p><h2 id="📚-书籍"><a href="#📚-书籍" class="headerlink" title="📚 书籍"></a>📚 书籍</h2><blockquote><p><a href="https://github.com/huihut/CS-Books" target="_blank" rel="noopener">huihut/CS-Books</a>:📚 Computer Science Books 计算机技术类书籍 PDF</p></blockquote><h3 id="语言"><a href="#语言" class="headerlink" title="语言"></a>语言</h3><ul><li>《C++ Primer》</li><li>《Effective C++》</li><li>《More Effective C++》</li><li>《深度探索 C++ 对象模型》</li><li>《深入理解 C++11》</li><li>《STL 源码剖析》</li></ul><h3 id="算法-1"><a href="#算法-1" class="headerlink" title="算法"></a>算法</h3><ul><li>《剑指 Offer》</li><li>《编程珠玑》</li><li>《程序员面试宝典》</li></ul><h3 id="系统"><a href="#系统" class="headerlink" title="系统"></a>系统</h3><ul><li>《深入理解计算机系统》</li><li>《Windows 核心编程》</li><li>《Unix 环境高级编程》</li></ul><h3 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h3><ul><li>《Unix 网络编程》</li><li>《TCP/IP 详解》</li></ul><h3 id="其他-1"><a href="#其他-1" class="headerlink" title="其他"></a>其他</h3><ul><li>《程序员的自我修养》</li></ul><p><a id="cc-development-direction"></a></p><h2 id="🔱-C-C-发展方向"><a href="#🔱-C-C-发展方向" class="headerlink" title="🔱 C/C++ 发展方向"></a>🔱 C/C++ 发展方向</h2><blockquote><p>C/C++ 发展方向甚广,包括不限于以下方向, 以下列举一些大厂校招岗位要求。</p></blockquote><h3 id="后台-服务器"><a href="#后台-服务器" class="headerlink" title="后台/服务器"></a>后台/服务器</h3><p>【后台开发】</p><ul><li>编程基本功扎实,掌握 C/C++/JAVA 等开发语言、常用算法和数据结构;</li><li>熟悉 TCP/UDP 网络协议及相关编程、进程间通讯编程;</li><li>了解 Python、Shell、Perl 等脚本语言;</li><li>了解 MYSQL 及 SQL 语言、编程,了解 NoSQL, key-value 存储原理;</li><li>全面、扎实的软件知识结构,掌握操作系统、软件工程、设计模式、数据结构、数据库系统、网络安全等专业知识;</li><li>了解分布式系统设计与开发、负载均衡技术,系统容灾设计,高可用系统等知识。</li></ul><h3 id="桌面客户端"><a href="#桌面客户端" class="headerlink" title="桌面客户端"></a>桌面客户端</h3><p>【PC 客户端开发】</p><ul><li>计算机软件相关专业本科或以上学历,热爱编程,基础扎实,理解算法和数据结构相关知识; </li><li>熟悉 windows 操作系统的内存管理、文件系统、进程线程调度; </li><li>熟悉 MFC/windows 界面实现机制,熟练使用 VC,精通 C/C++,熟练使用 STL,以及 Windows 下网络编程经验;</li><li>熟练掌握 Windows 客户端开发、调试,有 Windows 应用软件开发经验优先;</li><li>对于创新及解决具有挑战性的问题充满激情,具有良好的算法基础及系统分析能力。</li></ul><h3 id="图形学-游戏-VR-AR"><a href="#图形学-游戏-VR-AR" class="headerlink" title="图形学/游戏/VR/AR"></a>图形学/游戏/VR/AR</h3><p>【游戏客户端开发】</p><ul><li>计算机科学/工程相关专业本科或以上学历,热爱编程,基础扎实,理解算法、数据结构、软件设计相关知识;</li><li>至少掌握一种游戏开发常用的编程语言,具 C++/C# 编程经验优先;</li><li>具游戏引擎(如 Unity、Unreal)使用经验者优先;</li><li>了解某方面的游戏客户端技术(如图形、音频、动画、物理、人工智能、网络同步)者优先考虑;</li><li>对于创新及解决具有挑战性的问题充满激情,有较强的学习能力、分析及解决问题能力,具备良好的团队合作意识;</li><li>具阅读英文技术文档能力;</li><li>热爱游戏。</li></ul><h3 id="测试开发"><a href="#测试开发" class="headerlink" title="测试开发"></a>测试开发</h3><p>【测试开发】</p><ul><li>计算机或相关专业本科及以上学历;</li><li>一至两年的 C/C++/Python 或其他计算机语言的编程经验;</li><li>具备撰写测试计划、测试用例、以及实现性能和安全等测试的能力;</li><li>具备实现自动化系统的能力;</li><li>具备定位调查产品缺陷能力、以及代码级别调试缺陷的能力;</li><li>工作主动积极,有责任心,具有良好的团队合作精神。</li></ul><h3 id="网络安全-逆向"><a href="#网络安全-逆向" class="headerlink" title="网络安全/逆向"></a>网络安全/逆向</h3><p>【安全技术】</p><ul><li>热爱互联网,对操作系统和网络安全有狂热的追求,专业不限;</li><li>熟悉漏洞挖掘、网络安全攻防技术,了解常见黑客攻击手法; </li><li>掌握基本开发能力,熟练使用 C/C++ 语言;</li><li>对数据库、操作系统、网络原理有较好掌握; </li><li>具有软件逆向,网络安全攻防或安全系统开发经验者优先。</li></ul><h3 id="嵌入式-物联网"><a href="#嵌入式-物联网" class="headerlink" title="嵌入式/物联网"></a>嵌入式/物联网</h3><p>【嵌入式应用开发】</p><ul><li>有良好的编程基础,熟练掌握 C/C++ 语言;</li><li>掌握操作系统、数据结构等软件开发必备知识;</li><li>具备较强的沟通理解能力及良好的团队合作意识;</li><li>有 Linux/Android 系统平台的开发经验者优先。</li></ul><h3 id="音视频-流媒体-SDK"><a href="#音视频-流媒体-SDK" class="headerlink" title="音视频/流媒体/SDK"></a>音视频/流媒体/SDK</h3><p>【音视频编解码】</p><ol><li>硕士及以上学历,计算机、信号处理、数学、信息类及相关专业和方向; </li><li>视频编解码基础扎实,熟常用的 HEVC 或 H264,有较好的数字信号处理基础; </li><li>掌握 C/C++,代码能力强, 熟悉一种汇编语言尤佳; </li><li>较强的英文文献阅读能力; </li><li>学习能力强,具有团队协作精神,有较强的抗压能力。</li></ol><h3 id="计算机视觉-机器学习"><a href="#计算机视觉-机器学习" class="headerlink" title="计算机视觉/机器学习"></a>计算机视觉/机器学习</h3><p>【计算机视觉研究】</p><ul><li>计算机、应用数学、模式识别、人工智能、自控、统计学、运筹学、生物信息、物理学/量子计算、神经科学、社会学/心理学等专业,图像处理、模式识别、机器学习相关研究方向,本科及以上,博士优先;</li><li>熟练掌握计算机视觉和图像处理相关的基本算法及应用;</li><li>较强的算法实现能力,熟练掌握 C/C++ 编程,熟悉 Shell/Python/Matlab 至少一种编程语言;</li><li>在计算机视觉、模式识别等学术会议或者期刊上发表论文、相关国际比赛获奖、及有相关专利者优先。</li></ul><p><a id="review-of-brush-questions-website"></a></p><h2 id="💯-复习刷题网站"><a href="#💯-复习刷题网站" class="headerlink" title="💯 复习刷题网站"></a>💯 复习刷题网站</h2><ul><li><a href="http://www.cplusplus.com/" target="_blank" rel="noopener">cplusplus</a></li><li><a href="https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5" target="_blank" rel="noopener">cppreference</a></li><li><a href="http://www.runoob.com/cplusplus/cpp-tutorial.html" target="_blank" rel="noopener">runoob</a></li><li><a href="https://leetcode.com/" target="_blank" rel="noopener">leetcode</a> | <a href="https://leetcode-cn.com/" target="_blank" rel="noopener">leetcode-cn</a></li><li><a href="https://www.lintcode.com/" target="_blank" rel="noopener">lintcode</a></li><li><a href="https://www.nowcoder.net/" target="_blank" rel="noopener">nowcoder</a></li></ul><p><a id="interview-questions-experience"></a></p><h2 id="📝-面试题目经验"><a href="#📝-面试题目经验" class="headerlink" title="📝 面试题目经验"></a>📝 面试题目经验</h2><ul><li><a href="https://www.nowcoder.com/discuss/205497" target="_blank" rel="noopener">牛客网 . 2020秋招面经大汇总!(岗位划分)</a></li><li><a href="https://www.nowcoder.com/discuss/197116" target="_blank" rel="noopener">牛客网 . 【备战秋招】2020届秋招备战攻略</a></li><li><a href="https://www.nowcoder.com/discuss/90907" target="_blank" rel="noopener">牛客网 . 2019校招面经大汇总!【每日更新中】</a></li><li><a href="https://www.nowcoder.com/discuss/146655" target="_blank" rel="noopener">牛客网 . 2019校招技术类岗位面经汇总【技术类】</a></li><li><a href="https://www.nowcoder.com/discuss/68802" target="_blank" rel="noopener">牛客网 . 2018校招笔试真题汇总</a></li><li><a href="https://www.nowcoder.com/discuss/12805" target="_blank" rel="noopener">牛客网 . 2017秋季校园招聘笔经面经专题汇总</a></li><li><a href="https://www.nowcoder.com/discuss/25268" target="_blank" rel="noopener">牛客网 . 史上最全2017春招面经大合集!!</a></li><li><a href="https://www.nowcoder.com/discuss/57978" target="_blank" rel="noopener">牛客网 . 面试题干货在此</a></li><li><a href="https://www.zhihu.com/question/29693016" target="_blank" rel="noopener">知乎 . 互联网求职路上,你见过哪些写得很好、很用心的面经?最好能分享自己的面经、心路历程。</a></li><li><a href="https://www.zhihu.com/question/24964987" target="_blank" rel="noopener">知乎 . 互联网公司最常见的面试算法题有哪些?</a></li><li><a href="http://blog.csdn.net/ljzcome/article/details/574158" target="_blank" rel="noopener">CSDN . 全面整理的C++面试题</a></li><li><a href="http://blog.csdn.net/Xiongchao99/article/details/74524807?locationNum=6&fps=1" target="_blank" rel="noopener">CSDN . 百度研发类面试题(C++方向)</a></li><li><a href="http://blog.csdn.net/fakine/article/details/51321544" target="_blank" rel="noopener">CSDN . c++常见面试题30道</a></li><li><a href="http://blog.csdn.net/onever_say_love/article/details/51223886" target="_blank" rel="noopener">CSDN . 腾讯2016实习生面试经验(已经拿到offer)</a></li><li><a href="https://www.cnblogs.com/Y1Focus/p/6707121.html" target="_blank" rel="noopener">cnblogs . C++面试集锦( 面试被问到的问题 )</a></li><li><a href="https://www.cnblogs.com/fangyukuan/archive/2010/09/18/1829871.html" target="_blank" rel="noopener">cnblogs . C/C++ 笔试、面试题目大汇总</a></li><li><a href="https://www.cnblogs.com/LUO77/p/5771237.html" target="_blank" rel="noopener">cnblogs . 常见C++面试题及基本知识点总结(一)</a></li><li><a href="https://segmentfault.com/a/1190000003745529" target="_blank" rel="noopener">segmentfault . C++常见面试问题总结</a></li></ul><p><a id="recruitment-time-post"></a></p><h2 id="📆-招聘时间岗位"><a href="#📆-招聘时间岗位" class="headerlink" title="📆 招聘时间岗位"></a>📆 招聘时间岗位</h2><ul><li><a href="https://www.nowcoder.com/school/schedule" target="_blank" rel="noopener">牛客网 . 2020届校招 | 2020 IT名企校招日程</a></li></ul><p><a id="recommend"></a></p><h2 id="👍-内推"><a href="#👍-内推" class="headerlink" title="👍 内推"></a>👍 内推</h2><ul><li><a href="https://github.com/CyC2018/Job-Recommend" target="_blank" rel="noopener">Github . CyC2018/Job-Recommend</a>:🔎 互联网内推信息(社招、校招、实习)</li><li><a href="https://github.com/amusi/AI-Job-Recommend" target="_blank" rel="noopener">Github . amusi/AI-Job-Recommend</a>:国内公司人工智能方向(含机器学习、深度学习、计算机视觉和自然语言处理)岗位的招聘信息(含全职、实习和校招)</li></ul><p><a id="contributor"></a></p><h2 id="👬-贡献者"><a href="#👬-贡献者" class="headerlink" title="👬 贡献者"></a>👬 贡献者</h2><p><a href="https://github.com/huihut/interview/graphs/contributors" target="_blank" rel="noopener"><img src="https://opencollective.com/interview/contributors.svg?button=false" srcset="/img/loading.gif" /></a></p><p><a id="support-sponsor"></a></p><h2 id="🍭-支持赞助"><a href="#🍭-支持赞助" class="headerlink" title="🍭 支持赞助"></a>🍭 支持赞助</h2><p><strong><a href="https://store.steampowered.com/app/1137770/Avalive/" target="_blank" rel="noopener">Avalive</a></strong>:一个面部捕捉的虚拟形象扮演软件。</p></div>]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>提问的智慧</title>
<link href="/2020/07/03/HowToAskQuestion/"/>
<url>/2020/07/03/HowToAskQuestion/</url>
<content type="html"><![CDATA[<div class="markdown-body">How To Ask Questions The Smart Way(Github镜像源)<a id="more"></a><h1 id="提问的智慧"><a href="#提问的智慧" class="headerlink" title="提问的智慧"></a>提问的智慧</h1><p><a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/pulls" target="_blank" rel="noopener"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" srcset="/img/loading.gif" alt="PRs Welcome"></a></p><p><strong>How To Ask Questions The Smart Way</strong></p><p>Copyright © 2001,2006,2014 Eric S. Raymond, Rick Moen</p><p>本指南英文版版权为 Eric S. Raymond, Rick Moen 所有。</p><p>原文网址:<a href="http://www.catb.org/~esr/faqs/smart-questions.html" target="_blank" rel="noopener">http://www.catb.org/~esr/faqs/smart-questions.html</a></p><p>Copyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu</p><p>本中文指南是基于原文 3.10 版以及 2010 年由 <a href="https://github.com/gasolin" target="_blank" rel="noopener">Gasolin</a> 所翻译版本的最新翻译;</p><p>协助指出翻译问题,<strong>请<a href="https://github.com/ryanhanwu/smartquestions/issues/new" target="_blank" rel="noopener">发 Issue</a>,或直接<a href="https://github.com/ryanhanwu/smartquestions/compare/" target="_blank" rel="noopener">发 Pull Request</a> 给我。</strong></p><p>本文另有<a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README.md" target="_blank" rel="noopener">繁體中文版</a>。</p><h2 id="原文版本历史"><a href="#原文版本历史" class="headerlink" title="原文版本历史"></a><a href="https://github.com/ryanhanwu/smartquestions/blob/master/history.md" target="_blank" rel="noopener">原文版本历史</a></h2><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#声明">声明</a></li><li><a href="#简介">简介</a></li><li><a href="#在提问之前">在提问之前</a></li><li><a href="#当你提问时">当你提问时</a><ul><li><a href="#慎选提问的论坛">慎选提问的论坛</a></li><li><a href="#stack-overflow">Stack Overflow</a></li><li><a href="#网站和-irc-论坛">网站和 IRC 论坛</a></li><li><a href="#第二步使用项目邮件列表">第二步,使用项目邮件列表</a></li><li><a href="#使用有意义且描述明确的标题">使用有意义且描述明确的标题</a></li><li><a href="#使问题容易回复">使问题容易回复</a></li><li><a href="#用清晰正确精准并合法语法的语句">用清晰、正确、精准并合法语法的语句</a></li><li><a href="#使用易于读取且标准的文件格式发送问题">使用易于读取且标准的文件格式发送问题</a></li><li><a href="#精确地描述问题并言之有物">精确地描述问题并言之有物</a></li><li><a href="#话不在多而在精">话不在多而在精</a></li><li><a href="#别动辄声称找到-bug">别动辄声称找到 Bug</a></li><li><a href="#低声下气不能代替你的功课">低声下气不能代替你的功课</a></li><li><a href="#描述问题症状而非你的猜测">描述问题症状而非你的猜测</a></li><li><a href="#按发生时间先后列出问题症状">按发生时间先后列出问题症状</a></li><li><a href="#描述目标而不是过程">描述目标而不是过程</a></li><li><a href="#别要求使用私人电邮回复">别要求使用私人电邮回复</a></li><li><a href="#清楚明确的表达你的问题以及需求">清楚明确的表达你的问题以及需求</a></li><li><a href="#询问有关代码的问题时">询问有关代码的问题时</a></li><li><a href="#别把自己家庭作业的问题贴上来">别把自己家庭作业的问题贴上来</a></li><li><a href="#去掉无意义的提问句">去掉无意义的提问句</a></li><li><a href="#即使你很急也不要在标题写紧急">即使你很急也不要在标题写紧急</a></li><li><a href="#礼多人不怪而且有时还很有帮助">礼多人不怪,而且有时还很有帮助</a></li><li><a href="#问题解决后加个简短的补充说明">问题解决后,加个简短的补充说明</a></li></ul></li><li><a href="#如何解读答案">如何解读答案</a><ul><li><a href="#rtfm-和-stfw如何知道你已完全搞砸了">RTFM 和 STFW:如何知道你已完全搞砸了</a></li><li><a href="#如果还是搞不懂">如果还是搞不懂</a></li><li><a href="#处理无礼的回应">处理无礼的回应</a></li></ul></li><li><a href="#如何避免扮演失败者">如何避免扮演失败者</a></li><li><a href="#不该问的问题">不该问的问题</a></li><li><a href="#好问题与蠢问题">好问题与蠢问题</a></li><li><a href="#如果得不到回答">如果得不到回答</a></li><li><a href="#如何更好地回答问题">如何更好地回答问题</a></li><li><a href="#相关资源">相关资源</a></li><li><a href="#鸣谢">鸣谢</a></li></ul><h2 id="声明"><a href="#声明" class="headerlink" title="声明"></a>声明</h2><p>许多项目在他们的使用协助/说明网页中链接了本指南,这么做很好,我们也鼓励大家都这么做。但如果你是负责管理这个项目网页的人,请在超链接附近的显著位置上注明:</p><p><strong>本指南不提供此项目的实际支持服务!</strong></p><p>我们已经深刻领教到少了上述声明所带来的痛苦。因为少了这点声明,我们不停地被一些白痴纠缠。这些白痴认为既然我们发布了这本指南,那么我们就有责任解决世上所有的技术问题。</p><p>如果你是因为需要某些协助而正在阅读这本指南,并且最后离开是因为发现从本指南作者们身上得不到直接的协助,那么你就是我们所说的那些白痴之一。别问我们问题,我们只会忽略你。我们在这本指南中是教你如何从那些真正懂得你所遇到软件或硬件问题的人取得协助,而 99% 的情况下那不会是我们。除非你确定本指南的作者之一刚好是你所遇到的问题领域的专家,否则请不要打扰我们,这样大家都会开心一点。</p><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>在<a href="http://www.catb.org/~esr/faqs/hacker-howto.html" target="_blank" rel="noopener">黑客</a>的世界里,当你拋出一个技术问题时,最终是否能得到有用的回答,往往取决于你所提问和追问的方式。本指南将教你如何正确的提问以获得你满意的答案。</p><p>不只是黑客,现在开源(Open Source)软件已经相当盛行,你常常也可以由其他有经验的使用者身上得到好答案,这是件<strong>好事</strong>;使用者比起黑客来,往往对那些新手常遇到的问题更宽容一些。然而,将有经验的使用者视为黑客,并采用本指南所提的方法与他们沟通,同样也是能从他们身上得到满意回答的最有效方式。</p><p>首先你应该明白,黑客们喜爱有挑战性的问题,或者能激发他们思维的好问题。如果我们并非如此,那我们也不会成为你想询问的对象。如果你给了我们一个值得反复咀嚼玩味的好问题,我们自会对你感激不尽。好问题是激励,是厚礼。好问题可以提高我们的理解力,而且通常会暴露我们以前从没意识到或者思考过的问题。对黑客而言,”好问题!”是诚挚的大力称赞。</p><p>尽管如此,黑客们有着蔑视或傲慢面对简单问题的坏名声,这有时让我们看起来对新手、无知者似乎较有敌意,但其实不是那样的。</p><p>我们不讳言我们对那些不愿思考、或者在发问前不做他们该做的事的人的蔑视。那些人是时间杀手 —— 他们只想索取,从不付出,消耗我们可用在更有趣的问题或更值得回答的人身上的时间。我们称这样的人为 <code>失败者(撸瑟)</code> (由于历史原因,我们有时把它拼作 <code>lusers</code>)。</p><p>我们意识到许多人只是想使用我们写的软件,他们对学习技术细节没有兴趣。对大多数人而言,电脑只是种工具,是种达到目的的手段而已。他们有自己的生活并且有更要紧的事要做。我们了解这点,也从不指望每个人都对这些让我们着迷的技术问题感兴趣。尽管如此,我们回答问题的风格是指向那些真正对此有兴趣并愿意主动参与解决问题的人,这一点不会变,也不该变。如果连这都变了,我们就是在降低做自己最擅长的事情上的效率。</p><p>我们(在很大程度上)是自愿的,从繁忙的生活中抽出时间来解答疑惑,而且时常被提问淹没。所以我们无情的滤掉一些话题,特别是拋弃那些看起来像失败者的家伙,以便更高效的利用时间来回答<code>赢家(winner)</code>的问题。</p><p>如果你厌恶我们的态度,高高在上,或过于傲慢,不妨也设身处地想想。我们并没有要求你向我们屈服 —— 事实上,我们大多数人非常乐意与你平等地交流,只要你付出小小努力来满足基本要求,我们就会欢迎你加入我们的文化。但让我们帮助那些不愿意帮助自己的人是没有效率的。无知没有关系,但装白痴就是不行。</p><p>所以,你不必在技术上很在行才能吸引我们的注意,但你必须表现出能引导你变得在行的特质 – 机敏、有想法、善于观察、乐于主动参与解决问题。如果你做不到这些使你与众不同的事情,我们建议你花点钱找家商业公司签个技术支持服务合同,而不是要求黑客个人无偿地帮助你。</p><p>如果你决定向我们求助,当然你也不希望被视为失败者,更不愿成为失败者中的一员。能立刻得到快速并有效答案的最好方法,就是像赢家那样提问 – 聪明、自信、有解决问题的思路,只是偶尔在特定的问题上需要获得一点帮助。</p><p>(欢迎对本指南提出改进意见。你可以 email 你的建议至 <a href="[email protected]">[email protected]</a> 或 <a href="[email protected]">[email protected]</a>。然而请注意,本文并非<a href="http://www.ietf.org/rfc/rfc1855.txt" target="_blank" rel="noopener">网络礼节</a>的通用指南,而我们通常会拒绝无助于在技术论坛得到有用答案的建议)。</p><h2 id="在提问之前"><a href="#在提问之前" class="headerlink" title="在提问之前"></a>在提问之前</h2><p>在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请先做到以下事情:</p><ol><li>尝试在你准备提问的论坛的旧文章中搜索答案。</li><li>尝试上网搜索以找到答案。</li><li>尝试阅读手册以找到答案。</li><li>尝试阅读常见问题文件(FAQ)以找到答案。</li><li>尝试自己检查或试验以找到答案。</li><li>向你身边的强者朋友打听以找到答案。</li><li>如果你是程序开发者,请尝试阅读源代码以找到答案。</li></ol><p>当你提出问题的时候,请先表明你已经做了上述的努力;这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所<strong>学到</strong>的东西会更好,因为我们更乐于回答那些表现出能从答案中学习的人的问题。</p><p>运用某些策略,比如先用 Google 搜索你所遇到的各种错误信息(搜索 <a href="http://groups.google.com/" target="_blank" rel="noopener">Google 论坛</a>和网页),这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果,在邮件列表或新闻组寻求帮助时加上一句 <code>我在 Google 中搜过下列句子但没有找到什么有用的东西</code> 也是件好事,即使它只是表明了搜索引擎不能提供哪些帮助。这么做(加上搜索过的字串)也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。</p><p>别着急,不要指望几秒钟的 Google 搜索就能解决一个复杂的问题。在向专家求助之前,再阅读一下常见问题文件(FAQ)、放轻松、坐舒服一些,再花点时间思考一下这个问题。相信我们,他们能从你的提问看出你做了多少阅读与思考,如果你是有备而来,将更有可能得到解答。不要将所有问题一股脑拋出,只因你的第一次搜索没有找到答案(或者找到太多答案)。</p><p>准备好你的问题,再将问题仔细的思考过一遍,因为草率的发问只能得到草率的回答,或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力,你越有可能得到实质性的帮助。</p><p>小心别问错了问题。如果你的问题基于错误的假设,某个普通黑客(J. Random Hacker)多半会一边在心里想着<code>蠢问题…</code>, 一边用无意义的字面解释来答复你,希望着你会从问题的回答(而非你想得到的答案)中汲取教训。</p><p>绝不要自以为<strong>够格</strong>得到答案,你没有;你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去<strong>挣到</strong>一个答案,靠提出有内涵的、有趣的、有思维激励作用的问题 —— 一个有潜力能贡献社区经验的问题,而不仅仅是被动的从他人处索取知识。</p><p>另一方面,表明你愿意在找答案的过程中做点什么是一个非常好的开端。<code>谁能给点提示?</code>、<code>我的这个例子里缺了什么?</code>以及<code>我应该检查什么地方</code>比<code>请把我需要的确切的过程贴出来</code>更容易得到答复。因为你表现出只要有人能指个正确方向,你就有完成它的能力和决心。</p><h2 id="当你提问时"><a href="#当你提问时" class="headerlink" title="当你提问时"></a>当你提问时</h2><h3 id="慎选提问的论坛"><a href="#慎选提问的论坛" class="headerlink" title="慎选提问的论坛"></a>慎选提问的论坛</h3><p>小心选择你要提问的场合。如果你做了下述的事情,你很可能被忽略掉或者被看作失败者:</p><ul><li>在与主题不合的论坛上贴出你的问题。</li><li>在探讨进阶技术问题的论坛张贴非常初级的问题;反之亦然。</li><li>在太多的不同新闻群组上重复转贴同样的问题(cross-post)。</li><li>向既非熟人也没有义务解决你问题的人发送私人电邮。</li></ul><p>黑客会剔除掉那些搞错场合的问题,以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。</p><p>因此,第一步是找到对的论坛。再说一次,Google 和其它搜索引擎还是你的朋友,用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题(FAQ)、邮件列表及相关说明文件的链接。如果你的努力(包括<strong>阅读</strong> FAQ)都没有结果,网站上也许还有报告 Bug(Bug-reporting)的流程或链接,如果是这样,链过去看看。</p><p>向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说,别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 – 如果你不确定,那就向别处发送,或者压根别发。</p><p>在选择论坛、新闻群组或邮件列表时,别太相信名字,先看看 FAQ 或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题,这样可以让你感受一下那里的文化。事实上,事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意,也许这样就找到答案了。即使没有,也能帮助你归纳出更好的问题。</p><p>别像机关枪似的一次”扫射”所有的帮助渠道,这就像大喊大叫一样会使人不快。要一个一个地来。</p><p>搞清楚你的主题!最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于 Unix 或 Windows 操作系统程序界面的问题。如果你不明白为什么这是大错,最好在搞清楚这之间差异之前什么也别问。</p><p>一般来说,在仔细挑选的公共论坛中提问,会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点,一是看潜在的回复者有多少,二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。</p><p>可以理解的是,老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样,你的加入也有可能使情况走向极端 —— 已经好几次了,一些热门软件的作者从自己软件的支持中抽身出来,因为伴随而来涌入其私人邮箱的无用邮件变得无法忍受。</p><h3 id="Stack-Overflow"><a href="#Stack-Overflow" class="headerlink" title="Stack Overflow"></a>Stack Overflow</h3><p>搜索,<strong>然后</strong> 在 Stack Exchange 问。</p><p>近年来,Stack Exchange community 社区已经成为回答技术及其他问题的主要渠道,尤其是那些开放源码的项目。</p><p>因为 Google 索引是即时的,在看 Stack Exchange 之前先在 Google 搜索。有很高的机率某人已经问了一个类似的问题,而且 Stack Exchange 网站们往往会是搜索结果中最前面几个。如果你在 Google 上没有找到任何答案,你再到特定相关主题的网站去找。用标签(Tag)搜索能让你更缩小你的搜索结果。</p><p>Stack Exchange 已经成长到<a href="http://stackexchange.com/sites" target="_blank" rel="noopener">超过一百个网站</a>,以下是最常用的几个站:</p><ul><li>Super User 是问一些通用的电脑问题,如果你的问题跟代码或是写程序无关,只是一些网络连线之类的,请到这里。</li><li>Stack Overflow 是问写程序有关的问题。</li><li>Server Fault 是问服务器和网管相关的问题。</li></ul><h3 id="网站和-IRC-论坛"><a href="#网站和-IRC-论坛" class="headerlink" title="网站和 IRC 论坛"></a>网站和 IRC 论坛</h3><p>本地的使用者群组(user group),或者你所用的 Linux 发行版本也许正在宣传他们的网页论坛或 IRC 频道,并提供新手帮助(在一些非英语国家,新手论坛很可能还是邮件列表), 这些地方是开始提问的好首选,特别是当你觉得遇到的也许只是相对简单或者很普通的问题时。有广告赞助的 IRC 频道是公开欢迎提问的地方,通常可以即时得到回应。</p><p>事实上,如果程序出的问题只发生在特定 Linux 发行版提供的版本(这很常见),最好先去该发行版的论坛或邮件列表中提问,再到程序本身的论坛或邮件列表提问。(否则)该项目的黑客可能仅仅回复 “用<strong>我们的</strong>版本”。</p><p>在任何论坛发文以前,先确认一下有没有搜索功能。如果有,就试着搜索一下问题的几个关键词,也许这会有帮助。如果在此之前你已做过通用的网页搜索(你也该这样做),还是再搜索一下论坛,搜索引擎有可能没来得及索引此论坛的全部内容。</p><p>通过论坛或 IRC 频道来提供使用者支持服务有增长的趋势,电子邮件则大多为项目开发者间的交流而保留。所以最好先在论坛或 IRC 中寻求与该项目相关的协助。</p><p>在使用 IRC 的时候,首先最好不要发布很长的问题描述,有些人称之为频道洪水。最好通过一句话的问题描述来开始聊天。</p><h3 id="第二步,使用项目邮件列表"><a href="#第二步,使用项目邮件列表" class="headerlink" title="第二步,使用项目邮件列表"></a>第二步,使用项目邮件列表</h3><p>当某个项目提供开发者邮件列表时,要向列表而不是其中的个别成员提问,即使你确信他能最好地回答你的问题。查一查项目的文件和首页,找到项目的邮件列表并使用它。有几个很好的理由支持我们采用这种办法:</p><ul><li>任何好到需要向个别开发者提出的问题,也将对整个项目群组有益。反之,如果你认为自己的问题对整个项目群组来说太愚蠢,也不能成为骚扰个别开发者的理由。</li><li>向列表提问可以分散开发者的负担,个别开发者(尤其是项目领导人)也许太忙以至于没法回答你的问题。</li><li>大多数邮件列表都会被存档,那些被存档的内容将被搜索引擎索引。如果你向列表提问并得到解答,将来其它人可以通过网页搜索找到你的问题和答案,也就不用再次发问了。</li><li>如果某些问题经常被问到,开发者可以利用此信息来改进说明文件或软件本身,以使其更清楚。如果只是私下提问,就没有人能看到最常见问题的完整场景。</li></ul><p>如果一个项目既有”使用者” 也有”开发者”(或”黑客”)邮件列表或论坛,而你又不会动到那些源代码,那么就向”使用者”列表或论坛提问。不要假设自己会在开发者列表中受到欢迎,那些人多半会将你的提问视为干扰他们开发的噪音。</p><p>然而,如果你<strong>确信</strong>你的问题很特别,而且在”使用者” 列表或论坛中几天都没有回复,可以试试前往”开发者”列表或论坛发问。建议你在张贴前最好先暗地里观察几天以了解那里的行事方式(事实上这是参与任何私有或半私有列表的好主意)</p><p>如果你找不到一个项目的邮件列表,而只能查到项目维护者的电子邮件地址,尽管向他发信。即使是在这种情况下,也别假设(项目)邮件列表不存在。在你的电子邮件中,请陈述你已经试过但没有找到合适的邮件列表,也提及你不反对将自己的邮件转发给他人(许多人认为,即使没什么秘密,私人电子邮件也不应该被公开。通过允许将你的电子邮件转发他人,你给了相应人员处置你邮件的选择)。</p><h3 id="使用有意义且描述明确的标题"><a href="#使用有意义且描述明确的标题" class="headerlink" title="使用有意义且描述明确的标题"></a>使用有意义且描述明确的标题</h3><p>在邮件列表、新闻群组或论坛中,大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的<code>帮帮忙</code>、<code>跪求</code>、<code>急</code>(更别说<code>救命啊!!!!</code>这样让人反感的话,用这种标题会被条件反射式地忽略)来浪费这个机会。不要妄想用你的痛苦程度来打动我们,而应该是在这点空间中使用极简单扼要的描述方式来提出问题。</p><p>一个好标题范例是<code>目标 —— 差异</code>式的描述,许多技术支持组织就是这样做的。在<code>目标</code>部分指出是哪一个或哪一组东西有问题,在<code>差异</code>部分则描述与期望的行为不一致的地方。</p><blockquote><p>蠢问题:救命啊!我的笔记本电脑不能正常显示了!</p></blockquote><blockquote><p>聪明问题:X.org 6.8.1 的鼠标光标会变形,某牌显卡 MV1005 芯片组。</p></blockquote><blockquote><p>更聪明问题:X.org 6.8.1 的鼠标光标,在某牌显卡 MV1005 芯片组环境下 - 会变形。</p></blockquote><p>编写<code>目标 —— 差异</code> 式描述的过程有助于你组织对问题的细致思考。是什么被影响了? 仅仅是鼠标光标或者还有其它图形?只在 X.org 的 X 版中出现?或只是出现在 6.8.1 版中? 是针对某牌显卡芯片组?或者只是其中的 MV1005 型号? 一个黑客只需瞄一眼就能够立即明白你的环境<strong>和</strong>你遇到的问题。</p><p>总而言之,请想像一下你正在一个只显示标题的存档讨论串(Thread)索引中查寻。让你的标题更好地反映问题,可使下一个搜索类似问题的人能够关注这个讨论串,而不用再次提问相同的问题。</p><p>如果你想在回复中提出问题,记得要修改内容标题,以表明你是在问一个问题, 一个看起来像 <code>Re: 测试</code> 或者 <code>Re: 新 bug</code> 的标题很难引起足够重视。另外,在不影响连贯性之下,适当引用并删减前文的内容,能给新来的读者留下线索。</p><p>对于讨论串,不要直接点击回复来开始一个全新的讨论串,这将限制你的观众。因为有些邮件阅读程序,比如 mutt ,允许使用者按讨论串排序并通过折叠讨论串来隐藏消息,这样做的人永远看不到你发的消息。</p><p>仅仅改变标题还不够。mutt 和其它一些邮件阅读程序还会检查邮件标题以外的其它信息,以便为其指定讨论串。所以宁可发一个全新的邮件。</p><p>在网页论坛上,好的提问方式稍有不同,因为讨论串与特定的信息紧密结合,并且通常在讨论串外就看不到里面的内容,故通过回复提问,而非改变标题是可接受的。不是所有论坛都允许在回复中出现分离的标题,而且这样做了基本上没有人会去看。不过,通过回复提问,这本身就是暧昧的做法,因为它们只会被正在查看该标题的人读到。所以,除非你<strong>只想</strong>在该讨论串当前活跃的人群中提问,不然还是另起炉灶比较好。</p><h3 id="使问题容易回复"><a href="#使问题容易回复" class="headerlink" title="使问题容易回复"></a>使问题容易回复</h3><p>以<code>请将你的回复发送到……</code>来结束你的问题多半会使你得不到回答。如果你觉得花几秒钟在邮件客户端设置一下回复地址都麻烦,我们也觉得花几秒钟思考你的问题更麻烦。如果你的邮件程序不支持这样做,<a href="http://linuxmafia.com/faq/Mail/muas.html" target="_blank" rel="noopener">换个好点的</a>;如果是操作系统不支持这种邮件程序,也换个好点的。</p><p>在论坛,要求通过电子邮件回复是非常无礼的,除非你认为回复的信息可能比较敏感(有人会为了某些未知的原因,只让你而不是整个论坛知道答案)。如果你只是想在有人回复讨论串时得到电子邮件提醒,可以要求网页论坛发送给你。几乎所有论坛都支持诸如<code>追踪此讨论串</code>、<code>有回复时发送邮件提醒</code>等功能。</p><h3 id="用清晰、正确、精准并语法正确的语句"><a href="#用清晰、正确、精准并语法正确的语句" class="headerlink" title="用清晰、正确、精准并语法正确的语句"></a>用清晰、正确、精准并语法正确的语句</h3><p>我们从经验中发现,粗心的提问者通常也会粗心的写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。</p><p>正确的拼写、标点符号和大小写是很重要的。一般来说,如果你觉得这样做很麻烦,不想在乎这些,那我们也觉得麻烦,不想在乎你的提问。花点额外的精力斟酌一下字句,用不着太僵硬与正式 —— 事实上,黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它<strong>必须很</strong>准确,而且有迹象表明你是在思考和关注问题。</p><p>正确地拼写、使用标点和大小写,不要将<code>its</code>混淆为<code>it's</code>,<code>loose</code>搞成<code>lose</code>或者将<code>discrete</code>弄成<code>discreet</code>。不要<strong>全部用大写</strong>,这会被视为无礼的大声嚷嚷(全部小写也好不到哪去,因为不易阅读。<a href="http://en.wikipedia.org/wiki/Alan_Cox" target="_blank" rel="noopener">Alan Cox</a> 也许可以这样做,但你不行)。</p><p>更白话的说,如果你写得像是个半文盲[译注:<a href="http://zh.wikipedia.org/wiki/小白" target="_blank" rel="noopener">小白</a>],那多半得不到理睬。也不要使用即时通信中的简写或<a href="http://zh.wikipedia.org/wiki/火星文" target="_blank" rel="noopener">火星文</a>,如将<code>的</code>简化为<code>d</code>会使你看起来像一个为了少打几个键而省字的小白。更糟的是,如果像个小孩似地鬼画符那绝对是在找死,可以肯定没人会理你(或者最多是给你一大堆指责与挖苦)。</p><p>如果在使用非母语的论坛提问,你可以犯点拼写和语法上的小错,但决不能在思考上马虎(没错,我们通常能弄清两者的分别)。同时,除非你知道回复者使用的语言,否则请使用英语书写。繁忙的黑客一般会直接删除用他们看不懂语言写的消息。在网络上英语是通用语言,用英语书写可以将你的问题在尚未被阅读就被直接删除的可能性降到最低。</p><p>如果英文是你的外语(Second language),提示潜在回复者你有潜在的语言困难是很好的:<br>[译注:以下附上原文以供使用]</p><blockquote><p>English is not my native language; please excuse typing errors.</p></blockquote><ul><li>英文不是我的母语,请原谅我的错字或语法。</li></ul><blockquote><p>If you speak $LANGUAGE, please email/PM me;<br>I may need assistance translating my question.</p></blockquote><ul><li>如果你说<strong>某语言</strong>,请寄信/私讯给我;我需要有人协助我翻译我的问题。</li></ul><blockquote><p>I am familiar with the technical terms,<br>but some slang expressions and idioms are difficult for me.</p></blockquote><ul><li>我对技术名词很熟悉,但对于俗语或是特别用法比较不甚了解。</li></ul><blockquote><p>I’ve posted my question in $LANGUAGE and English.<br>I’ll be glad to translate responses, if you only use one or the other.</p></blockquote><ul><li>我把我的问题用<strong>某语言</strong>和英文写出来,如果你只用一种语言回答,我会乐意将其翻译成另一种。</li></ul><h3 id="使用易于读取且标准的文件格式发送问题"><a href="#使用易于读取且标准的文件格式发送问题" class="headerlink" title="使用易于读取且标准的文件格式发送问题"></a>使用易于读取且标准的文件格式发送问题</h3><p>如果你人为地将问题搞得难以阅读,它多半会被忽略,人们更愿读易懂的问题,所以:</p><ul><li>使用纯文字而不是 HTML (<a href="http://archive.birdhouse.org/etc/evilmail.html" target="_blank" rel="noopener">关闭 HTML</a> 并不难)。</li><li>使用 MIME 附件通常是可以的,前提是真正有内容(譬如附带的源代码或 patch),而不仅仅是邮件程序生成的模板(譬如只是信件内容的拷贝)。</li><li>不要发送一段文字只是一行句子但自动换行后会变成多行的邮件(这使得回复部分内容非常困难)。设想你的读者是在 80 个字符宽的终端机上阅读邮件,最好设置你的换行分割点小于 80 字。</li><li>但是,对一些特殊的文件<strong>不要</strong>设置固定宽度(譬如日志档案拷贝或会话记录)。数据应该原样包含,让回复者有信心他们看到的是和你看到的一样的东西。</li><li>在英语论坛中,不要使用<code>Quoted-Printable</code> MIME 编码发送消息。这种编码对于张贴非 ASCII 语言可能是必须的,但很多邮件程序并不支持这种编码。当它们处理换行时,那些文本中四处散布的<code>=20</code>符号既难看也分散注意力,甚至有可能破坏内容的语意。</li><li>绝对,<strong>永远</strong>不要指望黑客们阅读使用封闭格式编写的文档,像微软公司的 Word 或 Excel 文件等。大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你家门口时你的反应一样。即便他们能够处理,他们也很厌恶这么做。</li><li>如果你从使用 Windows 的电脑发送电子邮件,关闭微软愚蠢的<code>智能引号</code>功能 (从[选项] > [校订] > [自动校正选项],勾选掉<code>智能引号</code>单选框),以免在你的邮件中到处散布垃圾字符。</li><li>在论坛,勿滥用<code>表情符号</code>和<code>HTML</code>功能(当它们提供时)。一两个表情符号通常没有问题,但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。这通常不是个好主意,除非你只是对性而不是对答案感兴趣。</li></ul><p>如果你使用图形用户界面的邮件程序(如微软公司的 Outlook 或者其它类似的),注意它们的默认设置不一定满足这些要求。大多数这类程序有基于选单的<code>查看源代码</code>命令,用它来检查发送文件夹中的邮件,以确保发送的是纯文本文件同时没有一些奇怪的字符。</p><h3 id="精确地描述问题并言之有物"><a href="#精确地描述问题并言之有物" class="headerlink" title="精确地描述问题并言之有物"></a>精确地描述问题并言之有物</h3><ul><li>仔细、清楚地描述你的问题或 Bug 的症状。</li><li>描述问题发生的环境(机器配置、操作系统、应用程序、以及相关的信息),提供经销商的发行版和版本号(如:<code>Fedora Core 4</code>、<code>Slackware 9.1</code>等)。</li><li>描述在提问前你是怎样去研究和理解这个问题的。</li><li>描述在提问前为确定问题而采取的诊断步骤。</li><li>描述最近做过什么可能相关的硬件或软件变更。</li><li>尽可能的提供一个可以<code>重现这个问题的可控环境</code>的方法。</li></ul><p>尽量去揣测一个黑客会怎样反问你,在你提问之前预先将黑客们可能遇到的问题回答一遍。</p><p>以上几点中,当你报告的是你认为可能在代码中的问题时,给黑客一个可以重现你的问题的环境尤其重要。当你这么做时,你得到有效的回答的机会和速度都会大大的提升。</p><p><a href="http://www.chiark.greenend.org.uk/~sgtatham/" target="_blank" rel="noopener">Simon Tatham</a> 写过一篇名为《<a href="http://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html" target="_blank" rel="noopener">如何有效的报告 Bug</a>》的出色文章。强力推荐你也读一读。</p><h3 id="话不在多而在精"><a href="#话不在多而在精" class="headerlink" title="话不在多而在精"></a>话不在多而在精</h3><p>你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境,尽量将它剪裁得越小越好。</p><p>这样做的用处至少有三点。<br>第一,表现出你为简化问题付出了努力,这可以使你得到回答的机会增加;<br>第二,简化问题使你更有可能得到<strong>有用</strong>的答案;<br>第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或权宜之计。</p><h3 id="别动辄声称找到-Bug"><a href="#别动辄声称找到-Bug" class="headerlink" title="别动辄声称找到 Bug"></a>别动辄声称找到 Bug</h3><p>当你在使用软件中遇到问题,除非你非常、<strong>非常</strong>的有根据,不要动辄声称找到了 Bug。提示:除非你能提供解决问题的源代码补丁,或者提供回归测试来表明前一版本中行为不正确,否则你都多半不够完全确信。这同样适用在网页和文件,如果你(声称)发现了文件的<code>Bug</code>,你应该能提供相应位置的修正或替代文件。</p><p>请记得,还有许多其它使用者没遇到你发现的问题,否则你在阅读文件或搜索网页时就应该发现了(你在抱怨前<a href="#在提问之前">已经做了这些,是吧</a>?)。这也意味着很有可能是你弄错了而不是软件本身有问题。</p><p>编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug,也就是在质疑他们的能力,即使你是对的,也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有<code>Bug</code>时,这尤其严重。</p><p>提问时,即使你私下非常确信已经发现一个真正的 Bug,最好写得像是<strong>你</strong>做错了什么。如果真的有 Bug,你会在回复中看到这点。这样做的话,如果真有 Bug,维护者就会向你道歉,这总比你惹恼别人然后欠别人一个道歉要好一点。</p><h3 id="低声下气不能代替你的功课"><a href="#低声下气不能代替你的功课" class="headerlink" title="低声下气不能代替你的功课"></a>低声下气不能代替你的功课</h3><p>有些人明白他们不该粗鲁或傲慢的提问并要求得到答复,但他们选择另一个极端 —— 低声下气:<code>我知道我只是个可悲的新手,一个撸瑟,但...</code>。这既使人困扰,也没有用,尤其是伴随着与实际问题含糊不清的描述时更令人反感。</p><p>别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是,尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。</p><p>有时网页论坛会设有专为新手提问的版面,如果你真的认为遇到了初学者的问题,到那去就是了,但一样别那么低声下气。</p><h3 id="描述问题症状而非你的猜测"><a href="#描述问题症状而非你的猜测" class="headerlink" title="描述问题症状而非你的猜测"></a>描述问题症状而非你的猜测</h3><p>告诉黑客们你认为问题是怎样造成的并没什么帮助。(如果你的推断如此有效,还用向别人求助吗?),因此要确信你原原本本告诉了他们问题的症状,而不是你的解释和理论;让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要,清楚地说明这只是你的猜测,并描述为什么它们不起作用。</p><p><strong>蠢问题</strong></p><blockquote><p>我在编译内核时接连遇到 SIG11 错误,<br>我怀疑某条飞线搭在主板的走线上了,这种情况应该怎样检查最好?</p></blockquote><p><strong>聪明问题</strong></p><blockquote><p>我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU(威盛 Apollo VP2 芯片组),<br>256MB Corsair PC133 SDRAM 内存,在编译内核时,从开机 20 分钟以后就频频产生 SIG11 错误,<br>但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用,但是关机一晚上就又能工作 20 分钟。<br>所有内存都换过了,没有效果。相关部分的标准编译记录如下…。</p></blockquote><p>由于以上这点似乎让许多人觉得难以配合,这里有句话可以提醒你:<code>所有的诊断专家都来自密苏里州。</code> 美国国务院的官方座右铭则是:<code>让我看看</code>(出自国会议员 Willard D. Vandiver 在 1899 年时的讲话:<code>我来自一个出产玉米,棉花,牛蒡和民主党人的国家,滔滔雄辩既不能说服我,也不会让我满意。我来自密苏里州,你必须让我看看。</code>) 针对诊断者而言,这并不是一种怀疑,而只是一种真实而有用的需求,以便让他们看到的是与你看到的原始证据尽可能一致的东西,而不是你的猜测与归纳的结论。所以,大方的展示给我们看吧!</p><h3 id="按发生时间先后列出问题症状"><a href="#按发生时间先后列出问题症状" class="headerlink" title="按发生时间先后列出问题症状"></a>按发生时间先后列出问题症状</h3><p>问题发生前的一系列操作,往往就是对找出问题最有帮助的线索。因此,你的说明里应该包含你的操作步骤,以及机器和软件的反应,直到问题发生。在命令行处理的情况下,提供一段操作记录(例如运行脚本工具所生成的),并引用相关的若干行(如 20 行)记录会非常有帮助。</p><p>如果挂掉的程序有诊断选项(如 -v 的详述开关),试着选择这些能在记录中增加调试信息的选项。记住,<code>多</code>不等于<code>好</code>。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。</p><p>如果你的说明很长(如超过四个段落),在开头简述问题,接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。</p><h3 id="描述目标而不是过程"><a href="#描述目标而不是过程" class="headerlink" title="描述目标而不是过程"></a>描述目标而不是过程</h3><p>如果你想弄清楚如何做某事(而不是报告一个 Bug),在开头就描述你的目标,然后才陈述重现你所卡住的特定步骤。</p><p>经常寻求技术帮助的人在心中有个更高层次的目标,而他们在自以为能达到目标的特定道路上被卡住了,然后跑来问该怎么走,但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。</p><p><strong>蠢问题</strong></p><blockquote><p>我怎样才能从某绘图程序的颜色选择器中取得十六进制的的 RGB 值?</p></blockquote><p><strong>聪明问题</strong></p><blockquote><p>我正试着用替换一幅图片的色码(color table)成自己选定的色码,我现在知道的唯一方法是编辑每个色码区块(table slot),<br>但却无法从某绘图程序的颜色选择器取得十六进制的的 RGB 值。</p></blockquote><p>第二种提问法比较聪明,你可能得到像是<code>建议采用另一个更合适的工具</code>的回复。</p><h3 id="别要求使用私人电邮回复"><a href="#别要求使用私人电邮回复" class="headerlink" title="别要求使用私人电邮回复"></a>别要求使用私人电邮回复</h3><p>黑客们认为问题的解决过程应该公开、透明,此过程中如果更有经验的人注意到不完整或者不当之处,最初的回复才能够、也应该被纠正。同时,作为提供帮助者可以得到一些奖励,奖励就是他的能力和学识被其他同行看到。</p><p>当你要求私下回复时,这个过程和奖励都被中止。别这样做,让<strong>回复者</strong>来决定是否私下回答 —— 如果他真这么做了,通常是因为他认为问题编写太差或者太肤浅,以至于对其它人没有兴趣。</p><p>这条规则存在一条有限的例外,如果你确信提问可能会引来大量雷同的回复时,那么这个神奇的提问句会是<code>向我发电邮,我将为论坛归纳这些回复</code>。试着将邮件列表或新闻群组从洪水般的雷同回复中解救出来是非常有礼貌的 —— 但你必须信守诺言。</p><h3 id="清楚明确的表达你的问题以及需求"><a href="#清楚明确的表达你的问题以及需求" class="headerlink" title="清楚明确的表达你的问题以及需求"></a>清楚明确的表达你的问题以及需求</h3><p>漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人(他们忙是因为要亲自完成大部分工作)。这样的人对无节制的时间黑洞相当厌恶,所以他们也倾向于厌恶那些漫无边际的提问。</p><p>如果你明确表述需要回答者做什么(如提供指点、发送一段代码、检查你的补丁、或是其他等等),就最有可能得到有用的答案。因为这会定出一个时间和精力的上限,便于回答者能集中精力来帮你。这么做很棒。</p><p>要理解专家们所处的世界,请把专业技能想像为充裕的资源,而回复的时间则是稀缺的资源。你要求他们奉献的时间越少,你越有可能从真正专业而且很忙的专家那里得到解答。</p><p>所以,界定一下你的问题,使专家花在辨识你的问题和回答所需要付出的时间减到最少,这技巧对你有用答案相当有帮助 —— 但这技巧通常和简化问题有所区别。因此,问<code>我想更好的理解 X,可否指点一下哪有好一点说明?</code>通常比问<code>你能解释一下 X 吗?</code>更好。如果你的代码不能运作,通常请别人看看哪里有问题,比要求别人替你改正要明智得多。</p><h3 id="询问有关代码的问题时"><a href="#询问有关代码的问题时" class="headerlink" title="询问有关代码的问题时"></a>询问有关代码的问题时</h3><p>别要求他人帮你调试有问题的代码,不提示一下应该从何入手。张贴几百行的代码,然后说一声:<code>它不能工作</code>会让你完全被忽略。只贴几十行代码,然后说一句:<code>在第七行以后,我期待它显示 <x>,但实际出现的是 <y></code>比较有可能让你得到回应。</p><p>最有效描述程序问题的方法是提供最精简的 Bug 展示测试用例(bug-demonstrating test case)。什么是最精简的测试用例?那是问题的缩影;一小个程序片段能<strong>刚好</strong>展示出程序的异常行为,而不包含其他令人分散注意力的内容。怎么制作最精简的测试用例?如果你知道哪一行或哪一段代码会造成异常的行为,复制下来并加入足够重现这个状况的代码(例如,足以让这段代码能被编译/直译/被应用程序处理)。如果你无法将问题缩减到一个特定区块,就复制一份代码并移除不影响产生问题行为的部分。总之,测试用例越小越好(查看<a href="#话不在多而在精">话不在多而在精</a>一节)。</p><p>一般而言,要得到一段相当精简的测试用例并不太容易,但永远先尝试这样做的是种好习惯。这种方式可以帮助你了解如何自行解决这个问题 —— 而且即使你的尝试不成功,黑客们也会看到你在尝试取得答案的过程中付出了努力,这可以让他们更愿意与你合作。</p><p>如果你只是想让别人帮忙审查(Review)一下代码,在信的开头就要说出来,并且一定要提到你认为哪一部分特别需要关注以及为什么。</p><h3 id="别把自己家庭作业的问题贴上来"><a href="#别把自己家庭作业的问题贴上来" class="headerlink" title="别把自己家庭作业的问题贴上来"></a>别把自己家庭作业的问题贴上来</h3><p>黑客们很擅长分辨哪些问题是家庭作业式的问题;因为我们中的大多数都曾自己解决这类问题。同样,这些问题得由<strong>你</strong>来搞定,你会从中学到东西。你可以要求给点提示,但别要求得到完整的解决方案。</p><p>如果你怀疑自己碰到了一个家庭作业式的问题,但仍然无法解决,试试在使用者群组,论坛或(最后一招)在项目的<strong>使用者</strong>邮件列表或论坛中提问。尽管黑客们<strong>会</strong>看出来,但一些有经验的使用者也许仍会给你一些提示。</p><h3 id="去掉无意义的提问句"><a href="#去掉无意义的提问句" class="headerlink" title="去掉无意义的提问句"></a>去掉无意义的提问句</h3><p>避免用无意义的话结束提问,例如<code>有人能帮我吗?</code>或者<code>这有答案吗?</code>。</p><p>首先:如果你对问题的描述不是很好,这样问更是画蛇添足。</p><p>其次:由于这样问是画蛇添足,黑客们会很厌烦你 —— 而且通常会用逻辑上正确,但毫无意义的回答来表示他们的蔑视, 例如:<code>没错,有人能帮你</code>或者<code>不,没答案</code>。</p><p>一般来说,避免用 <code>是或否</code>、<code>对或错</code>、<code>有或没有</code>类型的问句,除非你想得到<a href="http://homepage.ntlworld.com./jonathan.deboynepollard/FGA/questions-with-yes-or-no-answers.html" target="_blank" rel="noopener">是或否类型的回答</a>。</p><h3 id="即使你很急也不要在标题写紧急"><a href="#即使你很急也不要在标题写紧急" class="headerlink" title="即使你很急也不要在标题写紧急"></a>即使你很急也不要在标题写<code>紧急</code></h3><p>这是你的问题,不是我们的。宣称<code>紧急</code>极有可能事与愿违:大多数黑客会直接删除无礼和自私地企图即时引起关注的问题。更严重的是,<code>紧急</code>这个字(或是其他企图引起关注的标题)通常会被垃圾信过滤器过滤掉 —— 你希望能看到你问题的人可能永远也看不到。</p><p>有半个例外的情况是,如果你是在一些很高调,会使黑客们兴奋的地方,也许值得这样去做。在这种情况下,如果你有时间压力,也很有礼貌地提到这点,人们也许会有兴趣回答快一点。</p><p>当然,这风险很大,因为黑客们兴奋的点多半与你的不同。譬如从 NASA 国际空间站(International Space Station)发这样的标题没有问题,但用自我感觉良好的慈善行为或政治原因发肯定不行。事实上,张贴诸如<code>紧急:帮我救救这个毛绒绒的小海豹!</code>肯定让你被黑客忽略或惹恼他们,即使他们认为毛绒绒的小海豹很重要。</p><p>如果你觉得这点很不可思议,最好再把这份指南剩下的内容多读几遍,直到你弄懂了再发文。</p><h3 id="礼多人不怪,而且有时还很有帮助"><a href="#礼多人不怪,而且有时还很有帮助" class="headerlink" title="礼多人不怪,而且有时还很有帮助"></a>礼多人不怪,而且有时还很有帮助</h3><p>彬彬有礼,多用<code>请</code>和<code>谢谢您的关注</code>,或<code>谢谢你的关照</code>。让大家都知道你对他们花时间免费提供帮助心存感激。</p><p>坦白说,这一点并没有比清晰、正确、精准并合法语法和避免使用专用格式重要(也不能取而代之)。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告,而不是那种有礼但含糊的报告。(如果这点让你不解,记住我们是按问题能教给我们什么来评价问题的价值的)</p><p>然而,如果你有一串的问题待解决,客气一点肯定会增加你得到有用回应的机会。</p><p>(我们注意到,自从本指南发布后,从资深黑客那里得到的唯一严重缺陷反馈,就是对预先道谢这一条。一些黑客觉得<code>先谢了</code>意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说<code>先谢了</code>,<strong>然后</strong>事后再对回复者表示感谢,或者换种方式表达感激,譬如用<code>谢谢你的关注</code>或<code>谢谢你的关照</code>。)</p><h3 id="问题解决后,加个简短的补充说明"><a href="#问题解决后,加个简短的补充说明" class="headerlink" title="问题解决后,加个简短的补充说明"></a>问题解决后,加个简短的补充说明</h3><p>问题解决后,向所有帮助过你的人发个说明,让他们知道问题是怎样解决的,并再一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注,应该在那里贴一个说明比较恰当。</p><p>最理想的方式是向最初提问的话题回复此消息,并在标题中包含<code>已修正</code>,<code>已解决</code>或其它同等含义的明显标记。在人来人往的邮件列表里,一个看见讨论串<code>问题 X</code>和<code>问题 X - 已解决</code>的潜在回复者就明白不用再浪费时间了(除非他个人觉得<code>问题 X</code>的有趣),因此可以利用此时间去解决其它问题。</p><p>补充说明不必很长或是很深入;简单的一句<code>你好,原来是网线出了问题!谢谢大家 – Bill</code>比什么也不说要来的好。事实上,除非结论真的很有技术含量,否则简短可爱的小结比长篇大论更好。说明问题是怎样解决的,但大可不必将解决问题的过程复述一遍。</p><p>对于有深度的问题,张贴调试记录的摘要是有帮助的。描述问题的最终状态,说明是什么解决了问题,在此<strong>之后</strong>才指明可以避免的盲点。避免盲点的部分应放在正确的解决方案和其它总结材料之后,而不要将此信息搞成侦探推理小说。列出那些帮助过你的名字,会让你交到更多朋友。</p><p>除了有礼貌和有内涵以外,这种类型的补充也有助于他人在邮件列表/新闻群组/论坛中搜索到真正解决你问题的方案,让他们也从中受益。</p><p>至少,这种补充有助于让每位参与协助的人因问题的解决而从中得到满足感。如果你自己不是技术专家或者黑客,那就相信我们,这种感觉对于那些你向他们求助的大师或者专家而言,是非常重要的。问题悬而未决会让人灰心;黑客们渴望看到问题被解决。好人有好报,满足他们的渴望,你会在下次提问时尝到甜头。</p><p>思考一下怎样才能避免他人将来也遇到类似的问题,自问写一份文件或加个常见问题(FAQ)会不会有帮助。如果是的话就将它们发给维护者。</p><p>在黑客中,这种良好的后继行动实际上比传统的礼节更为重要,也是你如何透过善待他人而赢得声誉的方式,这是非常有价值的资产。</p><h2 id="如何解读答案"><a href="#如何解读答案" class="headerlink" title="如何解读答案"></a>如何解读答案</h2><p><a id="RTFM"></a></p><h3 id="RTFM-和-STFW:如何知道你已完全搞砸了"><a href="#RTFM-和-STFW:如何知道你已完全搞砸了" class="headerlink" title="RTFM 和 STFW:如何知道你已完全搞砸了"></a>RTFM 和 STFW:如何知道你已完全搞砸了</h3><p>有一个古老而神圣的传统:如果你收到<code>RTFM (Read The Fucking Manual)</code>的回应,回答者认为你<strong>应该去读他妈的手册</strong>。当然,基本上他是对的,你应该去读一读。</p><p>RTFM 有一个年轻的亲戚。如果你收到<code>STFW(Search The Fucking Web)</code>的回应,回答者认为你<strong>应该到他妈的网上搜索</strong>。那人多半也是对的,去搜索一下吧。(更温和一点的说法是 <strong><a href="http://lmgtfy.com/" target="_blank" rel="noopener">Google 是你的朋友</a></strong>!)</p><p>在论坛,你也可能被要求去爬爬论坛的旧文。事实上,有人甚至可能热心地为你提供以前解决此问题的讨论串。但不要依赖这种关照,提问前应该先搜索一下旧文。</p><p>通常,用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址,而且他们打这些字的时候也正在读着。这些答复意味着回答者认为</p><ul><li><strong>你需要的信息非常容易获得</strong>;</li><li><strong>你自己去搜索这些信息比灌给你,能让你学到更多</strong>。</li></ul><p>你不应该因此不爽;<strong>依照黑客的标准,他已经表示了对你一定程度的关注,而没有对你的要求视而不见</strong>。你应该对他祖母般的慈祥表示感谢。</p><h3 id="如果还是搞不懂"><a href="#如果还是搞不懂" class="headerlink" title="如果还是搞不懂"></a>如果还是搞不懂</h3><p>如果你看不懂回应,别立刻要求对方解释。像你以前试着自己解决问题时那样(利用手册,FAQ,网络,身边的高手),先试着去搞懂他的回应。如果你真的需要对方解释,记得表现出你已经从中学到了点什么。</p><p>比方说,如果我回答你:<code>看来似乎是 zentry 卡住了;你应该先清除它。</code>,然后,这是一个<strong>很糟的</strong>后续问题回应:<code>zentry 是什么?</code> <strong>好</strong>的问法应该是这样:<code>哦~~~我看过说明了但是只有 -z 和 -p 两个参数中提到了 zentries,而且还都没有清楚的解释如何清除它。你是指这两个中的哪一个吗?还是我看漏了什么?</code></p><h3 id="处理无礼的回应"><a href="#处理无礼的回应" class="headerlink" title="处理无礼的回应"></a>处理无礼的回应</h3><p>很多黑客圈子中看似无礼的行为并不是存心冒犯。相反,它是直接了当,一针见血式的交流风格,这种风格更注重解决问题,而不是使人感觉舒服而却模模糊糊。</p><p>如果你觉得被冒犯了,试着平静地反应。如果有人真的做了出格的事,邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这<strong>没有</strong>发生而你却发火了,那么你发火对象的言语可能在黑客社区中看起来是正常的,而<strong>你</strong>将被视为有错的一方,这将伤害到你获取信息或帮助的机会。</p><p>另一方面,你偶尔真的会碰到无礼和无聊的言行。与上述相反,对真正的冒犯者狠狠地打击,用犀利的语言将其驳得体无完肤都是可以接受的。然而,在行事之前一定要非常非常的有根据。纠正无礼的言论与开始一场毫无意义的口水战仅一线之隔,黑客们自己莽撞地越线的情况并不鲜见。如果你是新手或外人,避开这种莽撞的机会并不高。如果你想得到的是信息而不是消磨时光,这时最好不要把手放在键盘上以免冒险。</p><p>(有些人断言很多黑客都有轻度的自闭症或亚斯伯格综合症,缺少用于润滑人类社会<strong>正常</strong>交往所需的神经。这既可能是真也可能是假的。如果你自己不是黑客,兴许你认为我们脑袋有问题还能帮助你应付我们的古怪行为。只管这么干好了,我们不在乎。我们<strong>喜欢</strong>我们现在这个样子,并且通常对病患标记都有站得住脚的怀疑)。</p><p>Jeff Bigler 的观察总结和这个相关也值得一读 (<strong><a href="http://www.mit.edu/~jcb/tact.html" target="_blank" rel="noopener">tact filters</a></strong>)。</p><p>在下一节,我们会谈到另一个问题,当<strong>你</strong>行为不当时所会受到的<code>冒犯</code>。</p><h2 id="如何避免扮演失败者"><a href="#如何避免扮演失败者" class="headerlink" title="如何避免扮演失败者"></a>如何避免扮演失败者</h2><p>在黑客社区的论坛中有那么几次你可能会搞砸 —— 以本指南所描述到的或类似的方式。而你会在公开场合中被告知你是如何搞砸的,也许攻击的言语中还会带点夹七夹八的颜色。</p><p>这种事发生以后,你能做的最糟糕的事莫过于哀嚎你的遭遇、宣称被口头攻击、要求道歉、高声尖叫、憋闷气、威胁诉诸法律、向其雇主报怨、忘了关马桶盖等等。相反地,你该这么做:</p><p>熬过去,这很正常。事实上,它是有益健康且合理的。</p><p>社区的标准不会自行维持,它们是通过参与者积极而<strong>公开地</strong>执行来维持的。不要哭嚎所有的批评都应该通过私下的邮件传送,它不是这样运作的。当有人评论你的一个说法有误或者提出不同看法时,坚持声称受到个人攻击也毫无益处,这些都是失败者的态度。</p><p>也有其它的黑客论坛,受过高礼节要求的误导,禁止参与者张贴任何对别人帖子挑毛病的消息,并声称<code>如果你不想帮助用户就闭嘴。</code> 结果造成有想法的参与者纷纷离开,这么做只会使它们沦为毫无意义的唠叨与无用的技术论坛。</p><p>夸张的讲法是:你要的是“友善”(以上述方式)还是有用?两个里面挑一个。</p><p>记着:当黑客说你搞砸了,并且(无论多么刺耳)告诉你别再这样做时,他正在为关心<strong>你</strong>和<strong>他的社区</strong>而行动。对他而言,不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢,至少要表现得有点尊严,别大声哀嚎,也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者,就指望别人像对待脆弱的洋娃娃那样对你。</p><p>有时候,即使你没有搞砸(或者只是在他的想像中你搞砸了),有些人也会无缘无故地攻击你本人。在这种情况下,抱怨倒是<strong>真的</strong>会把问题搞砸。</p><p>这些来找麻烦的人要么是毫无办法但自以为是专家的不中用家伙,要么就是测试你是否真会搞砸的心理专家。其它读者要么不理睬,要么用自己的方式对付他们。这些来找麻烦的人在给他们自己找麻烦,这点你不用操心。</p><p>也别让自己卷入口水战,最好不要理睬大多数的口水战 – 当然,这是在你检验它们只是口水战,并且未指出你有搞砸的地方,同时也没有巧妙地将问题真正的答案藏于其后(这也是有可能的)。</p><h2 id="不该问的问题"><a href="#不该问的问题" class="headerlink" title="不该问的问题"></a>不该问的问题</h2><p>以下是几个经典蠢问题,以及黑客没回答时心中所想的:</p><p>问题:<a href="#q1">我能在哪找到 X 程序或 X 资源?</a></p><p>问题:<a href="#q2">我怎样用 X 做 Y?</a></p><p>问题:<a href="#q3">如何设定我的 shell 提示?</a></p><p>问题:<a href="#q4">我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗?</a></p><p>问题:<a href="#q5">我的程序/设定/SQL 语句没有用</a></p><p>问题:<a href="#q6">我的 Windows 电脑有问题,你能帮我吗?</a></p><p>问题:<a href="#q7">我的程序不会动了,我认为系统工具 X 有问题</a></p><p>问题:<a href="#q8">我在安装 Linux(或者 X )时有问题,你能帮我吗?</a></p><p>问题:<a href="#q9">我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?</a></p><hr><p><a id="q1"></a></p><blockquote><p>问题:我能在哪找到 X 程序或 X 资源?</p></blockquote><p>回答:就在我找到它的地方啊,白痴 —— 搜索引擎的那一头。天哪!难道还有人不会用 <a href="http://www.google.com" target="_blank" rel="noopener">Google</a> 吗?</p><p><a id="q2"></a></p><blockquote><p>问题:我怎样用 X 做 Y?</p></blockquote><p>回答:如果你想解决的是 Y ,提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知,也对 Y 要解决的问题糊涂,还被特定形势禁锢了思维。最好忽略这种人,等他们把问题搞清楚了再说。</p><p><a id="q3"></a></p><blockquote><p>问题:如何设定我的 shell 提示??</p></blockquote><p>回答:如果你有足够的智慧提这个问题,你也该有足够的智慧去 <a href="#RTFM">RTFM</a>,然后自己去找出来。</p><p><a id="q4"></a></p><blockquote><p>问题:我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗?</p></blockquote><p>回答:试试看就知道了。如果你试过,你既知道了答案,就不用浪费我的时间了。</p><p><a id="q5"></a></p><blockquote><p>问题:我的{程序/设定/SQL 语句}不工作</p></blockquote><p>回答:这不算是问题吧,我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更有意思的事要做呢。在看到这类问题的时候,我的反应通常不外如下三种</p><ul><li>你还有什么要补充的吗?</li><li>真糟糕,希望你能搞定。</li><li>这关我有什么屁事?</li></ul><p><a id="q6"></a></p><blockquote><p>问题:我的 Windows 电脑有问题,你能帮我吗?</p></blockquote><p>回答:能啊,扔掉微软的垃圾,换个像 Linux 或 BSD 的开源操作系统吧。</p><p>注意:如果程序有官方版 Windows 或者与 Windows 有互动(如 Samba),你<strong>可以</strong>问与 Windows 相关的问题, 只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶, 因为 Windows 一般来说实在太烂,这种说法通常都是对的。</p><p><a id="q7"></a></p><blockquote><p>问题:我的程序不会动了,我认为系统工具 X 有问题</p></blockquote><p>回答:你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库档案有明显缺陷的人,更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据,当你这样声称时,你必须有清楚而详尽的缺陷说明文件作后盾。</p><p><a id="q8"></a></p><blockquote><p>问题:我在安装 Linux(或者 X )时有问题,你能帮我吗?</p></blockquote><p>回答:不能,我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧(你能在<a href="http://www.linux.org/groups/index.html" target="_blank" rel="noopener">这儿</a>找到使用者群组的清单)。</p><p>注意:如果安装问题与某 Linux 的发行版有关,在它的邮件列表、论坛或本地使用者群组中提问也许是恰当的。此时,应描述问题的准确细节。在此之前,先用 <code>Linux</code> 和<strong>所有</strong>被怀疑的硬件作关键词仔细搜索。</p><p><a id="q9"></a></p><blockquote><p>问题:我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?</p></blockquote><p>回答:想要这样做,说明了你是个卑鄙小人;想找个黑客帮你,说明你是个白痴!</p><h2 id="好问题与蠢问题"><a href="#好问题与蠢问题" class="headerlink" title="好问题与蠢问题"></a>好问题与蠢问题</h2><p>最后,我将透过举一些例子,来说明怎样聪明的提问;同一个问题的两种问法被放在一起,一种是愚蠢的,另一种才是明智的。</p><p><strong>蠢问题</strong>:</p><blockquote><p>我可以在哪儿找到关于 Foonly Flurbamatic 的资料?</p></blockquote><p>这种问法无非想得到 <a href="#RTFM">STFW</a> 这样的回答。</p><p><strong>聪明问题</strong>:</p><blockquote><p>我用 Google 搜索过 “Foonly Flurbamatic 2600”,但是没找到有用的结果。谁知道上哪儿去找对这种设备编程的资料?</p></blockquote><p>这个问题已经 STFW 过了,看起来他真的遇到了麻烦。</p><p><strong>蠢问题</strong>:</p><blockquote><p>我从 foo 项目找来的源码没法编译。它怎么这么烂?</p></blockquote><p>他觉得都是别人的错,这个傲慢自大的提问者。</p><p><strong>聪明问题</strong>:</p><blockquote><p>foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ,但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录,我有什么做的不对的地方吗?</p></blockquote><p>提问者已经指明了环境,也读过了 FAQ,还列出了错误,并且他没有把问题的责任推到别人头上,他的问题值得被关注。</p><p><strong>蠢问题</strong>:</p><blockquote><p>我的主机板有问题了,谁来帮我?</p></blockquote><p>某黑客对这类问题的回答通常是:<code>好的,还要帮你拍拍背和换尿布吗?</code>,然后按下删除键。</p><p><strong>聪明问题</strong>:</p><blockquote><p>我在 S2464 主机板上试过了 X 、 Y 和 Z ,但没什么作用,我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking,但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题?</p></blockquote><p>这个家伙,从另一个角度来看,值得去回答他。他表现出了解决问题的能力,而不是坐等天上掉答案。</p><p>在最后一个问题中,注意<code>告诉我答案</code>和<code>给我启示,指出我还应该做什么诊断工作</code>之间微妙而又重要的区别。</p><p>事实上,后一个问题源自于 2001 年 8 月在 Linux 内核邮件列表(lkml)上的一个真实的提问。我(Eric)就是那个提出问题的人。我在 Tyan S2464 主板上观察到了这种无法解释的锁定现象,列表成员们提供了解决这一问题的重要信息。</p><p>通过我的提问方法,我给了别人可以咀嚼玩味的东西;我设法让人们很容易参与并且被吸引进来。我显示了自己具备和他们同等的能力,并邀请他们与我共同探讨。通过告诉他们我所走过的弯路,以避免他们再浪费时间,我也表明了对他们宝贵时间的尊重。</p><p>事后,当我向每个人表示感谢,并且赞赏这次良好的讨论经历的时候, 一个 Linux 内核邮件列表的成员表示,他觉得我的问题得到解决并非由于我是这个列表中的<strong>名</strong>人,而是因为我用了正确的方式来提问。</p><p>黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙;我相信他是对的,如果我<strong>像</strong>个乞讨者那样提问,不论我是谁,一定会惹恼某些人或者被他们忽视。他建议我记下这件事,这直接导致了本指南的出现。</p><h2 id="如果得不到回答"><a href="#如果得不到回答" class="headerlink" title="如果得不到回答"></a>如果得不到回答</h2><p>如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。</p><p>总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。</p><p>你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。</p><p>有许多网上的以及本地的使用者群组,由热情的软件爱好者(即使他们可能从没亲自写过任何软件)组成。通常人们组建这样的团体来互相帮助并帮助新手。</p><p>另外,你可以向很多商业公司寻求帮助,不论公司大还是小。别为要付费才能获得帮助而感到沮丧!毕竟,假使你的汽车发动机汽缸密封圈爆掉了 —— 完全可能如此 —— 你还得把它送到修车铺,并且为维修付费。就算软件没花费你一分钱,你也不能强求技术支持总是免费的。</p><p>对像是 Linux 这种大众化的软件,每个开发者至少会对应到上万名使用者。根本不可能由一个人来处理来自上万名使用者的求助电话。要知道,即使你要为这些协助付费,和你所购买的同类软件相比,你所付出的也是微不足道的(通常封闭源代码软件的技术支持费用比开源软件的要高得多,且内容也没那么丰富)。</p><h2 id="如何更好地回答问题"><a href="#如何更好地回答问题" class="headerlink" title="如何更好地回答问题"></a>如何更好地回答问题</h2><p><strong>态度和善一点</strong>。问题带来的压力常使人显得无礼或愚蠢,其实并不是这样。</p><p><strong>对初犯者私下回复</strong>。对那些坦诚犯错之人没有必要当众羞辱,一个真正的新手也许连怎么搜索或在哪找常见问题都不知道。</p><p><strong>如果你不确定,一定要说出来</strong>!一个听起来权威的错误回复比没有还要糟,别因为听起来像个专家很好玩,就给别人乱指路。要谦虚和诚实,给提问者与同行都树个好榜样。</p><p><strong>如果帮不了忙,也别妨碍他</strong>。不要在实际步骤上开玩笑,那样也许会毁了使用者的设置 —— 有些可怜的呆瓜会把它当成真的指令。</p><p><strong>试探性的反问以引出更多的细节</strong>。如果你做得好,提问者可以学到点东西 —— 你也可以。试试将蠢问题转变成好问题,别忘了我们都曾是新手。</p><p>尽管对那些懒虫抱怨一声 RTFM 是正当的,能指出文件的位置(即使只是建议个 Google 搜索关键词)会更好。</p><p><strong>如果你决定回答,就请给出好的答案</strong>。当别人正在用错误的工具或方法时别建议笨拙的权宜之计(wordaround),应推荐更好的工具,重新界定问题。</p><p><strong>正面的回答问题</strong>!如果这个提问者已经很深入的研究而且也表明已经试过 X 、 Y 、 Z 、 A 、 B 、 C 但没得到结果,回答 <code>试试看 A 或是 B</code> 或者 <code>试试 X 、 Y 、 Z 、 A 、 B 、 C</code> 并附上一个链接一点用都没有。</p><p><strong>帮助你的社区从问题中学习</strong>。当回复一个好问题时,问问自己<code>如何修改相关文件或常见问题文件以免再次解答同样的问题?</code>,接着再向文件维护者发一份补丁。</p><p>如果你是在研究一番后才做出的回答,<strong>展现你的技巧而不是直接端出结果</strong>。毕竟<code>授人以鱼不如授人以渔</code>。</p><h2 id="相关资源"><a href="#相关资源" class="headerlink" title="相关资源"></a>相关资源</h2><p>如果你需要个人电脑、Unix 系统和网络如何运作的基础知识,参阅 <a href="http://en.tldp.org/HOWTO/Unix-and-Internet-Fundamentals-HOWTO/" target="_blank" rel="noopener">Unix 系统和网络基本原理</a>。</p><p>当你发布软件或补丁时,试着按<a href="http://en.tldp.org/HOWTO/Software-Release-Practice-HOWTO/index.html" target="_blank" rel="noopener">软件发布实践</a>操作。</p><h2 id="鸣谢"><a href="#鸣谢" class="headerlink" title="鸣谢"></a>鸣谢</h2><p>Evelyn Mitchel 贡献了一些愚蠢问题例子并启发了编写<code>如何更好地回答问题</code>这一节, Mikhail Ramendik 贡献了一些特别有价值的建议和改进。</p></div>]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>Congratulations on your graduation,My Senior.</title>
<link href="/2020/06/23/Happygraduation/"/>
<url>/2020/06/23/Happygraduation/</url>
<content type="html"><![CDATA[<div class="markdown-body">ご卒業おめでとうございます<a id="more"></a><h1 id="祝贺学长成功毕业"><a href="#祝贺学长成功毕业" class="headerlink" title="祝贺学长成功毕业"></a>祝贺学长成功毕业</h1><p>首先,歌曲送祝福。</p><iframe frameborder="yes" border="100" marginwidth="500" marginheight="100" width=600 height=56 src="/img/2020_06_23/One.mp3"></iframe><p>很荣幸认识学长,希望学长能够继续有长久的发展,献花,献花。</p><p>大学以来认识的最厉害的<a href="https://haoyuan.info/" target="_blank" rel="noopener">学长</a>,没有之一。</p><p>流年笑掷,未来可期。</p></div>]]></content>
<categories>
<category>我自己</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>Rapidminer 中文分词&&去除停用词</title>
<link href="/2020/06/14/rapidminer-chinese/"/>
<url>/2020/06/14/rapidminer-chinese/</url>
<content type="html"><![CDATA[<div class="markdown-body">Rapidminer 利用python插件替代原有只支持英文的功能进行中文分词&&去除停用词<a id="more"></a><h1 id="Rapidminer-中文分词-amp-amp-去除停用词"><a href="#Rapidminer-中文分词-amp-amp-去除停用词" class="headerlink" title="Rapidminer 中文分词&&去除停用词"></a>Rapidminer 中文分词&&去除停用词</h1><h2 id="First-打开Rapidminer新建项目"><a href="#First-打开Rapidminer新建项目" class="headerlink" title="First: 打开Rapidminer新建项目"></a>First: 打开Rapidminer新建项目</h2><p><img src="/img/2020_06_14/first_step.jpg" srcset="/img/loading.gif" alt="Fisrt"></p><p>首先打开你的<code>Rapidminer</code>,选择新建<code>Blank</code>项目。</p><h2 id="Second-导入数据集-amp-amp-导入停用词列表"><a href="#Second-导入数据集-amp-amp-导入停用词列表" class="headerlink" title="Second: 导入数据集&&导入停用词列表"></a>Second: 导入数据集&&导入停用词列表</h2><p class="note note-warning">导入的数据必须是清洗过后的数据,不要有脏数据。</p><p><a href="https://www.zhihu.com/question/22403549/answer/645506708" target="_blank" rel="noopener">如何清洗数据?</a></p><p><img src="/img/2020_06_14/second_step.jpg" srcset="/img/loading.gif" alt="second"></p><p><code>File</code>,点击<code>Import Data</code>选项。会出现下面画面:</p><p><img src="/img/2020_06_14/third_step.jpg" srcset="/img/loading.gif" alt="third"></p><p>因为我们是要从本地导入数据集,所以我们选择<code>My Computer</code>。会出现下面画面:</p><p><img src="/img/2020_06_14/Fourth_step.jpg" srcset="/img/loading.gif" alt="fourth"></p><p>我们只需要找到我们想导入的数据集,然后点击<code>next</code>。这时候会出现以下画面:</p><p><img src="/img/2020_06_14/Fifth_step.jpg" srcset="/img/loading.gif" alt="fifth"></p><p><strong>注意事项:<code>File Encoding</code>必须要选择<code>UTF-8</code>。<code>Header Row</code>根据实际情况去选择,一般数据集第一行为<code>Header</code>。</strong></p><p>之后我们点击<code>next</code>。这时候会出现以下画面:</p><p><img src="/img/2020_06_14/sixth_step.jpg" srcset="/img/loading.gif" alt="sixth"></p><p><strong>注意事项,右上角<code>Replace errors with missing values</code>是处理数据错误情况的,他是用<code>None</code>值填充的。以下过程必须保证你数据是被清洗过后的。</strong></p><p>之后我们点击<code>next</code>,会出现下面界面:</p><p><img src="/img/2020_06_14/seventh_step.jpg" srcset="/img/loading.gif" alt="seven"></p><p>这里填写的是你的保存数据的地址,你只需要保证你保存后的地址能够找到即可。之后我们点击<code>Finish</code>。便可以完成了数据的导入。</p><p>同理我们导入停用词列表,原理方法一样。</p><h2 id="Third-构建整个流程"><a href="#Third-构建整个流程" class="headerlink" title="Third: 构建整个流程"></a>Third: 构建整个流程</h2><p>回到主界面,然后找到你导入的数据集,将其拖入主界面。</p><p><img src="/img/2020_06_14/eighth_step.jpg" srcset="/img/loading.gif" alt="eight"></p><p>之后在左下角找到<code>Nominal to Text</code>这个<code>Operators</code>。并将其拖入主界面,将第一个控件的<code>out</code>连接这个控件的<code>exa</code>。</p><p><img src="/img/2020_06_14/ninth_step.jpg" srcset="/img/loading.gif" alt="nine"></p><p><img src="/img/2020_06_14/tenth_step.jpg" srcset="/img/loading.gif" alt="ten"></p><p>之后我们在搜索<code>Process Document from Data</code>,将其拖入主界面,然后将<code>Nominal to Text</code>控件的<code>exa</code>连接<code>Process Document from Data</code>控件的exa。</p><p><img src="/img/2020_06_14/eleventh_step.jpg" srcset="/img/loading.gif" alt="eleven"></p><p>这时候我们可以点击<code>Process Document from Data</code>,在右边可以看到这个控件的功能。<code>vector creation</code>我们目前需要选择<code>TF-IDF</code>,剩下都默认就好。</p><p><img src="/img/2020_06_14/12.jpg" srcset="/img/loading.gif" alt="12"></p><p>这时候我们在双击<code>Process Document from Data</code>,进入这个控件里面。会出现以下画面:</p><p><img src="/img/2020_06_14/13.jpg" srcset="/img/loading.gif" alt="13"></p><p>这时候在搜索<code>Document to Data</code>,并将其拖入主屏幕,点击这个控件,<strong>在右边必须填写两个不相等的值。</strong>目前我填写的是<code>text1</code>和<code>text2</code>。</p><p><img src="/img/2020_06_14/14.jpg" srcset="/img/loading.gif" alt="14"></p><p>之后我们在搜索<code>python</code>,将其拖入到主界面里。将<code>Documen to Data</code>的<code>exa</code>连接到<code>python</code>的第一个<code>inp</code>接口。如下图所示。</p><p><img src="/img/2020_06_14/15.jpg" srcset="/img/loading.gif" alt="15"></p><p>之后点击<code>Execute Python</code>,在右侧点击<code>Edit Text</code>,如下图所示。</p><p><img src="/img/2020_06_14/16.jpg" srcset="/img/loading.gif" alt="16"></p><p>之后导入下面的代码(中文分词操作):</p><pre><code class="hljs python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd<span class="hljs-keyword">import</span> jieba<span class="hljs-keyword">import</span> jieba.analyse<span class="hljs-comment">#里面很多无关紧要的代码,但是可以测试用。</span><span class="hljs-comment"># rm_main is a mandatory function,</span><span class="hljs-comment"># the number of arguments has to be the number of input ports (can be none)</span><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">rm_main</span><span class="hljs-params">(data)</span>:</span> <span class="hljs-comment"># output can be found in Log View</span> segments = [] <span class="hljs-comment">#功能留用</span> framedata = [] <span class="hljs-keyword">for</span> index, row <span class="hljs-keyword">in</span> data.iterrows(): content=row[<span class="hljs-number">0</span>] words = jieba.cut(content) splitedStr = <span class="hljs-string">''</span> <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> words: <span class="hljs-comment">#停用词判断,如果当前的关键词不在停用词库中才进行记录</span> segments.append({<span class="hljs-string">'word'</span>:word, <span class="hljs-string">'count'</span>:<span class="hljs-number">1</span>})<span class="hljs-comment">#可有可无</span> splitedStr += word + <span class="hljs-string">' '</span> framedata.append({<span class="hljs-string">'splited word'</span>:splitedStr}) Sgdata = pd.DataFrame(framedata) <span class="hljs-comment">#your code goes here</span> list1 = [] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(Sgdata)): lis = Sgdata.iloc[i][<span class="hljs-number">0</span>].strip().split(<span class="hljs-string">' '</span>) list1.append(lis) <span class="hljs-comment"># connect 2 output ports to see the results</span> Sgdata = pd.DataFrame(list1) <span class="hljs-keyword">return</span> Sgdata</code></pre><p>之后拖入我们所需要的停用词列表数据到主界面,并导入另一个<code>python</code>控件,将第一个<code>python</code>控件的<code>out</code>连接第二个<code>python</code>脚本的第一个<code>inp</code>接口,将停用词的<code>out</code>连接第二个<code>python</code>脚本的第二个<code>inp</code>接口。如下图所示:</p><p><img src="/img/2020_06_14/17.jpg" srcset="/img/loading.gif" alt="17"></p><p>在第二个<code>python</code>控件导入以下代码(去除停用词):</p><pre><code class="hljs python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">rm_main</span><span class="hljs-params">(data,data1)</span>:</span> <span class="hljs-comment"># output can be found in Log View</span> listdata = np.array(data1) stopwords = listdata.tolist() segments = [] <span class="hljs-comment"># 功能留用</span> col=k=data.shape[<span class="hljs-number">1</span>] My_content=[] framedata = [] j = <span class="hljs-number">0</span> <span class="hljs-comment"># 记录列数</span> <span class="hljs-keyword">for</span> index, row <span class="hljs-keyword">in</span> data.iterrows(): My_content.append([]) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>,k): content=row[i] My_content[j].append(content) splitedStr = <span class="hljs-string">''</span> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>,col): stop = [] <span class="hljs-keyword">if</span> My_content[j][l]!=<span class="hljs-literal">None</span>: stop.append(My_content[j][l]) <span class="hljs-keyword">if</span> stop <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> stopwords: <span class="hljs-comment"># 记录全局分词</span> segments.append({<span class="hljs-string">'word'</span>: My_content[j][l], <span class="hljs-string">'count'</span>: <span class="hljs-number">1</span>}) splitedStr += My_content[j][l] + <span class="hljs-string">' '</span> j = j + <span class="hljs-number">1</span> framedata.append({<span class="hljs-string">'splited word'</span>: splitedStr}) Sgdata = pd.DataFrame(framedata) <span class="hljs-comment"># your code goes here</span> list1 = [] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(Sgdata)): lis = Sgdata.iloc[i][<span class="hljs-number">0</span>].strip().split(<span class="hljs-string">' '</span>) list1.append(lis) <span class="hljs-comment"># connect 2 output ports to see the results</span> Sgdata = pd.DataFrame(list1) <span class="hljs-keyword">return</span> Sgdata</code></pre><p>之后我们在搜索<code>Nominal to Text</code>这个<code>Operators</code>。并将其拖入主界面,将第二个<code>python</code>控件的<code>out</code>连接这个控件的<code>exa</code>。如下图所示:</p><p><img src="/img/2020_06_14/18.jpg" srcset="/img/loading.gif" alt="18"></p><p>之后我们在搜索<code>Data to Document</code>这个<code>Operators</code>。并将其拖入主界面,将<code>Nominal to Text</code>控件右侧的<code>exa</code>连接这个控件的<code>exa</code>,将<code>Data to Document</code>右侧的<code>doc</code>接口连接右上角的<code>doc</code>接口。如下图所示:</p><p><img src="/img/2020_06_14/19.jpg" srcset="/img/loading.gif" alt="19"></p><p>这时候我们里面的全部流程就做完了。</p><p>下面我们回到主界面。</p><p><img src="/img/2020_06_14/20.jpg" srcset="/img/loading.gif" alt="20"></p><p>之后我们在搜索<code>K-means</code>这个<code>Operators</code>。并将其拖入主界面,并按照如图所示连接所有的线。如下图所示:</p><p><img src="/img/2020_06_14/21.jpg" srcset="/img/loading.gif" alt="21"></p><p>我们可以点击<code>Clustering</code>这个控件,然后在右侧设置我们想设置的数值。</p><p><img src="/img/2020_06_14/22.jpg" srcset="/img/loading.gif" alt="22"></p><h2 id="Fourth-运行整个流程"><a href="#Fourth-运行整个流程" class="headerlink" title="Fourth: 运行整个流程"></a>Fourth: 运行整个流程</h2><p>点击在左上角的蓝色小箭头即可运行您的项目,稍等片刻,即可出现所有的结果。</p><p><img src="/img/2020_06_14/23.jpg" srcset="/img/loading.gif" alt="23"></p><p>那么我们大功告成!</p></div>]]></content>
<categories>
<category>rapidminer插件应用</category>
</categories>
<tags>
<tag>教程</tag>
</tags>
</entry>
<entry>
<title>Rosette文本扩展介绍及安装</title>
<link href="/2020/06/04/rapidminer-textmining/"/>
<url>/2020/06/04/rapidminer-textmining/</url>
<content type="html"><![CDATA[<div class="markdown-body">文本分析五部曲之1-Rosette文本扩展介绍及安装<a id="more"></a><h1 id="Rosette文本扩展介绍及安装"><a href="#Rosette文本扩展介绍及安装" class="headerlink" title="Rosette文本扩展介绍及安装"></a>Rosette文本扩展介绍及安装</h1><h2 id="引言-什么是Rosette"><a href="#引言-什么是Rosette" class="headerlink" title="引言:什么是Rosette?"></a>引言:什么是Rosette?</h2><p>Rosette,<strong>作为RapidMiner的文本挖掘扩展</strong>,是市场上最广泛使用的多语言信息提取解决方案。</p><p>Rosette®文本分析是一个强大的工具包,可用于处理<strong>多语言</strong>,<strong>文档</strong>和<strong>名称</strong>等。</p><p>在政府,金融,电子商务,搜索,社交媒体等领域的全球客户依赖Rosette文本分析来分析大量数据。百分之八十的大数据作为非结构化文本存在,非结构化文本也是多语言的,Rosette为企业提供巨大的价值,能够将<strong>非结构化文本转换为可行的见解</strong>,<strong>验证身份</strong>和<strong>防止灾难</strong>等。</p><p>整合了 Rosette TextToolkit 的 RapidMiner Studio,我们就可以处理任意的文本挖掘任务了!您也可以尝试不同的算子,包括<strong>提取文本,</strong>分类,情感分析,形态分析,分词,句标记,名称翻译和名称匹配等。</p><p><img src="/img/2020_06_04/1.jpg" srcset="/img/loading.gif" alt="1"></p><h2 id="First-下载扩展工具"><a href="#First-下载扩展工具" class="headerlink" title="First:下载扩展工具"></a>First:下载扩展工具</h2><p>打开RapidMiner Studio,导航到<strong>扩展(Extensions)</strong>菜单并在下拉菜单中选择<strong>应用市场(Marketplace)</strong>。</p><p>之后将打开一个新窗口,请在搜索栏目中搜索“rosette”,然后从结果列表中选择Rosette Text Toolkit。单击窗口底部的“<strong>Install 1 Packages</strong>”按钮,然后按照点击后说明完成安装。</p><p><img src="/img/2020_06_04/2.jpg" srcset="/img/loading.gif" alt="2"></p><p>一旦扩展完成安装,Rosette扩展将会在<strong>算子(Operators)</strong>操作面板的<strong>扩展文件夹</strong>中可见。</p><p><img src="/img/2020_06_04/3.jpg" srcset="/img/loading.gif" alt="3"></p><h2 id="Second-获取RosetteAPI密钥"><a href="#Second-获取RosetteAPI密钥" class="headerlink" title="Second:获取RosetteAPI密钥"></a>Second:获取RosetteAPI密钥</h2><p>为了激活用于RapidMiner Studio的Rosette文本工具包,您需要一个API密钥和一个Rosette开发人员帐户。进入如下网址<a href="https://developer.rosette.com/" target="_blank" rel="noopener"><strong>developer.rosette.com</strong></a>并完成注册。</p><p class="note note-warning">建议绑定github,既方便又好使。</p><p><img src="/img/2020_06_04/4.jpg" srcset="/img/loading.gif" alt="4"></p><p>您可以创建一个链接到您的电子邮件或GitHub帐户,不需要信用卡 – Rosette 默认提供<strong>每天10,000条免费调用</strong>!超过1万条需要付费。</p><p>PS:注册邮箱为企业邮箱,推荐学生邮箱。<a href="http://mail.student.cup.edu.cn/" target="_blank" rel="noopener">进入CUP学生邮箱</a></p><p><img src="/img/2020_06_04/5.jpg" srcset="/img/loading.gif" alt="5"></p><p>完成注册并验证您的帐户后,点击菜单栏左上角的<strong>API密钥</strong>标签以显示您的密钥。</p><p><img src="/img/2020_06_04/6.jpg" srcset="/img/loading.gif" alt="6"></p><h2 id="Third-设置RosetteAPI连接"><a href="#Third-设置RosetteAPI连接" class="headerlink" title="Third:设置RosetteAPI连接"></a>Third:设置RosetteAPI连接</h2><p>返回RapidMiner Studio,输入您的Rosette API密钥后就可以开始使用所有的Rosette算子。下一部分将会讲解,如何实体操作中查看提取算子,并使用它来设置Rosette API连接。</p><p>首先,在<strong>算子“Operators”</strong>面板的Rosette Text Toolkit文件夹中找到<strong>文本提取“Extract Entities”</strong>,并将其拖动到<strong>流程“Process”</strong>面板。</p><p><img src="/img/2020_06_04/7.jpg" srcset="/img/loading.gif" alt="7"></p><p>您可以在<strong>流程“Process”</strong>面板右侧的<strong>参数“Parameters”</strong>面板中查看文本提取“Extract Entities”算子的各种参数设置选项。第一个参数是<strong>连接“Connection”</strong>。 单击框右侧的Rosette图标。</p><p><img src="/img/2020_06_04/8.jpg" srcset="/img/loading.gif" alt="8"></p><p> 打开<strong>管理连接“Manage Connections”</strong>窗口。 单击左下角的<strong>添加连接“AddConnection”</strong>按钮,然后从 <strong>连接类型“Connection type”</strong> 下拉列表中选择<strong>”Rosette Connection”</strong>。 为新连接命名,然后单击<strong>创建“Create”</strong>按钮。</p><p><img src="/img/2020_06_04/9.jpg" srcset="/img/loading.gif" alt="9"></p><p>从左侧列表中选择新的“Rosette API”连接,然后在API KEY框中输入Rosette API密钥。 使用窗口底部的<strong>测试“Test”</strong>按钮来验证连接是否正常工作。 如果遇到问题,请确认API密钥是否正确。当确信一切运行顺利时,单击<strong>保存所有更改按钮“Save all changes”</strong>以返回到<strong>参数“Parameters”</strong>面板。</p><p><img src="/img/2020_06_04/10.jpg" srcset="/img/loading.gif" alt="10"></p><p>从连接<strong>Connection</strong>下拉列表中选择您的新连接。</p><p><img src="/img/2020_06_04/11.jpg" srcset="/img/loading.gif" alt="11"></p><p>现在您已设置好Rosette连接了。</p></div>]]></content>
<categories>
<category>rapidminer插件</category>
</categories>
<tags>
<tag>教程</tag>
</tags>
</entry>
<entry>
<title>RAPIDMINER 教育版申请及下载</title>
<link href="/2020/06/03/DownLoad-repidminer/"/>
<url>/2020/06/03/DownLoad-repidminer/</url>
<content type="html"><![CDATA[<div class="markdown-body">REPIDMINER 软件下载教程---WEB实验<a id="more"></a><h1 id="RAPIDMINER-教育版申请"><a href="#RAPIDMINER-教育版申请" class="headerlink" title="RAPIDMINER 教育版申请"></a>RAPIDMINER 教育版申请</h1><hr><p><a href="https://rapidminer.com/get-started-educational/" target="_blank" rel="noopener">申请页面</a></p><p>填入信息,<code>course</code>和<code>course term</code>可以瞎填。其他如实填写。</p><p>信息管理与信息系统专业:<code>Information Management and Information System</code></p><p>中国石油大学(北京):<code>China University of Petroleum,Beijing</code></p><p><img src="/img/GetTheStudentSoftWare.jpg" srcset="/img/loading.gif" alt="页面"></p><h2 id="First-登录学生邮箱"><a href="#First-登录学生邮箱" class="headerlink" title="First:登录学生邮箱"></a>First:登录学生邮箱</h2><p>登录<a href="http://mail.student.cup.edu.cn/" target="_blank" rel="noopener">中国石油大学(北京)学生邮箱</a>,默认用户名为学号(例如2017010001),默认密码为生日(例如20000101),第一次登录后需要修改密码,请务必牢记修改后的密码。</p><p><img src="/img/image2.png" srcset="/img/loading.gif" alt="界面2"></p><p><strong>!忘记密码不要担心!登录微信,找中国石油大学企业号,找邮箱密码修改即可!</strong></p><p><img src="/img/image-20200603184458425.png" srcset="/img/loading.gif" alt="image-20200603184458425"></p><h2 id="Second-注册RAPIDMINER账号"><a href="#Second-注册RAPIDMINER账号" class="headerlink" title="Second:注册RAPIDMINER账号"></a>Second:注册RAPIDMINER账号</h2><p><a href="https://my.rapidminer.com/nexus/account/index.html#signup?returnUrl=https%3A%2F%2Fmy.rapidminer.com%2Fnexus%2Faccount%2Findex.html%23home" target="_blank" rel="noopener">注册网址</a></p><p><img src="/img/1591181675(1).jpg" srcset="/img/loading.gif" alt="Regist"></p><p><strong>一定要选择Educational!</strong></p><p>且你得email address一定要是你的学生邮箱:<code>学号@student.cup.edu.cn</code></p><p>注册完毕以后,进入学生邮箱。</p><p><img src="/img/1591181916(1).jpg" srcset="/img/loading.gif" alt="verify"></p><p><img src="/img/1591181989(1).jpg" srcset="/img/loading.gif" alt="verify2"></p><p>Finished!</p><h2 id="Third-下载RAPIDMINER"><a href="#Third-下载RAPIDMINER" class="headerlink" title="Third:下载RAPIDMINER"></a>Third:下载RAPIDMINER</h2><p>找到Download页面。点击即可下载。</p><p><img src="/img/1591182167(1).jpg" srcset="/img/loading.gif" alt="下载"></p><p>以下是Win 64其他辅助用具</p><ul><li><a href="https://redirects.rapidminer.com/web/studio/installation-guide" target="_blank" rel="noopener">Installation Guide</a></li><li><a href="https://redirects.rapidminer.com/web/studio/getting-started-tutorials" target="_blank" rel="noopener">Getting Started Tutorials</a></li><li><a href="https://rapidminer.com/support/" target="_blank" rel="noopener">Support</a></li><li><a href="https://redirects.rapidminer.com/web/download-source" target="_blank" rel="noopener">Download Source</a></li></ul><p>或者直接联系Slian,传送文件。(要文件可以,不给账号,自己申请)</p><p>安装过程基本上一路next。</p><h2 id="Forth-打开软件开通一年教育版使用权限"><a href="#Forth-打开软件开通一年教育版使用权限" class="headerlink" title="Forth:打开软件开通一年教育版使用权限"></a>Forth:打开软件开通一年教育版使用权限</h2><p>安装完之后会自动生成一个桌面文件,打开。</p><p><img src="/img/1591182537(1).jpg" srcset="/img/loading.gif" alt="Open"></p><p>打开之后会让你登录你的账号,填入你申请的教育版账号。</p><p><img src="/img/1591183007(1).jpg" srcset="/img/loading.gif" alt="open3"></p><p>大功告成。<br><img src="/img/1591183115(1).jpg" srcset="/img/loading.gif" alt="open4"></p></div>]]></content>
<categories>
<category>下载</category>
</categories>
<tags>
<tag>教程</tag>
</tags>
</entry>
<entry>
<title>Hello My 21 years old.</title>
<link href="/2020/05/18/Hello%20My%2021/"/>
<url>/2020/05/18/Hello%20My%2021/</url>
<content type="html"><![CDATA[<div class="markdown-body">Slian 21岁了!<a id="more"></a><h1 id="Hello-My-Bro"><a href="#Hello-My-Bro" class="headerlink" title="Hello My Bro"></a>Hello My Bro</h1><p>想对自己说一声,希望今后能够每样多懂一点点。</p><p>感谢每一个帮助过我的人,有幸相识,有幸相聚。</p><p>Best Wishes.</p></div>]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>我自己</tag>
</tags>
</entry>
</search>