-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
299 lines (142 loc) · 102 KB
/
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Interview-250212</title>
<link href="/2025/02/12/Interview-250212/"/>
<url>/2025/02/12/Interview-250212/</url>
<content type="html"><![CDATA[<ol><li>判断一个unsigned char有多少个bit为1;</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">count_set_bits</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> byte)</span></span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span>(byte){</span><br><span class="line">count += byte & <span class="number">1</span>;</span><br><span class="line">byte >>= <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> count;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="2"><li>在一个字符串中查找匹配的子串,返回匹配到位置;</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">find_sub_string</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">const</span> <span class="type">char</span> *sub)</span></span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> str_len = <span class="built_in">strlen</span>(str);</span><br><span class="line"><span class="type">int</span> sub_str_len = <span class="built_in">strlen</span>(sub);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i <= str_len - sub_str_len; ++i)</span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> j;</span><br><span class="line"><span class="keyword">for</span>(j = <span class="number">0</span>; j < sub_str_len; ++j)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(str[i + j] != substr[j])</span><br><span class="line">{</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(j == sub_str_len)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> i;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="3"><li>双向链表删除节点</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Node</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> *<span class="title">prev</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> *<span class="title">next</span>;</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> <span class="title">Node</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">delete_node</span><span class="params">(Node **head, <span class="type">int</span> val)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span>(*head == <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"empty\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Node* current = *head;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(current != <span class="literal">NULL</span> && current->data != val)</span><br><span class="line"> {</span><br><span class="line"> current = current->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (current == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"未找到值为 %d 的节点。\n"</span>, val);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果删除的是头节点</span></span><br><span class="line"> <span class="keyword">if</span> (current->prev == <span class="literal">NULL</span>) {</span><br><span class="line"> *head = current->next; <span class="comment">// 更新头节点</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> current->prev->next = current->next; <span class="comment">// 更新前一个节点的 next 指针</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果删除的不是尾节点</span></span><br><span class="line"> <span class="keyword">if</span> (current->next != <span class="literal">NULL</span>) {</span><br><span class="line"> current->next->prev = current->prev; <span class="comment">// 更新后一个节点的 prev 指针</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(current); <span class="comment">// 释放内存</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"成功删除值为 %d 的节点。\n"</span>, val);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>4.hash表的常见应用<br>字典<br>缓存<br>数据库索引<br>唯一性检查</p><p>hash冲突的解决方法:</p><ol><li><strong>链地址法(Chaining)</strong></li><li><strong>开放地址法(Open Addressing)</strong></li><li><strong>再哈希法(Rehashing)</strong></li></ol><p>tips:作缓存时,发生冲突直接覆盖即可</p><p>5.MYSQL的CAPI<br>mysql_init()初始化连接<br>mysql_real_connect()简历连接<br>mysql_query() 执行SQL语句<br>mysql_store_result() 和 mysql_fetch_row() 等函数来处理结果</p><p>5.Redis的常用数据结构<br>STRING,LIST,ZSET,SET,HASH</p>]]></content>
<categories>
<category> Interview </category>
</categories>
</entry>
<entry>
<title>Happy CNY!!!</title>
<link href="/2025/01/29/Happy-CNY/"/>
<url>/2025/01/29/Happy-CNY/</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>A distributed version control tool:Git</title>
<link href="/2024/11/19/A-distributed-version-control-tool-Git/"/>
<url>/2024/11/19/A-distributed-version-control-tool-Git/</url>
<content type="html"><![CDATA[<p>History of Git:</p>]]></content>
<categories>
<category> Tools </category>
</categories>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Some tips for tmux</title>
<link href="/2024/10/18/Some-tips-for-tmux/"/>
<url>/2024/10/18/Some-tips-for-tmux/</url>
<content type="html"><![CDATA[<ul><li>Tmux<ul><li>session<ul><li>Ctrl+b d:分离当前会话。</li><li>Ctrl+b s:列出所有会话。</li><li>Ctrl+b $:重命名当前会话。</li></ul></li><li>pane<ul><li>Ctrl+b %:划分左右两个窗格。</li><li>Ctrl+b “:划分上下两个窗格。</li><li>Ctrl+b <code><arrow key></code>:光标切换到其他窗格。<code><arrow key></code>是指向要切换到的窗格的方向键,比如切换到下方窗格,就按方向键↓。</li><li>Ctrl+b ;:光标切换到上一个窗格。</li><li>Ctrl+b o:光标切换到下一个窗格。</li><li>Ctrl+b {:当前窗格与上一个窗格交换位置。</li><li>Ctrl+b }:当前窗格与下一个窗格交换位置。</li><li>Ctrl+b Ctrl+o:所有窗格向前移动一个位置,第一个窗格变成最后一个窗格。</li><li>Ctrl+b Alt+o:所有窗格向后移动一个位置,最后一个窗格变成第一个窗格。</li><li>Ctrl+b x:关闭当前窗格。</li><li>Ctrl+b !:将当前窗格拆分为一个独立窗口。</li><li>Ctrl+b z:当前窗格全屏显示,再使用一次会变回原来大小。</li><li>Ctrl+b Ctrl+<code><arrow key></code>:按箭头方向调整窗格大小。</li><li>Ctrl+b q:显示窗格编号。</li></ul></li><li>window<ul><li>Ctrl+b c:创建一个新窗口,状态栏会显示多个窗口的信息。</li><li>Ctrl+b p:切换到上一个窗口(按照状态栏上的顺序)。</li><li>Ctrl+b n:切换到下一个窗口。</li><li>Ctrl+b <code><number></code>:切换到指定编号的窗口,其中的 <code><number></code>是状态栏上的窗口编号。</li><li>Ctrl+b w:从列表中选择窗口。</li><li>Ctrl+b ,:窗口重命名。</li></ul></li></ul></li></ul>]]></content>
<categories>
<category> Tools </category>
</categories>
<tags>
<tag> Tmux </tag>
</tags>
</entry>
<entry>
<title>essays</title>
<link href="/2024/10/18/essays/"/>
<url>/2024/10/18/essays/</url>
<content type="html"><![CDATA[<h1 id="Github-Gitee-Page"><a href="#Github-Gitee-Page" class="headerlink" title="Github/Gitee Page"></a>Github/Gitee Page</h1><p>GitHub Pages 是一个由 GitHub 提供的静态网站托管服务,它允许用户直接从 GitHub 仓库托管静态网页。这些网页可以是个人简历、项目文档、博客、电子书籍、幻灯片演示文稿等。GitHub Pages 非常适合托管不需要服务器端处理的静态内容,因为它提供免费的托管服务,并且与 GitHub 仓库紧密集成。</p><p>以下是 GitHub Pages 的一些主要特点:</p><p><strong>易于使用</strong> :</p><ul><li>无需配置复杂的服务器或数据库,只需将静态网页文件推送到 GitHub 仓库,GitHub Pages 就会自动为你托管网站。</li><li>支持直接通过 GitHub 仓库的默认分支(通常是 <code>main</code> 或 <code>master</code>)托管网站,或者通过 <code>gh-pages</code> 分支托管。</li></ul><p><strong>自定义域名</strong> :</p><ul><li>你可以为你的 GitHub Pages 网站配置自定义域名,使其更专业。</li></ul><p><strong>HTTPS 支持</strong> :</p><ul><li>GitHub Pages 提供自动 HTTPS 支持,确保你的网站内容通过加密传输。</li></ul><p><strong>CDN 和缓存</strong> :</p><ul><li>GitHub Pages 使用全球 CDN(内容分发网络)来加速内容分发,并且自动缓存静态资源以提高加载速度。</li></ul><p><strong>Jekyll 集成</strong> :</p><ul><li>GitHub Pages 与 Jekyll 静态站点生成器集成,使得你可以使用 Jekyll 生成静态网页,然后托管在 GitHub Pages 上。</li></ul><p><strong>版本控制</strong> :</p><ul><li>你的网站内容存储在 GitHub 仓库中,这意味着你可以利用 Git 的版本控制功能来管理你的网站内容。</li></ul><p><strong>免费的</strong> :</p><ul><li>对于公共仓库,GitHub Pages 提供的托管服务是完全免费的。</li></ul><p><strong>易于分享和协作</strong> :</p><ul><li>由于网站内容存储在 GitHub 上,你可以轻松地与他人分享和协作网站内容。</li></ul><p><strong>实时更新</strong> :</p><ul><li>每当你推送新的更改到 GitHub 仓库时,GitHub Pages 会自动更新你的网站。</li></ul><p>要开始使用 GitHub Pages,你只需要创建一个 GitHub 仓库,将你的静态网页文件(如 HTML、CSS、JavaScript 文件等)推送到仓库中,然后按照 GitHub 的指引配置你的仓库以启用 GitHub Pages 功能。对于使用 Jekyll 的用户,过程也类似,只是你需要在仓库中包含 Jekyll 站点的配置文件和内容。</p><p>GitHub Pages 是一个强大的工具,特别适合开发者和小型团队托管静态网站,以及个人创建个人网站或博客。</p><h1 id="tools"><a href="#tools" class="headerlink" title="tools:"></a>tools:</h1><ul><li><a href="https://fishshell.com/">fish</a></li><li><a href="https://github.com/tmux/tmux/wiki">tmux</a></li></ul><p><code>fish</code> 是一种智能且用户友好的命令行 shell,它旨在提供更加人性化的用户体验。以下是 <code>fish</code> 的一些特点:</p><p><strong>自动建议</strong> :</p><ul><li><code>fish</code> 提供了强大的命令自动补全功能,它根据上下文环境智能地提示命令和参数。</li></ul><p><strong>语法高亮</strong> :</p><ul><li>它能够对输入的命令进行语法高亮显示,帮助用户快速发现错误。</li></ul><p><strong>历史搜索</strong> :</p><ul><li><code>fish</code> 提供了一种改进的历史搜索功能,允许用户以自然语言的方式搜索命令历史。</li></ul><p><strong>定制性</strong> :</p><ul><li>用户可以通过配置文件和第三方插件来定制 <code>fish</code> 的外观和行为。</li></ul><p><strong>脚本友好</strong> :</p><ul><li><code>fish</code> 支持强大的脚本功能,并且提供了一些内置的函数和工具来简化脚本编写。</li></ul><p><strong>跨平台</strong> :</p><ul><li><code>fish</code> 可以在多种操作系统上运行,包括 Linux、macOS 和 Windows(通过 WSL 或其他终端模拟器)。</li></ul><p><strong>友好的错误提示</strong> :</p><ul><li>当命令执行出错时,<code>fish</code> 会提供有用的错误信息和可能的解决方案。</li></ul><p><code>tmux</code> 是一个开源的终端复用器,由 Nicholas Marriott 开发。它允许用户在一个终端窗口中访问多个独立的终端会话,并且可以在这些会话之间自由切换。<code>tmux</code> 特别适合需要长时间运行命令、需要断开连接后能够恢复工作场景的用户,以及需要在多个终端任务之间快速切换的用户。</p><p>以下是 <code>tmux</code> 的一些核心特性:</p><p><strong>会话分离与恢复</strong> :</p><ul><li>用户可以在 <code>tmux</code> 会话中运行程序,然后断开连接。程序会继续在后台运行,用户可以在任何时候重新连接到会话,恢复之前的工作状态。</li></ul><p><strong>窗口与窗格</strong> :</p><ul><li><code>tmux</code> 允许在一个会话中创建多个窗口(相当于多个终端),每个窗口可以进一步分割成多个窗格(panes),实现多任务并行处理。</li></ul><p><strong>可定制性</strong> :</p><ul><li>用户可以通过配置文件 <code>.tmux.conf</code> 来定制 <code>tmux</code> 的行为和外观,包括键绑定、颜色主题、状态栏等。</li></ul><p><strong>滚动缓冲区</strong> :</p><ul><li><code>tmux</code> 提供了一个滚动缓冲区,用户可以回顾之前在终端中执行的命令和输出,即使这些内容已经超出了当前屏幕的视野。</li></ul><p><strong>共享会话</strong> :</p><ul><li><code>tmux</code> 支持会话共享,允许多个用户连接到同一个会话,实时协作或观察。</li></ul><p><strong>跨平台</strong> :</p><ul><li><code>tmux</code> 可以在多种操作系统上运行,包括 Linux、macOS 和 BSD 系列。</li></ul><p><strong>命令和交互</strong> :</p><ul><li><code>tmux</code> 提供了丰富的命令和交互方式,用户可以通过前缀键(默认是 <code>Ctrl+b</code>)加上其他键来执行各种操作,如创建新窗口、切换窗格、调整窗格大小等。</li></ul><p><strong>状态栏和模式线</strong> :</p><ul><li><code>tmux</code> 的状态栏显示当前会话、窗口和时间等信息,模式线(mode line)则提供了当前窗格的状态和消息。</li></ul><p><strong>复制模式</strong> :</p><ul><li><code>tmux</code> 提供了一个复制模式,允许用户使用方向键和 <code>Page Up</code>/<code>Page Down</code> 键浏览和选择终端输出,然后复制到剪贴板。</li></ul><p><code>tmux</code> 是一个功能强大的工具,对于需要在终端中高效工作的用户来说,它提供了极大的便利和灵活性。通过熟练使用 <code>tmux</code>,用户可以提高工作效率,更好地管理复杂的终端任务。</p><h1 id="soup"><a href="#soup" class="headerlink" title="soup:"></a>soup:</h1><p><a href="https://zhuanlan.zhihu.com/p/414009313?utm_oi=1088037943095988224">用随机梯度下降来优化人生</a></p><p> <strong>要有目标</strong> 。你需要有目标。短的也好,长的也好。认真定下的也好,别人那里捡的也好。就跟随机梯度下降需要有个目标函数一样。</p><p> <strong>目标要大</strong> 。不管是人生目标还是目标函数,你最好不要知道最后可以走到哪里。如果你知道,那么你的目标就太简单了,可能是个凸函数。你可以在一开始的时候给自己一些小目标,例如期末考个80分,训练一个线性模型。但接下来得有更大的目标,财富自由也好,100亿参数的变形金刚也好,得足够一颗赛艇。</p><p> <strong>坚持走</strong> 。不管你的目标多复杂,随机梯度下降都是最简单的。每一次你找一个大概还行的方向(梯度),然后迈一步(下降)。两个核心要素是方向和步子的长短。但最重要的是你得一直走下去,能多走几步就多走几步。</p><p> <strong>痛苦的卷</strong> 。每一步里你都在试图改变你自己或者你的模型参数。改变带来痛苦。但没有改变就没有进步。你过得很痛苦不代表在朝着目标走,因为你可能走反了。但过得很舒服那一定在原地踏步。需要时刻跟自己作对。</p><p> <strong>可以躺平</strong> 。你用你内心的激情来迈步子。步子太小走不动,步子太长容易过早消耗掉了激情。周期性的调大调小步长效果挺好。所以你可以时不时休息休息。</p><p> <strong>四处看看</strong> 。每一步走的方向是你对世界的认识。如果你探索的世界不怎么变化,那么要么你的目标太简单,要么你困在你的舒适区了。随机梯度下降的第一个词是随机,就是你需要四处走走,看过很多地方,做些错误的决定,这样你可以在前期迈过一些不是很好的舒适区。</p><p> <strong>快也是慢</strong> 。你没有必要特意去追求找到最好的方向和最合适的步子。你身边当然会有幸运之子,他们每一步都在别人前面。但经验告诉我们,随机梯度下降前期进度太快,后期可能乏力。就是说你过早的找到一个舒适区,忘了世界有多大。所以你不要急,前面徘徊一段时间不是坏事。成名无需太早。</p><p> <strong>赢在起点</strong> 。起点当然重要。如果你在终点附近起步,可以少走很多路。而且终点附近的路都比较平,走着舒服。当你发现别人不如你的时候,看看自己站在哪里。可能你就是运气很好,赢在了起跑线。如果你跟别人在同一起跑线,不见得你能做更好。</p><p> <strong>很远也能到达</strong> 。如果你是在随机起点,那么做好准备前面的路会非常不平坦。越远离终点,越人迹罕见。四处都是悬崖。但随机梯度下降告诉我们,不管起点在哪里,最后得到的解都差不多。当然这个前提是你得一直按照梯度的方向走下去。如果中间梯度炸掉了,那么你随机一个起点,调整步子节奏,重新来。</p><p> <strong>独一无二</strong> 。也许大家有着差不多的目标,在差不多的时间毕业买房结婚生娃。但每一步里,每个人内心中看到的世界都不一样,导致走的路不一样。你如果跑多次随机梯度下降,在各个时间点的目标函数值可能都差不多,但每次的参数千差万别。不会有人关心你每次训练出来的模型里面参数具体是什么值,除了你自己。</p><p><strong>简单最好</strong> 。当然有比随机梯度下降更复杂的算法。他们想每一步看想更远更准,想步子迈最大。但如果你的目标很复杂,简单的随机梯度下降反而效果最好。深度学习里大家都用它。关注当前,每次抬头瞄一眼世界,快速做个决定,然后迈一小步。小步快跑。只要你有目标,不要停,就能到达。</p>]]></content>
<tags>
<tag> Slide </tag>
</tags>
</entry>
<entry>
<title>System programming</title>
<link href="/2024/09/21/System-programming/"/>
<url>/2024/09/21/System-programming/</url>
<content type="html"><![CDATA[<ul><li>系统编程<ul><li>错误处理<ul><li>检测错误<ul><li>函数的返回值</li><li>errno<ul><li>0: 没有错误</li><li>非0:发生了某种类型的错误</li></ul></li></ul></li><li>打印错误信息<ul><li>strerror(errnum)<ul><li>作用:将整数解析成文本的错误信息</li><li>errnum<ul><li>0: Success</li><li>非0: 具体的错误信息</li></ul></li></ul></li><li>perror(prefix)<ul><li>比较死板</li></ul></li><li>fprintf<ul><li>比较繁琐</li></ul></li><li>error<ul><li>status<ul><li>0: 不退出,不执行exit()</li><li>非0:打印错误信息后,执行 exit(status)</li></ul></li><li>errnum<ul><li>0</li><li>非0:在最后输出 strerror(errnum)</li></ul></li><li>格式串<ul><li>自定义输出格式</li></ul></li></ul></li></ul></li></ul></li><li>如何查看man手册<ul><li>手册编号<ul><li>1:shell命令</li><li>2:系统调用</li><li>3:库函数</li></ul></li><li>NAME<ul><li>一句话简单地介绍函数、命令的作用</li></ul></li><li>SYNOPSIS<ul><li>头文件</li><li>函数的声明<ul><li>指针类型的参数<ul><li>有const<ul><li>传入参数<ul><li>函数不会修改指针指向的内容</li></ul></li></ul></li><li>无const<ul><li>传入传出参数<ul><li>函数会修改指针指向的内容</li></ul></li></ul></li></ul></li><li>指针类型的返回值<ul><li>栈<ul><li>自动存储期限</li></ul></li><li>静态区<ul><li>静态存储期限</li></ul></li><li>堆<ul><li>动态存储期限<ul><li>调用者负责free</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>RETURN VALUE<ul><li>成功</li><li>失败</li></ul></li><li>DESCRIPTION<ul><li>带着问题去看</li><li>看地懂得就看,看不懂得就不看</li></ul></li></ul></li><li>文件<ul><li>目录<ul><li>getcwd<ul><li>作用:获取当前工作目录的绝对路径</li><li>参数<ul><li>buf</li><li>size</li></ul></li><li>返回值<ul><li>成功:当前工作目录</li><li>失败:NULL,并设置errno</li></ul></li><li>特例</li></ul></li><li>chdir<ul><li>作用:修改当前工作目录<ul><li>当前工作目录是进程的属性!</li></ul></li><li>参数<ul><li>path</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>mkdir<ul><li>作用:创建目录</li><li>参数<ul><li>path</li><li>mode<ul><li>目录的实际权限:mode & ~umask</li></ul></li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>rmdir<ul><li>作用:删除空目录</li><li>参数<ul><li>path</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>目录流<ul><li>opendir<ul><li>作用:打开目录流</li><li>参数<ul><li>path</li></ul></li><li>返回值<ul><li>成功:返回指向目录流的指针 (DIR*)</li><li>失败:NULL,并设置errno</li></ul></li></ul></li><li>closedir<ul><li>作用:关闭目录流</li><li>参数<ul><li>DIR* stream</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>readdir<ul><li>作用:读取下一个目录项</li><li>参数<ul><li>DIR* stream</li></ul></li><li>返回值<ul><li>成功<ul><li>返回指向下一个目录项的指针<ul><li>struct dirent<ul><li>d_name</li><li>d_ino</li><li>d_type<ul><li>DT_REG</li><li>DT_DIR</li><li>DT_BLK</li><li>DT_CHR</li><li>DT_LNK</li><li>DT_SOCK</li><li>DT_FIFO</li><li>DT_UNKNOWN</li></ul></li></ul></li></ul></li><li>如果读到流的末尾,返回NULL,不设置errno</li></ul></li><li>失败:NULL,设置errno</li></ul></li></ul></li><li>递归处理目录<ul><li>a. 递归打印目录 (tree)</li><li>b. 递归复制目录 (cp -r dir1 dir2)</li><li>c. 递归删除目录 (rm -r dir)</li></ul></li></ul></li></ul></li><li>文件描述符<ul><li>内核管理文件的数据结构<ul><li>fd</li><li>文件描述符表</li><li>打开文件<ul><li>flags</li><li>文件的位置</li><li>vnode的指针</li><li>引用计数</li></ul></li><li>vnode<ul><li>inode的副本</li><li>设备号</li></ul></li><li>inode</li></ul></li><li>为什么这么设计<ul><li>安全</li><li>简单方便</li><li>提供了统一的操作方式<ul><li>通用性</li></ul></li></ul></li><li>操作<ul><li>open<ul><li>作用:打开文件描述符</li><li>参数<ul><li>path</li><li>flags<ul><li>O_RDONLY</li><li>O_WRONLY</li><li>O_RDWR</li><li>O_CREAT</li><li>O_EXCL</li><li>O_TRUNC</li><li>O_APPEND</li></ul></li><li>mode<ul><li>如果flags里设置了 O_CREAT, 就需要传入文件的创建权限</li></ul></li></ul></li><li>返回值<ul><li>成功:文件描述符 (最小可用的文件描述符)</li><li>失败:-1, 并设置errno</li></ul></li></ul></li><li>close<ul><li>作用:关闭文件描述符</li><li>参数<ul><li>fd</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>read<ul><li>作用:读文件描述符</li><li>参数<ul><li>fd</li><li>buf</li><li>size</li></ul></li><li>返回值<ul><li>成功:返回实际读取的字节数目。如果返回值为 0,表示读到了文件的末尾。</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>write<ul><li>作用:写文件描述符</li><li>参数<ul><li>fd</li><li>buf</li><li>size</li></ul></li><li>返回值<ul><li>成功:返回实际写入的字节数目</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>lseek<ul><li>作用:移动文件位置</li><li>参数<ul><li>fd</li><li>offset</li><li>whence<ul><li>SEEK_SET</li><li>SEEK_CUR</li><li>SEEK_END</li></ul></li></ul></li><li>返回值<ul><li>成功:移动后文件的位置</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>fsync<ul><li>作用:将数据写回到磁盘</li><li>参数<ul><li>fd</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>ftruncate<ul><li>作用:截断文件</li><li>参数<ul><li>fd</li><li>length<ul><li>length < 原文件大小,截断的数据会丢失</li><li>length > 原文件大小,扩充的部分会填入空字符 (‘\0’)</li></ul></li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>fstat<ul><li>作用:查看文件的元数据</li><li>参数<ul><li>fd</li><li>struct stat* statbuf<ul><li>传出参数,用来存储文件的元数据<ul><li>struct stat<ul><li>st_ino</li><li>st_size</li><li>st_mode</li><li>st_blocks</li><li>st_nlinks</li><li>……</li></ul></li></ul></li></ul></li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>dup<ul><li>作用:复制文件描述符</li><li>参数<ul><li>oldfd</li></ul></li><li>返回值<ul><li>成功:新的文件描述符 (最小可用的文件描述符)</li><li>失败:-1,并设置 errno</li></ul></li><li>缺陷<ul><li>不能指定 newfd</li><li>close(); dup() 之间有一个时间窗口,可能会引发竞态条件,导致并发问题。</li></ul></li></ul></li><li>dup2<ul><li>作用:复制文件描述符</li><li>参数<ul><li>oldfd</li><li>newfd</li></ul></li><li>返回值<ul><li>成功:新的文件描述符</li><li>失败:-1,并设置 errno</li></ul></li></ul></li><li>内存映射 I/O<ul><li>mmap<ul><li>作用:将文件的内容直接映射到进程的虚拟内存空间</li><li>参数<ul><li>addr<ul><li>一般填 NULL,由内核指定起始位置</li></ul></li><li>length</li><li>prot<ul><li>PROT_NONE</li><li>PROT_READ</li><li>PROT_WRITE</li><li>PROT_EXEC</li></ul></li><li>flags<ul><li>MAP_PRIVATE<ul><li>特点<ul><li>写时复制</li><li>对映射区的修改,不会写回到底层文件</li></ul></li><li>作用:用文件的内容初始化内存空间</li></ul></li><li>MAP_SHARED<ul><li>特点<ul><li>对映射区的修改,会写回到磁盘</li><li>进程共享</li></ul></li><li>作用<ul><li>内存映射 I/O (零拷贝), 一般适用于大文件</li><li>IPC</li></ul></li></ul></li></ul></li><li>fd</li><li>offset<ul><li>一定是页的整数倍</li></ul></li></ul></li><li>返回值<ul><li>成功:映射区的起始地址</li><li>失败:MAP_FAILED,并设置 errno</li></ul></li></ul></li><li>munmap<ul><li>作用:解除映射</li><li>参数<ul><li>addr</li><li>length</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li>进程<ul><li>理论模型<ul><li>什么是进程<ul><li>目的:压榨计算机的资源</li><li>概念<ul><li>用户:程序的动态执行过程</li><li>内核:正在执行的任务,计算机的负载<ul><li>进程是计算机分配资源的最小单位</li></ul></li></ul></li><li>struct proc<ul><li>内存资源</li><li>上下文<ul><li>CPU的状态</li></ul></li><li>外部设备<ul><li>ofile</li></ul></li><li>当前工作目录</li><li>父进程</li><li>状态</li><li>….</li></ul></li><li>实现<ul><li>底层机制<ul><li>如何切换进程<ul><li>方案一<ul><li>简单,快速</li><li>不安全</li><li>没有控制权</li></ul></li><li>方案二<ul><li>引入了CPU的模态<ul><li>用户态<ul><li>不能访问非法内存空间</li><li>不能执行特权指令</li></ul></li><li>内核态</li><li>问题:应用程序如何执行特权指令<ul><li>系统调用</li><li>陷阱表</li></ul></li></ul></li><li>缺点:操作系统不能主动获取控制权</li></ul></li><li>方案三<ul><li>引入时钟设备<ul><li>每隔xms会触发一次时钟中断</li></ul></li></ul></li></ul></li></ul></li><li>上层策略<ul><li>调度算法</li></ul></li></ul></li><li>上下文切换的开销<ul><li>保存寄存器</li><li>恢复寄存器</li><li>高速缓存失效</li><li>TLB的失效<ul><li>页表的缓存</li></ul></li></ul></li></ul></li></ul></li><li>API<ul><li>基本操作 (原语)<ul><li>获取进程的标识<ul><li>getpid</li><li>getppid</li></ul></li><li>创建进程<ul><li>fork<ul><li>原理<ul><li>复制父进程的 PCB,并修改其中的某些信息</li><li>复制父进程的页表<ul><li>写时复制 (copy on write)</li></ul></li><li>……</li></ul></li><li>关键点<ul><li>父子进程都是从 fork() 返回点,开始执行的<ul><li>一次调用,两次返回</li></ul></li><li>当fork()返回后,到底是父进程先执行,还是子进程先执行,这是不确定的。</li></ul></li><li>返回值<ul><li>成功<ul><li>父进程:子进程的pid</li><li>子进程:0</li></ul></li><li>失败:-1,并设置 errno</li></ul></li><li>父子进程资源共享<ul><li>代码段<ul><li>共享</li></ul></li><li>栈,堆,数据段<ul><li>私有</li></ul></li><li>用户态缓冲区<ul><li>私有<ul><li>面试题</li></ul></li></ul></li><li>文件描述符列表<ul><li>私有</li></ul></li><li>打开文件<ul><li>共享</li></ul></li></ul></li></ul></li></ul></li><li>终止进程<ul><li>正常终止<ul><li>从 main 函数返回<ul><li>exit()</li></ul></li><li>exit()<ul><li><ol><li>按和注册相反的顺序执行退出处理函数</li></ol><ul><li>atexit<ul><li>参数<ul><li>void (*function) (void)</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:非0</li></ul></li><li>局限性<ul><li>不能获取退出状态</li><li>没有参数</li></ul></li></ul></li><li>on_exit<ul><li>参数<ul><li>void (<em>function) (int status, void</em> arg)</li><li>void* args</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:非0</li></ul></li></ul></li></ul></li><li><ol start="2"><li>刷新用户态缓冲区</li></ol></li><li><ol start="3"><li>调用 _exit()</li></ol></li></ul></li><li>_exit()<ul><li>参数<ul><li>status<ul><li>只用了后8位,表示退出状态。0 表示执行成功,非0表示执行失败。</li></ul></li></ul></li></ul></li></ul></li><li>异常终止<ul><li>abort()<ul><li>给自己发送 SIGABRT 信号</li></ul></li><li>发送信号</li></ul></li></ul></li><li>监控子进程的状态<ul><li>wait<ul><li>作用:监控子进程是否终止;如果没有子进程终止,会一直阻塞。</li><li>参数<ul><li>int* status<ul><li>传出参数,用来接收子进程的终止状态信息<ul><li>WIFEXITED<ul><li>判断子进程是否正常终止</li></ul></li><li>WEXITSTATUS<ul><li>获取子进程的退出状态</li></ul></li><li>WIFSIGNALED<ul><li>判断子进程是否异常终止</li></ul></li><li>WTERMSIGNAL<ul><li>获取导致子进程异常终止的信号</li></ul></li><li>WCOREDUMP<ul><li>非标准</li></ul></li></ul></li></ul></li></ul></li><li>返回值<ul><li>成功:终止子进程的pid</li><li>失败:-1,并设置erno</li></ul></li><li>局限性<ul><li><ol><li>如果没有子进程终止,会一直阻塞。</li></ol></li><li><ol start="2"><li>不能指定监控哪一个子进程</li></ol></li><li><ol start="3"><li>只能监控子进程是否终止</li></ol></li></ul></li></ul></li><li>waitpid<ul><li>作用:监控子进程的状态 (终止、停止、继续)</li><li>参数<ul><li>pid<ul><li><blockquote><p>0: 监控指定的子进程</p></blockquote></li><li>=0:监控同进程组的子进程</li><li>=-1:监控任意的子进程</li><li><-1: 监控进程组 ID 为 |pid| 的子进程</li></ul></li><li>int* status<ul><li>传出参数,用来接收子进程的终止状态信息<ul><li>WIFEXITED<ul><li>判断子进程是否正常终止</li></ul></li><li>WEXITSTATUS<ul><li>获取子进程的退出状态</li></ul></li><li>WIFSIGNALED<ul><li>判断子进程是否异常终止</li></ul></li><li>WTERMSIGNAL<ul><li>获取导致子进程异常终止的信号</li></ul></li><li>WCOREDUMP<ul><li>非标准</li></ul></li><li>WIFSTOPPED<ul><li>判断子进程是否停止</li></ul></li><li>WSTOPSIG<ul><li>获取导致子进程停止的信号</li></ul></li><li>WIFCONTINUED<ul><li>判断子进程是否继续执行</li></ul></li></ul></li></ul></li><li>options<ul><li>WONHANG<ul><li>不阻塞</li></ul></li><li>WUNTRACE<ul><li>监控停止状态</li></ul></li><li>WCONTINUED<ul><li>监控继续状态</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>执行程序<ul><li>execve<ul><li>原理<ul><li>清除进程的代码段、数据段、堆、栈…</li><li>加载新的可执行程序,设置代码段和数据段</li><li>从新的可执行程序main函数的第一行开始执行</li></ul></li><li>参数<ul><li>pathname<ul><li>可执行程序的路径</li></ul></li><li>char* argv[]<ul><li>命令行参数<ul><li>argv[0]: 可执行程序的名字</li><li>以NULL结尾</li></ul></li></ul></li><li>char* envp[]<ul><li>环境变量<ul><li>以 NULL 结尾</li></ul></li></ul></li></ul></li><li>返回值<ul><li>成功:不返回</li><li>失败:-1,并设置 errno</li></ul></li></ul></li><li>exec函数簇<ul><li>l: list<ul><li>命令行参数以可变长参数的形式指定</li></ul></li><li>v: vector<ul><li>命令行参数以数组的形式指定</li></ul></li><li>e: environment<ul><li>重新设置环境变量</li></ul></li><li>p: PATH<ul><li>根据PATH环境变量查找可执行程序</li></ul></li></ul></li><li>system<ul><li>作用:执行任意的 shell 命令</li><li>原理:fork()一个子进程,让子进程执行 “sh -c command”</li><li>参数<ul><li>command<ul><li>要执行的命令</li></ul></li></ul></li><li>惯用法<ul><li>fork()</li><li>子进程执行新的可执行程序</li><li>父进程等待子进程结束</li></ul></li></ul></li></ul></li></ul></li><li>IPC<ul><li>管道<ul><li>概念:内核管理的数据结构,用于进程之间通信</li><li>特性<ul><li>字节流<ul><li>数据是没有边界的</li><li>接收的顺序就是发送的顺序</li><li>不能够用 lseek() 移动位置</li></ul></li><li>半双工</li><li>管道的容量是有限的<ul><li>如果管道已经满了,写管道会引发阻塞</li></ul></li><li>读管道<ul><li>如果管道里面没有数据,读管道会引发阻塞</li></ul></li></ul></li><li>pipe<ul><li>作用:创建管道</li><li>参数<ul><li>int fields[2]<ul><li>传出参数:fields[0]会关联到管道的读端;fields[1]会关联到管道的写端</li></ul></li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li><li>惯用法<ul><li>先 pipe</li><li>再 fork</li><li>父进程关闭管道的一端;子进程关闭管道的另一端</li></ul></li><li>限制<ul><li>只能用于相关进程之间通信,往往是父子进程</li></ul></li></ul></li><li>fifo<ul><li>作用:创建有名管道 (fifo)</li><li>命令:mkfifo [-m mode] 名字</li><li>mkfifo<ul><li>参数<ul><li>pathname</li><li>mode</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:-1,并设置errno</li></ul></li></ul></li><li>用于任意两个进程之间通信</li></ul></li></ul></li><li>信号 (signal)<ul><li>异步的事件通知机制。是进程感知外部世界的一种很重要的方式。</li><li>特征<ul><li>异步<ul><li>进程不知道什么时候会收到信号,但一旦收到信号,会立刻响应信号。</li></ul></li><li>不稳定<ul><li>处于 pending 状态的信号,可能发生丢失</li></ul></li><li>语义不明确<ul><li>不同操作系统信号的语义不尽相同</li></ul></li></ul></li><li>事件源<ul><li>用户<ul><li>ctrl + c<ul><li>SIGINT</li></ul></li><li>ctrl + z<ul><li>SIGTSTP</li></ul></li><li>ctrl + \<ul><li>SIGQUIT</li></ul></li></ul></li><li>硬件<ul><li>执行了非法的指令<ul><li>SIGILL</li></ul></li><li>访问非法的内容<ul><li>SIGSEGV</li></ul></li><li>除0异常<ul><li>SIGFPE</li></ul></li></ul></li><li>软件<ul><li>子进程的状态发生修改<ul><li>SIGCHLD</li></ul></li><li>写破损的管道<ul><li>SIGPIPE</li></ul></li><li>调用 abort()<ul><li>SIGABRT</li></ul></li></ul></li></ul></li><li>信号的执行流程</li><li>响应信号<ul><li>默认处置<ul><li>Term</li><li>Core</li><li>Stop</li><li>Cont</li><li>Ign</li></ul></li><li>自行处置<ul><li>注册信号处理函数</li></ul></li></ul></li><li>API<ul><li>注册信号处理函数<ul><li>signal<ul><li>参数<ul><li>signo</li><li>handler<ul><li>typedef void (*sighandler_t) (int)<ul><li>SIG_DFL</li><li>SIG_IGN</li><li>自定义处理函数</li></ul></li></ul></li></ul></li><li>返回值<ul><li>成功:旧的信号处理函数</li><li>失败:SIG_ERR</li></ul></li></ul></li></ul></li><li>发送信号<ul><li>kill<ul><li>参数<ul><li>pid<ul><li><blockquote><p>0: 给进程ID为pid的进程发送信号</p></blockquote></li><li>=0: 给同进程组的进程发送信号</li><li>=-1:给权限允许的所有进程发送信号</li><li><-1: 给进程组 id 为 |pid| 的进程发送信号</li></ul></li><li>signo</li></ul></li><li>返回值<ul><li>成功 (至少发送了一次信号):0</li><li>失败:-1,并设置 errno</li></ul></li></ul></li><li>raise<ul><li>kill(getpid(), signo)</li></ul></li></ul></li></ul></li></ul></li><li>套接字 (socket)</li><li>共享内存 + 信号量 (semaphor)</li><li>消息队列</li></ul></li></ul></li></ul></li><li>I/O多路复用<ul><li>最佳实践:一个执行流程最多只能有一个阻塞点</li><li>作用:同步的I/O事件监听机制</li><li>API<ul><li>select<ul><li>参数<ul><li>nfds<ul><li>监听的最大文件描述符 + 1</li></ul></li><li>readfds<ul><li>传入:要监听读事件的文件描述符集合</li><li>传出:读事件已就绪的文件描述符集合</li></ul></li><li>writefds<ul><li>传入:要监听写事件的文件描述符集合</li><li>传出:写事件已就绪的文件描述符集合</li></ul></li><li>exceptfds<ul><li>传入:要监听看异常事件的文件描述符集合</li><li>传出:异常事件已就绪的文件描述符集合</li></ul></li><li>timeout<ul><li>传入:超时时间</li><li>传出:剩余的时间</li><li>NULL: 一直阻塞</li><li>{0, 0}:不阻塞</li></ul></li></ul></li><li>返回值<ul><li>成功<ul><li>返回就绪的 I/O 事件个数;如果超时,返回0</li></ul></li><li>失败<ul><li>-1,并设置errno</li></ul></li></ul></li><li>缺陷<ul><li><ol><li>监听的文件描述符个数是受限的</li></ol></li><li><ol start="2"><li>轮询fd_set,已判断哪些文件描述符就绪</li></ol></li></ul></li></ul></li><li>poll</li><li>epoll</li></ul></li></ul></li><li>线程<ul><li>什么是线程<ul><li>进程的一条执行流程,它是CPU调度的最小单位</li></ul></li><li>为什么引入线程<ul><li>相对进程而言,线程是轻量级的</li><li>进程之间通信需要打破隔离的壁障,线程之间通信的代价几乎没有</li><li>线程之间切换,不会导致 CPU 高速缓存和 TLB 的失效</li></ul></li><li>API<ul><li>基本操作 (控制原语)<ul><li>获取线程的标识<ul><li>pthread_self</li></ul></li><li>创建线程<ul><li>pthread_create<ul><li>参数<ul><li>tid: 传出参数,用来接收新线程的 ID</li><li>attr: 线程的属性,一般填NULL,表示采用默认属性</li><li>void* (<em>start_routine) (void</em>): 线程的入口函数</li><li>arg: 线程入口函数的实参</li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:返回错误码,不设置errno</li></ul></li></ul></li></ul></li><li>终止线程<ul><li>进程终止,会导致该进程的所有线程终止</li><li>从线程入口函数返回</li><li>调用 pthread_exit()</li><li>响应 pthread_cancel()</li></ul></li><li>退出线程<ul><li>pthread_exit<ul><li>参数<ul><li>void* retval</li></ul></li></ul></li></ul></li><li>连接线程<ul><li>pthread_join<ul><li>参数<ul><li>tid</li><li>void** retval<ul><li>传出参数,用来接收线程的返回值<ul><li>如果线程是因为响应取消请求而终止,线程会返回 PTHREAD_CANCELLED</li></ul></li></ul></li></ul></li><li>返回值<ul><li>成功:0</li><li>失败:返回错误码,不设置errno</li></ul></li></ul></li></ul></li><li>分离线程<ul><li>pthread_detach<ul><li>参数<ul><li>tid</li></ul></li><li>处于游离态的进程不可以被 join, 也不能重新恢复到 joinable 状态</li></ul></li></ul></li><li>发送取消请求<ul><li>pthread_cancel<ul><li>参数<ul><li>tid: 目标线程</li></ul></li><li>目标线程是否响应取消请求,以及何时响应<ul><li>取消状态<ul><li>ENABLE (默认)</li><li>DISABLE</li></ul></li><li>取消类型<ul><li>DEFFERED (默认)<ul><li>取消点</li></ul></li><li>ASYNCHROUNS<ul><li>任意时候</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>清理线程<ul><li>线程的清理函数栈<ul><li>pthead_cleanup_push<ul><li>参数<ul><li>void (<em>routine) (void</em>)</li><li>void* arg</li></ul></li></ul></li><li>pthread_cleanup_pop<ul><li>参数<ul><li>execute<ul><li>0: 不执行</li><li>非0:执行</li></ul></li></ul></li></ul></li></ul></li><li>执行时机<ul><li>响应取消请求</li><li>调用 pthread_exit()</li><li>调用 pthread_cleanup_pop(非0)</li><li>注意:从线程入口函数返回,不执行!</li></ul></li></ul></li></ul></li><li>同步<ul><li>竞态条件<ul><li>程序的运行结果 (状态), 取决于程序的调度情况</li><li>原因<ul><li>多线程</li><li>共享资源 (数据、文件…)</li><li>至少有一个线程会修改共享资源</li></ul></li></ul></li><li>并发问题<ul><li>竞态条件</li></ul></li><li>同步:避免坏的调度的发生<ul><li>互斥地访问资源<ul><li>锁机制<ul><li>互斥锁<ul><li>用法<ul><li>上锁</li><li>执行临界区的代码 (对共享资源的访问)</li><li>释放锁</li></ul></li><li>API<ul><li>初始化<ul><li>静态初始化<ul><li>PTHREAD_MUTEX_INITIALIZER</li></ul></li><li>动态初始化<ul><li>pthread_mutex_init</li></ul></li></ul></li><li>上锁<ul><li>pthread_mutex_lock</li><li>pthread_mutex_trylock</li><li>pthread_mutex_timedlock</li></ul></li><li>释放锁<ul><li>pthread_mutex_unlock</li></ul></li><li>销毁<ul><li>pthread_mutex_destroy</li></ul></li></ul></li></ul></li><li>自旋锁</li><li>读写锁</li><li>…</li></ul></li><li>CAS (无锁编程)</li></ul></li><li>等待某个条件成立<ul><li>条件变量 (提供了等待唤醒机制)<ul><li>条件不成立: 等待</li><li>条件成立:被唤醒 (执行)</li><li>API<ul><li>初始化<ul><li>静态初始化<ul><li>PTHREAD_COND_INITIALIZER</li></ul></li><li>动态初始化<ul><li>pthread_cond_init</li></ul></li></ul></li><li>等待<ul><li>pthread_cond_wait(&cond, &mutex)<ul><li><ol><li>释放锁</li></ol></li><li><ol start="2"><li>陷入阻塞状态</li></ol></li><li><ol start="3"><li>当pthread_cond_wait返回,当前线程一定再一次获取了锁</li></ol></li><li>注意事项:返回时,条件不一定成立!存在虚假唤醒现象。</li></ul></li><li>pthread_cond_timedwait(&cond, &mutex, abstime)</li></ul></li><li>唤醒<ul><li>pthread_cond_signal(&cond)</li><li>pthread_cond_broadcast(&cond)</li></ul></li><li>销毁<ul><li>pthread_cond_destroy</li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li>生产者消费者模型<ul><li>生产者<ul><li>队列不满<ul><li>生产商品,入队列;队列不空,唤醒消费者</li></ul></li><li>队列满了<ul><li>等待</li></ul></li></ul></li><li>消费者<ul><li>队列不空<ul><li>消费商品,出队列。队列不满,唤醒生产者</li></ul></li><li>队列空了<ul><li>等待</li></ul></li></ul></li><li>阻塞队列</li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<tags>
<tag> System-call </tag>
</tags>
</entry>
<entry>
<title>GNU toolchain</title>
<link href="/2024/09/20/GNU-toolchain/"/>
<url>/2024/09/20/GNU-toolchain/</url>
<content type="html"><![CDATA[<ul><li>编译工具链<ul><li>gcc<ul><li>常用选项<ul><li>-E: 生成预处理后的文件</li><li>-S: 生成汇编文件</li><li>-c: 生成目标文件</li><li>无: 生成可执行程序</li><li>-Wall: 生成警告信息</li><li>-g: 生成调试信息</li><li>-O0, -O1, -O2, -O3</li><li>-Dmacro</li><li>-Dmacro=value</li><li>-Idir</li></ul></li></ul></li><li>条件编译<ul><li>在预处理阶段决定包含哪些代码片段,不包含哪些代码片段</li><li>预处理指令<ul><li>#if<ul><li>运算符: defined</li></ul></li><li>#ifdef</li><li>#ifndef</li></ul></li><li>作用<ul><li>提高代码的可移植性</li><li>给宏提供默认值</li><li>避免头文件重复包含</li><li>#if 0 代码片段 #endif<ul><li>条件屏蔽</li></ul></li></ul></li></ul></li><li>gdb<ul><li>观念<ul><li>调试代码是写代码难度的两倍</li><li>程序报错的位置,很可能不是真正出错的位置</li><li>调试的基本步骤<ul><li>走一走</li><li>停一停</li><li>看一看<ul><li>查看程序的状态</li><li>你对程序的预期</li></ul></li></ul></li></ul></li><li>命令<ul><li>编译:加 -g</li><li>进入调试界面<ul><li>gdb 可执行程序</li></ul></li><li>退出调试界面<ul><li>quit / q</li></ul></li><li>设置命令行参数<ul><li>set args 其它的命令行参数</li></ul></li><li>查看代码<ul><li>layout src</li><li>layout asm</li><li>layout regs</li><li>layout next</li><li>layout prev</li><li>refresh<ul><li>刷新</li></ul></li></ul></li><li>走一走<ul><li>run / r</li><li>next / n</li><li>step / s</li><li>finish</li><li>continue / c</li></ul></li><li>停一停<ul><li>break / b</li><li>info break / i b</li><li>delete [NUM]</li><li>ignore NUM COUNT</li></ul></li><li>看一看<ul><li>print / p<ul><li>print 表达式</li><li>print 表达式=值</li></ul></li><li>display<ul><li>display 表达式</li><li>info display</li><li>undisplay [NUM]</li></ul></li><li>info locals</li><li>info args</li><li>info registers</li><li>bracktrace / bt</li><li>frame N</li><li>x/nfu<ul><li>查看内存</li></ul></li></ul></li></ul></li><li>查看 core 文件<ul><li>程序崩溃时的内存快照<ul><li>“黑匣子”</li></ul></li><li>复现错误</li><li>gdb 可执行程序 core文件<ul><li>看一看</li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<categories>
<category> Tools </category>
</categories>
<tags>
<tag> GNU-Toolchain </tag>
</tags>
</entry>
<entry>
<title>Shell command</title>
<link href="/2024/09/19/Shell-command/"/>
<url>/2024/09/19/Shell-command/</url>
<content type="html"><![CDATA[<ul><li>Shell命令<ul><li>Linux结构<ul><li>kernel<ul><li>管理计算机的硬件资源</li><li>为上层应用程序提供运行环境</li></ul></li><li>系统调用<ul><li>内核给上层应用程序提供的接口</li></ul></li><li>库函数<ul><li>系统调用的封装<ul><li>方便使用</li><li>增加代码的可移植性</li></ul></li></ul></li><li>shell<ul><li>命令解释器<ul><li>命令:一般来说,命令就是可执行程序</li><li>脚本 (script):命令的集合</li><li>具体的shell<ul><li>sh</li><li>csh</li><li>bash</li><li>ksh</li><li>zsh</li></ul></li></ul></li></ul></li></ul></li><li>用户子系统相关命令<ul><li>查看所有用户<ul><li>cat /etc/passwd</li><li>man 5 passwd</li></ul></li><li>添加用户<ul><li>useradd<ul><li>-m</li><li>-s /bin/bash</li></ul></li></ul></li><li>删除用户<ul><li>userdel<ul><li>-r</li></ul></li></ul></li><li>修改密码<ul><li>passwd</li></ul></li><li>切换用户<ul><li>su</li></ul></li><li>退出切换<ul><li>exit</li></ul></li></ul></li><li>文件<ul><li>目录<ul><li>常见的目录<ul><li>/</li><li>/bin</li><li>/lib</li><li>/proc</li><li>/dev</li><li>/var</li><li>/etc</li><li>/root</li><li>/home/{username}</li></ul></li><li>打印当前工作目录<ul><li>pwd</li></ul></li><li>切换当前工作目录<ul><li>cd<ul><li>cd</li><li>cd ~</li><li>cd .</li><li>cd ..</li><li>cd -<ul><li>上一次目录保存到 OLDPWD 环境变量中</li></ul></li></ul></li></ul></li><li>创建目录<ul><li>mkdir<ul><li>-p<ul><li>mkdir -p a/b/c</li></ul></li></ul></li></ul></li><li>删除空目录<ul><li>rmdir<ul><li>-p<ul><li>rmdir -p a/b/c</li></ul></li></ul></li></ul></li><li>显示目录项<ul><li>ls<ul><li>常用选项<ul><li>-a</li><li>-i</li><li>-l<ul><li>第一个字段<ul><li>第一个字符<ul><li>文件类型<ul><li>-:普通文件</li><li>d</li><li>c</li><li>b</li><li>l</li><li>p</li><li>s</li></ul></li></ul></li><li>其它九个字符<ul><li>权限<ul><li>拥有者</li><li>所属组</li><li>其他人</li></ul></li></ul></li></ul></li><li>硬链接数</li><li>拥有者</li><li>所属组</li><li>大小</li><li>最近修改时间</li><li>文件名</li></ul></li></ul></li></ul></li><li>tree</li></ul></li><li>复制<ul><li>cp<ul><li>格式<ul><li>cp SOURCE DEST</li><li>cp -r SOURCE DEST</li><li>cp SOURCE… DEST(已存在的目录)</li></ul></li><li>常用选项<ul><li>-r</li><li>-n</li><li>-i</li></ul></li></ul></li></ul></li><li>移动<ul><li>mv<ul><li>格式<ul><li>mv SOURCE DEST (重命名)</li><li>mv SOURCE… DEST (已存在的目录,移动)</li></ul></li><li>常用选项<ul><li>-n</li><li>-i</li></ul></li></ul></li></ul></li><li>删除文件或文件夹<ul><li>rm<ul><li>常用选项<ul><li>-r</li><li>-i</li><li>-f: 永远不提示</li></ul></li></ul></li></ul></li></ul></li><li>普通文件<ul><li>创建文件<ul><li>touch a.txt</li><li>echo “Hello world” > a.txt</li><li>vim a.txt</li></ul></li><li>查找文件<ul><li>which<ul><li>作用:查找可执行程序</li><li>原理:根据 PATH 环境变量,依次查找</li><li>常用选项<ul><li>-a</li></ul></li></ul></li><li>find<ul><li>作用:在目录下,递归地查找文件</li><li>格式:find [目录…] 查找条件</li><li>查找条件<ul><li>-name 通配符</li><li>-type c<ul><li>f: regular file</li><li>d: directory</li><li>p: named pipe</li><li>c: character device</li><li>b: block device</li><li>l: symbolic link</li><li>s: domain socket</li></ul></li><li>-size n[bcwkMG]<ul><li>单位<ul><li>b: block (默认)</li><li>c: character</li><li>w: word (2-bytes)</li><li>k</li><li>M</li><li>G</li></ul></li><li>-size 4M</li><li>-size +4M</li><li>-size -4M</li></ul></li><li>-empty</li><li>-user, -uid</li><li>-group, -gid</li><li>-perm 三个八进制数字</li><li>时间<ul><li>-atime, -amin</li><li>-ctime, -cmin</li><li>-mtime, -mmin<ul><li>-mtime 3</li><li>-mtime +3</li><li>-mtime -3</li></ul></li></ul></li><li>组合<ul><li>-a (and)</li><li>-o (or)</li><li>! (not)</li></ul></li></ul></li></ul></li></ul></li><li>查看文件内容<ul><li>cat<ul><li>作用:拼接文件内容,并将结果输出到 stdout</li><li>常用选项<ul><li>-n</li></ul></li></ul></li><li>head<ul><li>作用:显示文件的头几行</li><li>常用选项<ul><li>-n<ul><li>-n 20</li><li>-n -5</li></ul></li></ul></li></ul></li><li>tail<ul><li>作用:显示文件的后几行</li><li>常用选项<ul><li>-n<ul><li>-n 20</li><li>-n +5</li></ul></li><li>-F<ul><li>显示文件追加的内容<ul><li>看日志文件</li></ul></li></ul></li></ul></li></ul></li><li>less (more)<ul><li>作用:单页浏览</li><li>常用命令<ul><li>f: forward</li><li>b: backward</li><li>d: down</li><li>u: up</li><li>q: qut</li><li>:n (next)</li><li>:p (previous)</li></ul></li></ul></li></ul></li><li>搜索文件内容<ul><li>grep (globally regular expression print)<ul><li>作用:打印文件中所有匹配正则表达式的行</li><li>常用选项<ul><li>-E: 使用扩展的正则表达式语法</li><li>-n: 显示行号</li><li>-i: 忽略大小些</li><li>-v: 显示不匹配正则表达式的行</li><li>-c: 显示行数</li></ul></li></ul></li></ul></li><li>文件的权限<ul><li>目录权限的含义<ul><li>r<ul><li>查看目录项<ul><li>ls, tree</li></ul></li></ul></li><li>w<ul><li>添加和删除目录项</li></ul></li><li>x<ul><li>目录最基本的权限,rw都依赖x权限<ul><li>cd</li></ul></li></ul></li></ul></li><li>chmod<ul><li>文字设定法<ul><li>chmod [ugoa] [+-=] [rwx] item</li></ul></li><li>数字设定法<ul><li>chmod 三个八进制数字 item</li></ul></li></ul></li><li>umask<ul><li>文件创建掩码</li><li>查看当前文件创建掩码<ul><li>umask</li></ul></li><li>修改文件创建掩码<ul><li>umask 四个八进制数字<ul><li>0002</li></ul></li></ul></li><li>原理<ul><li>umask 指定的想去掉的权限</li><li>普通文件:666 & ~umask</li><li>目录文件:777 & ~umask</li></ul></li></ul></li></ul></li><li>链接<ul><li>硬链接<ul><li>ln 文件 硬链接</li></ul></li><li>符号链接<ul><li>一个文本文件,文件的内容是另一个文件的路径 (相对路径,绝对路径)</li><li>ln -s 文件 符号链接</li><li>类比<ul><li>Windows上的快捷方式</li><li>指针</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>通配符<ul><li>*: 匹配任意多个字符 (包括0个)</li><li>?: 匹配任意一个字符</li><li>集合/类<ul><li>[abc]</li><li>[!abc]</li><li>[0-9a-zA-Z_]</li></ul></li></ul></li><li>重定向<ul><li>stdin<ul><li><</li></ul></li><li>stdout<ul><li><blockquote><p>, >></p></blockquote></li></ul></li><li>stderr<ul><li>2>, 2>></li></ul></li></ul></li><li>正则表达式<ul><li>基本单位<ul><li>字符</li><li>转义字符</li><li>集合<ul><li>[abc]</li><li>[^abc]</li><li>[a-zA-Z0-9]</li></ul></li><li>.<ul><li>表示任意一个字符</li></ul></li><li>(expr)</li></ul></li><li>基本操作<ul><li>拼接<ul><li>[abc]x<ul><li>“贴贴”</li></ul></li></ul></li><li>重复<br>* <ul><li>?</li><li></li><li>{m}</li><li>{m, n}</li><li>{n, }</li></ul></li></ul></li><li>指定位置<ul><li>^</li><li>$</li><li><<ul><li>词首</li></ul></li><li>><ul><li>词尾</li></ul></li></ul></li></ul></li><li>命令的组合<ul><li>cmd1 ; cmd2</li><li>cmd1 | cmd2<ul><li>原理<ul><li>cmd1 的 stdout 重定向到管道的一端</li><li>cmd2 的 stdin 重定向到管道的另一端</li></ul></li></ul></li><li>cmd1 | xargs cmd2<ul><li>将cmd1的输出,作为cmd2的命令行参数</li></ul></li></ul></li><li>其它<ul><li>查看帮助手册<ul><li>man<ul><li>Tips: Be a MAN!</li><li>格式:man [手册编号] 词条</li><li>man手册<ul><li>1: shell命令</li><li>2:系统调用</li><li>3:库函数</li><li>……</li></ul></li></ul></li></ul></li><li>关机命令<ul><li>shutdown<ul><li>-P, –poweroff</li><li>-r, –reboot</li><li>-H, –halt</li><li>-c (cancel)</li></ul></li></ul></li><li>别名<ul><li>alias<ul><li>查看别名:alias</li><li>设置别名:alias cd=’sudo rm -rf’</li><li>bash的配置文件<ul><li>~/.bashrc<ul><li>source ~/.bashrc</li></ul></li></ul></li></ul></li></ul></li><li>上传和下载<ul><li>scp<ul><li>上传:将本地文件复制到远程</li><li>下载:将远程文件复制到本地</li><li>本地路径<ul><li>绝对路径</li><li>相对路径</li></ul></li><li>远程路径<ul><li>用户名@IP:绝对路径</li></ul></li></ul></li></ul></li><li>打包和压缩<ul><li>tar<ul><li>主选项<ul><li>c: 创建</li><li>x: 释放</li><li>r: 追加</li><li>t: 查看</li></ul></li><li>辅选项<ul><li>v: verbose<ul><li>显示详细过程</li></ul></li><li>f<ul><li>指定包裹的名字</li></ul></li><li>z<ul><li>采用gzip算法进行压缩和解压缩</li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<categories>
<category> Tools </category>
</categories>
<tags>
<tag> Shell </tag>
</tags>
</entry>
<entry>
<title>c programming</title>
<link href="/2024/09/18/c-programming/"/>
<url>/2024/09/18/c-programming/</url>
<content type="html"><![CDATA[<hr><ul><li>C语言<ul><li>程序是如何生成的<ul><li>预处理<ul><li>执行预处理指令<ul><li>#include<ul><li>头文件包含<ul><li>文本替换</li></ul></li></ul></li><li>#define N 5<ul><li>宏定义<ul><li>文本替换</li></ul></li></ul></li><li>#define FOO(x) (1 + (x) * (x))<ul><li>宏函数<ul><li>原理:用“实参”替换“形参”, 文本替换</li><li>注意事项<ul><li>左括号应该紧贴宏函数的名字</li><li>参数要加括号</li><li>表达式要加括号</li></ul></li><li>好处<ul><li>避免函数调用的开销<ul><li>用来替换频繁调用且简短的函数</li></ul></li><li>提供一定的宏编程能力</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>编译<ul><li>将预处理后的文件翻译成汇编代码</li></ul></li><li>汇编<ul><li>将汇编代码翻译成机器代码,生成目标文件</li></ul></li><li>链接<ul><li>将目标文件和库文件链接在一起,生成可执行程序</li></ul></li></ul></li><li>进程的虚拟内存空间<ul><li>内核</li><li>栈</li><li>堆</li><li>数据段</li><li>代码段</li></ul></li><li>变量<ul><li>三要素<ul><li>变量名<ul><li>引用变量绑定的值</li></ul></li><li>类型<ul><li>规定了值的取值范围<ul><li>编码</li><li>长度</li></ul></li><li>限定了值的操作</li></ul></li><li>值</li></ul></li></ul></li><li>格式化输入和输出<ul><li>输入输出模型</li><li>printf<ul><li>格式:printf(格式串,表达式1, …)</li><li>原理:打印格式串中的内容,并用后面表达式的值替换转换说明。</li><li>格式串<ul><li>普通字符<ul><li>原样输出</li></ul></li><li>转换说明<ul><li>%m.pX | %-m.pX</li><li>m.p<ul><li>控制输出格式<ul><li>m: 最小字段宽度</li><li>p: 精度。具体含义依赖于后面的X<ul><li>d: 最少显示数字的个数,不够前面补0</li><li>f, lf:显示小数点后几位</li></ul></li></ul></li></ul></li><li>X<ul><li>如何转换类型</li></ul></li></ul></li></ul></li></ul></li><li>scanf<ul><li>格式:scanf(格式串,表达式1, ….)</li><li>原理<ul><li>模式匹配函数 (格式串 <–> stdin中的字符)</li><li>从左到右,根据格式串,匹配stdin中的字符;如果匹配成功,继续匹配下一项;如果匹配失败,立刻返回;返回值表示成功匹配的转换说明个数。</li></ul></li><li>格式串<ul><li>普通字符<ul><li>精确匹配</li></ul></li><li>空白字符 (‘ ‘, ‘\n’, ‘\t’, ‘\v’, ‘\f’)<ul><li>匹配任意多个空白字符,包括0个</li></ul></li><li>转换说明<ul><li>%d: 忽略前置的空白字符,匹配一个有符号的十进制整数</li><li>%f: 忽略前置的空白字符,匹配一个浮点数</li><li>%c: 匹配一个字符</li></ul></li></ul></li></ul></li></ul></li><li>基本数据类型<ul><li>整数<ul><li>无符号整数<ul><li>编码<ul><li>二进制编码</li></ul></li></ul></li><li>有符号整数<ul><li>编码<ul><li>补码<ul><li>x + (~x) = 11…1 = -1</li><li>x + (-x) = 100…0 = 0</li></ul></li></ul></li></ul></li></ul></li><li>浮点数<ul><li>编码<ul><li>IEEE 754</li></ul></li><li>不精确的</li></ul></li><li>字符<ul><li>编码<ul><li>ASCII<ul><li>‘\0’: 0</li><li>‘ ‘: 32</li><li>‘0’: 48</li><li>‘A’: 65</li><li>‘a’: 97</li></ul></li></ul></li><li>值的书写方式<ul><li>字符转义序列<ul><li>\n</li><li>\r</li><li>\t</li><li>\</li><li>'</li><li>"</li></ul></li><li>数字转义序列<ul><li>八进制<ul><li>以\开头,后面接最多三个八进制数字<ul><li>\101</li></ul></li></ul></li><li>十六进制<ul><li>以\x开头,后面接十六进制数字<ul><li>\x41</li></ul></li></ul></li></ul></li></ul></li><li>值的操作<ul><li>C语言把字符类型当作小的整数来处理</li><li><ctype.h><ul><li>字符分类函数</li><li>字符转换函数<ul><li>c = tolower(c)</li><li>c = toupper(c)</li></ul></li></ul></li></ul></li><li>值的读写<ul><li>读<ul><li>scanf + %c</li><li>c = getchar()</li></ul></li><li>写<ul><li>printf + %c</li><li>putchar(c)</li></ul></li></ul></li></ul></li><li>类型转换<ul><li>隐式转换<ul><li><ol><li>整数提升</li></ol></li><li><ol start="2"><li>范围小 –> 范围大:int –> long –> long long –> float –> double</li></ol></li><li><ol start="3"><li>同一转换等级,有符号 –> 无符号</li></ol></li></ul></li><li>显示转换<ul><li>格式:(type-name) expression</li><li>使用场景<ul><li><ol><li>求浮点数的小数部分</li></ol></li><li><ol start="2"><li>说明注释,注意这里发生了类型转换,可能会丢失精度。</li></ol></li><li><ol start="3"><li>避免溢出</li></ol></li><li><ol start="4"><li>精确控制类型转换</li></ol></li></ul></li></ul></li></ul></li><li>给类型定义别名<ul><li>格式:typedef 类型 别名;</li><li>好处<ul><li>提高代码的可读性</li><li>提高代码的可移植性</li></ul></li></ul></li><li>sizeof运算符<ul><li>作用:计算某一类型值所占的内存长度 (以字节为单位)</li></ul></li></ul></li><li>表达式<ul><li>计算某个值的公式,最简单的表达式:变量和常量</li><li>运算符:连接表达式,创建更复杂的表达式<ul><li>优先级</li><li>结合性</li></ul></li><li>赋值<ul><li>v = expr</li><li>值:赋值后,左边表达式的值 (v)</li><li>副作用:改变了 v 的值</li></ul></li><li>算术<ul><li>两个整数相除,结果为整数 (向0取整)</li><li>浮点数不支持 % 运算</li><li>余数可能为负,它的符号和被除数相同</li></ul></li><li>自增和自减<ul><li>i++<ul><li>值:原来的i</li><li>副作用:i自增</li></ul></li><li>++i<ul><li>值:原来的 i 加 1</li><li>副作用:i自增</li></ul></li></ul></li><li>位运算<ul><li>移位<ul><li><<: 左移n位,相当于乘以2的n次方</li><li><code>></code>>:右移n位,相当于除以2的n次方</li><li>注意事项<ul><li>不要对有符号数进行右移运算</li><li><<和>>没有副作用</li></ul></li></ul></li><li>按位<ul><li>~</li><li>&</li><li>|</li><li>^<ul><li>a ^ a = 0</li><li>a ^ 0 = a</li><li>a ^ b = b ^ a</li><li>(a ^ b) ^ c = a ^ (b ^ c)</li></ul></li></ul></li><li>常见的面试题<ul><li><ol><li>判断一个数是否为奇数</li></ol><ul><li>n & 0x1</li></ul></li><li><ol start="2"><li>判断一个数是否是2的幂</li></ol><ul><li>(n & n-1) == 0</li></ul></li><li><ol start="3"><li>求一个数的 last set bit</li></ol><ul><li>n & -n</li></ul></li><li><ol start="4"><li>交换两个整数 (不使用中间临时变量)</li></ol></li><li><ol start="5"><li>找单独的数</li></ol></li><li><ol start="6"><li>找两个单独的数</li></ol></li></ul></li></ul></li></ul></li><li>语句<ul><li>表达式语句</li><li>分支语句<ul><li>if语句</li><li>switch语句<ul><li>注意事项<ul><li><ol><li>switch 后面的表达式必须是整数 (字符、枚举)</li></ol></li><li><ol start="2"><li>多个 case 标签可以共用一组语句</li></ol></li><li><ol start="3"><li>警惕 case 穿透现象</li></ol></li></ul></li></ul></li></ul></li><li>循环语句<ul><li>while (expr) statement<ul><li>在循环体前设置退出点</li></ul></li><li>do statement while (expr);<ul><li>在循环体后设置退出点</li></ul></li><li>for (初始语句 expr2; expr3) statement<ul><li>在循环体前设置退出点</li></ul></li></ul></li><li>跳转语句<ul><li>break</li><li>continue</li><li>goto</li><li>return</li></ul></li><li>空语句<ul><li>;</li></ul></li><li>复合语句<ul><li>{ statements }</li></ul></li></ul></li><li>数组<ul><li>一维数组<ul><li>内存模型<ul><li>a. 一片连续的内存空间,并且这片空间又被划分为大小相等的小空间<ul><li>目的是为了随机访问数组的元素</li></ul></li><li>b. 为什么数组的下标一般从 0 开始?<ul><li>寻址公式:i_addr = base_addr + i * sizeof(elem)</li></ul></li><li>c. 为什么一般来说数组的效率高于链表?<ul><li>数组是连续的,所以数组的空间局部性更好</li><li>数组占用的内存空间比链表少</li></ul></li></ul></li><li>定义和初始化<ul><li>int arr[] = {1, 2, 3, 4, 5}<ul><li>变量名:arr</li><li>类型:int[5]</li></ul></li></ul></li><li>操作<ul><li>取下标 []</li></ul></li><li>输入和输出<ul><li>数组和for循环是一对好伙伴</li></ul></li><li>求数组的长度<ul><li>SIZE(a) (sizeof(a) / sizeof(a[0]))</li></ul></li></ul></li><li>二维数组<ul><li>本质:元素是一维数组的数组</li><li>定义和初始化<ul><li>int matrix[3][4] = { {1, 2, 3, 4}, {2, 2, 3, 4}, {3, 2, 3, 4}}<ul><li>matrix: int[3][4]</li><li>matrix[1]: int[4]</li><li>matrix[1][1]: int</li></ul></li></ul></li><li>操作<ul><li>取下标 []</li></ul></li><li>输入和输出<ul><li>二维数组和嵌套的for循环是一对好伙伴</li></ul></li></ul></li><li>常量数组<ul><li>const int arr[] = {1, 2, 3, 4};</li><li>常量数组的元素不能被修改<ul><li>一般用常量数组存储不会发生改变的数据 (静态数据)</li></ul></li><li>好处<ul><li>安全</li><li>有利于编译器优化程序</li></ul></li></ul></li></ul></li><li>函数<ul><li>Function<ul><li>函数的功能应该越单一越好;函数的实现越高效越好</li></ul></li><li>C语言是一门面向过程 (函数) 的语言<ul><li>函数是C语言程序的基本构建组件,C语言程序是由函数之间的相互调用完成的。</li></ul></li><li>语法结构<ul><li>函数的声明<ul><li>bool is_prime(int n);</li></ul></li><li>函数的定义<ul><li>bool is_prime(int n) { … }</li></ul></li><li>函数的调用<ul><li>is_prime(34)</li></ul></li><li>函数指针<ul><li>is_prime</li></ul></li></ul></li><li>参数传递<ul><li>实际参数 –> 形式参数</li><li>值传递 (复制)<ul><li>局限性:不能被调函数中修改主调函数中的值<ul><li>解决方案:指针</li></ul></li><li>特例:数组作为参数传递时,会退化成指向它第一个元素的指针。<ul><li>优点<ul><li>避免大量数据的复制</li><li>避免值传递的局限性</li><li>可以让函数调用更灵活</li></ul></li><li>缺点:丢失了长度信息</li></ul></li></ul></li></ul></li><li>局部变量<ul><li>作用域:能够引用变量的文本区域;作用于编译期<ul><li>块作用域</li></ul></li><li>存储期限:变量”存活”的时间长度;作用于运行时<ul><li>默认为自动存储期限</li><li>通过static关键字,修改为静态存储期限</li></ul></li></ul></li><li>外部变量<ul><li>作用域<ul><li>文本作用域</li></ul></li><li>存储期限<ul><li>静态存储期限</li></ul></li></ul></li><li>递归<ul><li>名字<ul><li>recursion<ul><li>走重复的路<ul><li>一条代码路径执行了一遍又一遍</li></ul></li></ul></li><li>递归<ul><li>递<ul><li>把大问题分解成若干个子问题,子问题和大问题的求解方式一致,只是问题规模不一致。</li></ul></li><li>归<ul><li>把子问题的解合并成大问题的解</li></ul></li></ul></li></ul></li><li>例子<ul><li>电影院的例子</li><li>Fibnacci数列<ul><li>不是所有具有递归结构的问题,都适合用递归求解</li></ul></li><li>汉诺塔<ul><li>边界条件</li><li>递归关系</li></ul></li></ul></li></ul></li></ul></li><li>指针<ul><li>指针基础<ul><li>概念<ul><li>地址:字节是计算机最小的寻址单位;每个字节都有地址</li><li>变量的地址:变量首字节的地址<ul><li>&x</li></ul></li><li>指针就是地址</li><li>指针变量:存放地址值的变量</li><li>野指针<ul><li>不知道指向哪个数据的指针</li><li>危害:对野指针进行解引用,会导致未定义的行为</li></ul></li><li>空指针<ul><li>不指向任何对象的指针,在C语言中用NULL表示</li></ul></li></ul></li><li>语法<ul><li>定义<ul><li>int* p;<ul><li>变量名:p</li><li>类型:int *</li></ul></li></ul></li><li>赋值<ul><li>p = &i;</li><li>p = q;</li><li>p = NULL;</li></ul></li></ul></li><li>基本操作<ul><li>解引用:*<ul><li>i: 直接访问,访问内存一次</li><li>*p: 间接访问,访问内存两次</li></ul></li></ul></li><li>应用<ul><li>作为参数<ul><li>在被调函数中,通过指针变量 ,修改主调函数中的值</li><li>传入参数:const int* p<ul><li>不能通过指针变量 ,修改主调函数中的值</li></ul></li><li>传出参数: int* p<ul><li>通过指针变量 ,修改主调函数中的值<ul><li>作为返回值来用</li></ul></li></ul></li></ul></li><li>作为返回值<ul><li>千万不能返回指向当前栈帧区域的指针!</li></ul></li></ul></li><li>const<ul><li>本质:限制变量访问内存的权限</li><li>int *p = &i;<ul><li>p代表的内存:R/W</li><li>*p代表的内存:R/W</li></ul></li><li>const int *p = &i;<ul><li>p代表的内存:R/W</li><li>*p代表的内存:R</li></ul></li><li>int * const p = &i;<ul><li>p代表的内存:R</li><li>*p代表的内存:R/W</li></ul></li><li>const int * const p = &i;<ul><li>p代表的内存:R</li><li>*p代表的内存:R</li></ul></li></ul></li></ul></li><li>指针和数组的关系<ul><li>a. 可以利用指针处理数组<ul><li>指针的算术运算<ul><li>指针加上一个整数</li><li>指针减去一个整数</li><li>两个指针相减<ul><li>定义指针的比较运算<ul><li>p == q 等价于 p - q == 0</li><li>p > q 等价于 p - q > 0</li><li>p < q 等价于 p - q < 0</li></ul></li></ul></li></ul></li><li>++和*的组合<ul><li><em>p++,</em>(p++)<ul><li>值:*p</li><li>副作用:p自增</li></ul></li><li>(*p)++<ul><li>值:*p</li><li>副作用:*p 自增</li></ul></li><li>++*p, ++(*p)<ul><li>值:*p + 1</li><li>副作用:*p自增</li></ul></li><li>*++p, *(++p)<ul><li>值:*(p + 1)</li><li>副作用:p自增</li></ul></li></ul></li></ul></li><li>b. 数组名可以作为指向它第一个元素的指针</li><li>c. 指针也支持取下标运算<ul><li>p[i] = *(p + i) = *(i + p) = i[p]</li></ul></li></ul></li><li>指针的高级应用<ul><li>动态内存分配<ul><li>为什么需要动态内存分配<ul><li>栈帧的大小需要在编译时确定<ul><li>栈空间不能存具有动态大小的数据</li></ul></li><li>栈的大小是受限的<ul><li>栈空间不能存很大的数据</li></ul></li><li>每个线程都有自己的栈<ul><li>栈空间不适合存放线程之间共享的数据</li></ul></li></ul></li><li>基础概念<ul><li>空指针:不指向任何对象的指针<ul><li>不能解引用</li></ul></li><li>悬空指针<ul><li>指向已释放的堆内存空间,是野指针的一种</li></ul></li><li>通用指针类型:void*<ul><li>不能解引用</li><li>可以和其它类型的指针相互转换 (隐式转换)</li></ul></li></ul></li><li>API<ul><li>void* malloc(size_t size)</li><li>void* calloc(size_t n, size_t size)</li><li>void* realloc(void* ptr, size_t size)<ul><li>注意事项:ptr一定是malloc, calloc, realloc 的返回值</li></ul></li><li>void free(void* ptr)<ul><li>注意事项<ul><li>ptr一定是malloc, calloc, realloc 的返回值</li><li>double free</li><li>use after free</li><li>内存泄漏</li></ul></li></ul></li></ul></li></ul></li><li>二级指针<ul><li>本质:指向一级指针变量的指针</li><li>声明<ul><li>Node** p;</li></ul></li><li>指针作为参数,传递一级指针,还是传递二级指针<ul><li>宗旨:想修改哪个变量,就传递那个变量的地址</li><li>传值<ul><li>const int* p</li></ul></li><li>修改指针变量指向的对象<ul><li>int* p</li></ul></li><li>修改指针变量的值 (指向)</li></ul></li></ul></li><li>函数指针<ul><li>概念:指向函数的指针</li><li>语法<ul><li>定义函数指针变量<ul><li>void (*func) (int, int)</li></ul></li><li>函数地址<ul><li>foo</li><li>&foo</li></ul></li><li>通过函数指针调用函数<ul><li>func(实参列表)</li><li>(*func)(实参列表)</li></ul></li></ul></li><li>作用<ul><li>C语言是通过函数指针,来模拟函数式编程<ul><li>解耦合</li><li>易组合</li></ul></li></ul></li><li>qsort<ul><li>作用:对任意数组排序</li><li>参数<ul><li>base<ul><li>数组的起始地址</li></ul></li><li>nmemb<ul><li>待排序元素的个数</li></ul></li><li>size<ul><li>每个元素的大小</li></ul></li><li>int (<em>compare) (const void</em> p1, const void* p2)<ul><li>钩子函数</li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li>字符串<ul><li>字符串字面值<ul><li>书写方式<ul><li>三种</li></ul></li><li>内存布局<ul><li>代码段 (只读)</li><li>以字符数组的形式存储,以 \0 字符结尾</li></ul></li><li>操作<ul><li>将字符串字面值看作是字符常量数组<ul><li>取下标</li></ul></li></ul></li></ul></li><li>字符串变量<ul><li>内存模型:C语言没有真正的字符串类型,字符串变量依赖字符数组存在。</li><li>定义并初始化<ul><li>char s[] = { ‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’ };</li><li>char s[] = “Hello”;<ul><li>推荐1</li></ul></li><li>char s[10] = “Hello”;<ul><li>推荐2</li></ul></li><li>char s[6] = “Hello”;</li><li>char s[5] = “Hello”;<ul><li>不是字符串</li></ul></li></ul></li><li>操作<ul><li>字符串变量依赖字符数组存在<ul><li>取下标 []</li></ul></li><li><string.h><ul><li>strlen<ul><li>求字符串的长度,不计算空字符 ‘\0’</li></ul></li><li>strcpy, strncpy<ul><li>字符串复制</li></ul></li><li>strcat, strncat<ul><li>字符串的拼接</li></ul></li><li>strcmp<ul><li>字符串的比较<ul><li>返回值<ul><li><0: s1 < s2</li><li>=0: s1 == s2</li><li><blockquote><p>0: s1 > s2</p></blockquote></li></ul></li><li>比较规则<ul><li>字典序 (ASCII)</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>输入输出<ul><li>输出<ul><li>printf + %s</li><li>puts</li></ul></li><li>输入<ul><li>scanf + %s<ul><li>忽略前置的空白字符,读取字符存入字符数组,遇到空白字符结束<ul><li>读入单词</li></ul></li></ul></li><li>gets<ul><li>读取一行数据,并把’\n’替换成’\0’<ul><li>缺陷:不检查数组是否越界</li></ul></li></ul></li><li>fgets(str, n, stdin)<ul><li>读取一行数据,存储换行符,并在换行符后面添加 ‘\0’</li></ul></li></ul></li></ul></li></ul></li><li>字符串数组<ul><li>二维字符数组<ul><li>可能浪费内存空间</li><li>不灵活</li></ul></li><li>字符指针数组<ul><li>非常灵活</li></ul></li><li>命令行参数<ul><li>操作系统传递给 main 函数的参数<ul><li>int main(int argc, char* argv[])</li></ul></li><li>参数转换<ul><li>sscanf(argv[i], ….)</li></ul></li><li>作用<ul><li>传递不同参数,程序展示不同的行为<ul><li>ls</li><li>ls -l</li></ul></li><li>有利于编写通用的工具<ul><li>cp src dst</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>结构体<ul><li>定义结构体类型</li><li>定义结构体类型的变量,并赋初始值</li><li>内存模型<ul><li>a. 一片连续的内存空间</li><li>b. 成员是按声明的顺序依次存放</li><li>c. 在结构体的中间或者末尾,可能会出现填充</li></ul></li><li>操作<ul><li>访问成员<ul><li>s.name</li></ul></li><li>赋值 (结构体的复制)<ul><li>s2 = s1</li><li>结构体作为参数或返回值时,会涉及整个结构体的复制!<ul><li>传递指向结构体的指针</li></ul></li></ul></li><li>语法糖<ul><li>p->name 等价于 (*p).name</li></ul></li></ul></li><li>给结构体类型定义别名<ul><li>不要给指针类型定义别名!</li></ul></li></ul></li><li>枚举<ul><li>语法<ul><li>定义枚举类型</li><li>定义枚举类型的变量</li></ul></li><li>作用:表示一些离散值 (扑克牌的花色、状态、类别…)</li><li>枚举值是整数!</li></ul></li><li>文件流<ul><li>模型<ul><li><ol><li>流模型</li></ol><ul><li>将读端和写端解耦</li><li>程序员不需要手动care文件的位置</li></ul></li><li><ol start="2"><li>程序员视角下的文件</li></ol><ul><li>字节序列</li><li>pos: 下一个读写的字节</li><li>EOF: 标识着文件的末尾</li></ul></li><li><ol start="3"><li>文本文件 & 二进制文件</li></ol></li><li><ol start="4"><li>缓冲区类型</li></ol><ul><li>满缓冲</li><li>行缓冲<ul><li>stdin, stdout</li></ul></li><li>无缓冲<ul><li>stderr<ul><li>打印错误信息</li></ul></li></ul></li></ul></li><li><ol start="5"><li>标准流</li></ol><ul><li>stdin: 0</li><li>stdout: 1</li><li>stderr: 2</li></ul></li></ul></li><li>API<ul><li>打开<ul><li>fopen<ul><li>filename<ul><li>绝对路径</li><li>相对路径</li></ul></li><li>mode<ul><li>r<ul><li>存在<ul><li>不会清空</li></ul></li><li>不存在<ul><li>打开失败</li></ul></li></ul></li><li>w<ul><li>存在<ul><li>清空</li></ul></li><li>不存在<ul><li>创建</li></ul></li></ul></li><li>a<ul><li>存在<ul><li>不清空</li></ul></li><li>不存在<ul><li>创建</li></ul></li></ul></li></ul></li></ul></li></ul></li><li>关闭<ul><li>fclose</li></ul></li><li>读写<ul><li>以文本方式<ul><li>a. 一个字符一个字符<ul><li>fgetc (stream)</li><li>fputc (c, stream)</li></ul></li><li>b. 一行一行<ul><li>fgets<ul><li>str</li><li>count</li><li>stream</li></ul></li><li>fputs<ul><li>str</li><li>stream</li></ul></li></ul></li><li>c. 格式化地读写<ul><li>fscanf</li><li>fprintf</li></ul></li></ul></li><li>以二进制方式<ul><li>fread</li><li>fwrite</li></ul></li></ul></li><li>移动文件位置<ul><li>fseek<ul><li>stream</li><li>offset</li><li>whence<ul><li>参照点<ul><li>SEEK_SET</li><li>SEEK_CUR</li><li>SEEK_END</li></ul></li></ul></li></ul></li><li>ftell</li><li>rewind</li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<categories>
<category> Language </category>
</categories>
<tags>
<tag> C </tag>
</tags>
</entry>
<entry>
<title>Data structures and algorithms</title>
<link href="/2024/09/18/Data-structures-and-algorithms/"/>
<url>/2024/09/18/Data-structures-and-algorithms/</url>
<content type="html"><![CDATA[<ul><li>数据结构和算法<ul><li>数据结构<ul><li>集合<ul><li>哈希表<ul><li>模型</li><li>基本操作<ul><li>添加<ul><li>hashmap_put(map, key, val)</li></ul></li><li>删除<ul><li>hashmap_delete(map, key)</li></ul></li><li>查找<ul><li>hashmap_get(map, key)</li></ul></li><li>遍历</li></ul></li><li>实现<ul><li>哈希函数<ul><li>数据的指纹<ul><li>正向快速</li><li>逆向困难</li><li>平均分布</li><li>哈希冲突的概率小</li><li>对数据敏感</li></ul></li><li>散列<ul><li>正向快速</li><li>平均分布</li></ul></li></ul></li><li>哈希桶<ul><li>拉链法</li><li>开放地址法<ul><li>线性探测</li><li>平方探测</li><li>再散列法</li></ul></li></ul></li></ul></li><li>应用<ul><li>C++: unordered_map, unordered_set</li><li>Java: HashMap, HashSet</li><li>redis<ul><li>键值数据库</li></ul></li></ul></li></ul></li></ul></li><li>线性结构<ul><li>动态数组</li><li>链表<ul><li>单向链表<ul><li>模型</li><li>基本操作<ul><li>增<ul><li>在某个结点后面添加</li></ul></li><li>删<ul><li>在某个结点后面删除</li></ul></li><li>查<ul><li>根据索引查找值<ul><li>平均遍历 n/2 个结点</li></ul></li><li>查找与特定值相等的结点<ul><li>无序</li><li>有序<ul><li>记录上一次查找的位置</li></ul></li></ul></li></ul></li><li>遍历<ul><li>正向遍历</li></ul></li></ul></li><li>实现<ul><li>C++: forward_list</li></ul></li></ul></li><li>双向链表<ul><li>模型</li><li>基本操作<ul><li>增<ul><li>在某个结点后面添加</li><li>在某个结点前面添加</li></ul></li><li>删<ul><li>在某个结点后面删除</li><li>在某个结点前面删除</li><li>删除当前结点</li></ul></li><li>查<ul><li>根据索引查找值<ul><li>平均遍历 n/4 个结点</li></ul></li><li>查找与特定值相等的结点<ul><li>无序</li><li>有序<ul><li>记录上一次查找的位置</li></ul></li></ul></li></ul></li><li>遍历<ul><li>正向遍历</li><li>逆向遍历</li></ul></li></ul></li><li>实现</li><li>应用<ul><li>Java: LinkedList</li><li>C++: list</li></ul></li></ul></li></ul></li><li>栈<ul><li>模型<ul><li>操作受限的线性表,只能在一端添加或删除<ul><li>LIFO</li></ul></li></ul></li><li>基本操作<ul><li>入栈</li><li>出栈</li><li>查看栈顶元素</li><li>判空</li></ul></li><li>实现<ul><li>动态数组</li><li>链表</li></ul></li><li>应用<ul><li>LIFO<ul><li>函数调用</li><li>括号匹配</li></ul></li><li>单调栈 (表示优先级)<ul><li>表达式求值</li></ul></li><li>记录轨迹<ul><li>浏览器的前进后退功能</li><li>深度优先搜索</li><li>回溯</li></ul></li></ul></li></ul></li><li>队列<ul><li>模型<ul><li>操作受限的线性表,在一端添加,在另一端删除<ul><li>FIFO</li></ul></li></ul></li><li>基本操作<ul><li>入队列</li><li>出队列</li><li>查看队头元素</li><li>判空</li><li>判满</li></ul></li><li>实现<ul><li>动态数组 (循环数组)</li><li>链表</li></ul></li><li>应用<ul><li>缓冲区</li><li>缓存<ul><li>公平性</li></ul></li><li>广度优先搜索<ul><li>三度好友</li></ul></li></ul></li></ul></li></ul></li><li>树<ul><li>BST<ul><li>模型<ul><li>key值能进行比较:L < D < R</li><li>左子树也是一棵二叉搜素树</li><li>右子树也是一棵二叉搜素树</li></ul></li><li>基本操作<ul><li>添加</li><li>删除</li><li>查找</li><li>遍历<ul><li>深度优先遍历<ul><li>先序遍历<ul><li>D L R</li></ul></li><li>中序遍历<ul><li>L D R</li></ul></li><li>后序遍历<ul><li>L R D</li></ul></li></ul></li><li>广度优先遍历<ul><li>层次遍历</li></ul></li></ul></li></ul></li><li>实现</li><li>作用<ul><li>存储动态的有序数据</li></ul></li><li>应用<ul><li>C++: map, set</li><li>Java: TreeMap, TreeSet</li><li>epoll</li></ul></li></ul></li></ul></li><li>图</li></ul></li><li>算法<ul><li>排序<ul><li>选择排序<ul><li>时间:O(n^2)<ul><li>对数据不敏感<ul><li>比较:(n-1) + (n-2) + … + 1</li><li>交换:n-1</li></ul></li></ul></li><li>空间<ul><li>O(1)</li></ul></li><li>稳定性<ul><li>不稳定</li></ul></li></ul></li><li>冒泡排序<ul><li>时间<ul><li>最好情况: O(n)<ul><li>原数组有序<ul><li>比较:n-1</li><li>交换:0</li></ul></li></ul></li><li>平均情况:O(n^2)<ul><li>比较:大于等于交换的次数,小于等于 n(n-1)/2</li><li>交换:等于逆序对 n(n-1) / 4</li></ul></li><li>最坏情况:O(n^2)<ul><li>原数组逆序<ul><li>比较:1 + 2 + … + (n-1)</li><li>交换:1 + 2 + … + (n-1)</li></ul></li></ul></li></ul></li><li>空间<ul><li>O(1)</li></ul></li><li>稳定性<ul><li>稳定</li></ul></li></ul></li><li>插入排序<ul><li>时间<ul><li>最好情况: O(n)<ul><li>原数组有序<ul><li>比较:n-1</li><li>交换:0</li></ul></li></ul></li><li>平均情况:O(n^2)<ul><li>比较:大于等于交换的次数,小于等于 n(n-1)/2</li><li>交换:等于逆序对 n(n-1) / 4</li></ul></li><li>最坏情况:O(n^2)<ul><li>原数组逆序<ul><li>比较:1 + 2 + … + (n-1)</li><li>交换:1 + 2 + … + (n-1)</li></ul></li></ul></li></ul></li><li>空间<ul><li>O(1)</li></ul></li><li>稳定性<ul><li>稳定</li></ul></li></ul></li><li>希尔排序<ul><li>类比:多人轮流抓牌</li><li>时间<ul><li>和gap序列相关,一般来说小于 O(n^2)</li></ul></li><li>空间<ul><li>O(1)</li></ul></li><li>稳定性<ul><li>不稳定</li></ul></li></ul></li><li>归并排序<ul><li>时间<ul><li>对数据不敏感:O(nlogn)</li></ul></li><li>空间<ul><li>O(n) + O(logn) = O(n)</li></ul></li><li>稳定性<ul><li>稳定</li></ul></li></ul></li><li>快速排序<ul><li>时间<ul><li>最好情况:O(nlogn)<ul><li>每次分区,基准值都位于中间</li></ul></li><li>最坏情况:O(n^2)<ul><li>每次分区,基准值都位于两端</li></ul></li><li>平均情况:O(nlogn)</li></ul></li><li>空间<ul><li>O(log n)</li></ul></li><li>稳定性<ul><li>不稳定</li></ul></li><li>改进方法<ul><li>基准值选取<ul><li>随机选取</li><li>选择3个或5个元素的中位数</li></ul></li><li>如果相等的元素比较多<ul><li>三向分区</li></ul></li><li>当区间元素比较少时,比如小于64<ul><li>插入排序</li></ul></li><li>…</li></ul></li></ul></li><li>堆排序<ul><li>时间<ul><li>对数据不敏感:O(nlogn)</li></ul></li><li>空间<ul><li>O(1)</li></ul></li><li>稳定性<ul><li>不稳定</li></ul></li></ul></li></ul></li><li>二分查找<ul><li>前提<ul><li>随机访问元素<ul><li>底层数据结构是数组</li></ul></li><li>有序<ul><li>通过一次比较,可以丢掉一半区间</li></ul></li></ul></li><li>实现<ul><li>递归 (了解)</li><li>循环 (必须掌握)</li></ul></li><li>二分查找的变种<ul><li><ol><li>查找第一个等于key值的元素</li></ol></li><li><ol start="2"><li>查找最后一个等于key值的元素</li></ol></li><li><ol start="3"><li>查找第一个大于等于key值的元素</li></ol></li><li><ol start="4"><li>查找最后一个小于key值的元素</li></ol></li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<tags>
<tag> Data structures </tag>
<tag> Algorithms </tag>
</tags>
</entry>
<entry>
<title>something about vim</title>
<link href="/2024/09/17/something-about-vim/"/>
<url>/2024/09/17/something-about-vim/</url>
<content type="html"><![CDATA[<ul><li>Vim<ul><li>设计原理<ul><li>简洁</li><li>高速</li><li>组合<ul><li>action + motion</li><li>action + textobj</li></ul></li><li>最短前缀原则</li></ul></li><li>多模式编辑器<ul><li>Normal<ul><li>作用:执行命令</li><li>长命令 (底部命令):以:开头,以[Enter]结尾</li><li>短命令:a, dd, …</li></ul></li><li>Insert<ul><li>作用:编辑文本</li><li>i, I, a, A, o, O</li></ul></li><li>Visual<ul><li>作用:选择文本</li><li>v<ul><li>行选</li></ul></li><li>[Ctrl] + v<ul><li>竖选</li></ul></li></ul></li><li>…..</li></ul></li><li>命令<ul><li>移动光标<ul><li>h</li><li>j</li><li>k</li><li>l</li><li>[n]-</li><li>[n]+</li><li>:[n] | [n]G</li><li>gg</li><li>G</li><li>w</li><li>b</li><li>W</li><li>B</li><li>^</li><li>$</li><li>t字符</li><li>T字符</li><li>f字符</li><li>F字符</li></ul></li><li>文本对象<ul><li>i(, i), a(, a)</li><li>i[, i], a[, a]</li><li>i{, i}, a{, a}</li><li>i<, i>, a<, a></li><li>i”, a”</li><li>i’, a’</li><li>ip, ap<ul><li>p: paragraph<ul><li>边界:空行</li></ul></li></ul></li></ul></li><li>动作<ul><li>d (delete)</li><li>y (yank)</li><li>c (change)</li></ul></li><li>粘贴<ul><li>p / P (paste)</li></ul></li><li>撤销<ul><li>u (undo)</li></ul></li><li>恢复<ul><li>[ctrl] + r</li></ul></li><li>查找<ul><li>/pattern</li><li>?pattern</li><li>n / N</li></ul></li><li>替换<ul><li>格式<ul><li>:[范围]s/pattern/replace/[选项]</li></ul></li><li>:s/pattern/replace</li><li>:s/pattern/replace/g</li><li>:m,n s/pattern/replace/g</li><li>:% s/pattern/replace/g</li></ul></li><li>注释<ul><li>gcc<ul><li>注释一行,或者取消注释一行</li></ul></li></ul></li><li>代码对齐<ul><li>gg = G</li></ul></li></ul></li><li>对文件的操作<ul><li>:write</li><li>:quit</li><li>:wq</li><li>:q!</li></ul></li><li>多窗口<ul><li>水平分割<ul><li>:split</li><li>:new</li></ul></li><li>垂直分割<ul><li>:vsplit</li><li>:vnew</li></ul></li><li>切换窗口<ul><li>[ctrl] + ww</li></ul></li><li>退出窗口<ul><li>:q</li><li>:qa</li><li>:only</li></ul></li></ul></li><li>配置文件<ul><li>~/.vimrc</li><li>单独安装youcompleteme<ul><li>rm -rf ~/.vim/plugged/YouCompleteMe</li><li>sudo apt install vim-youcompleteme</li><li>vim-addons install youcompleteme</li></ul></li></ul></li></ul></li></ul>]]></content>
<categories>
<category> Tools </category>
</categories>
<tags>
<tag> Vim </tag>
</tags>
</entry>
<entry>
<title>log for this week</title>
<link href="/2024/09/08/log-for-this-week/"/>
<url>/2024/09/08/log-for-this-week/</url>
<content type="html"><![CDATA[<p>some tips:使用const修饰符在大部分情况下对程序的性能并没有提升,编译器会对两种写法进行相似的优化,最后生成几乎相同的汇编代码(clang会生成相似的IR)</p><p>使用 <code>const</code> 修饰符在 C/C++ 等编程语言中,通常被认为是一种良好的编程实践,因为它可以提高代码的可读性和可维护性,同时帮助开发者避免一些常见的错误。然而,关于 <code>const</code> 对程序性能的影响,存在一些误解。以下是对这一问题的详细拓展:</p><h3 id="1-const-的主要作用"><a href="#1-const-的主要作用" class="headerlink" title="1. const 的主要作用"></a>1. <strong><code>const</code> 的主要作用</strong></h3><ul><li><strong>代码可读性</strong>:<code>const</code> 可以明确标识出哪些变量或对象在程序执行过程中不会被修改,从而帮助开发者更好地理解代码的意图。</li><li><strong>防止误修改</strong>:通过使用 <code>const</code>,编译器可以在编译时捕获到对常量变量的修改操作,从而避免潜在的运行时错误。</li><li><strong>接口设计</strong>:在函数参数或返回值中使用 <code>const</code> 可以明确函数的输入输出约束,增强接口的清晰性和安全性。</li></ul><h3 id="2-const-对性能的影响"><a href="#2-const-对性能的影响" class="headerlink" title="2. const 对性能的影响"></a>2. <strong><code>const</code> 对性能的影响</strong></h3><ul><li><strong>编译器的优化能力</strong>:现代编译器(如 GCC、Clang、MSVC 等)具有非常强大的优化能力。对于 <code>const</code> 修饰的变量,编译器通常会进行与普通变量相似的优化。例如,编译器可能会将 <code>const</code> 变量直接内联到代码中,或者将其存储在寄存器中以加快访问速度。</li><li><strong>生成的汇编代码</strong>:在大多数情况下,使用 <code>const</code> 修饰符并不会显著改变生成的汇编代码。无论是使用 <code>const</code> 还是普通变量,编译器生成的机器代码通常是相似的。例如,Clang 生成的中间表示(IR)对于 <code>const</code> 和非 <code>const</code> 变量的处理方式几乎相同。</li><li><strong>内存访问</strong>:对于 <code>const</code> 修饰的全局变量或静态变量,编译器可能会将其放置在只读内存段(如 <code>.rodata</code> 段),这可以防止程序意外修改这些变量。然而,这种内存布局的变化对性能的影响通常可以忽略不计。</li></ul><h3 id="3-const-在特定场景下的性能影响"><a href="#3-const-在特定场景下的性能影响" class="headerlink" title="3. const 在特定场景下的性能影响"></a>3. <strong><code>const</code> 在特定场景下的性能影响</strong></h3><ul><li><p>**函数参数中的 <code>const</code>**:在函数参数中使用 <code>const</code> 修饰指针或引用时,通常不会对性能产生直接影响。例如:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">const</span> <span class="type">int</span>* ptr)</span></span>;</span><br></pre></td></tr></table></figure><p>这里的 <code>const</code> 只是告诉编译器 <code>ptr</code> 指向的内容不会被修改,但并不会影响函数调用的性能。</p></li><li><p><strong><code>const</code> 成员函数</strong>:在 C++ 中,<code>const</code> 成员函数表示该函数不会修改类的成员变量。这可以帮助编译器进行一些优化,例如在某些情况下避免不必要的内存访问。然而,这种优化通常是非常细微的,对整体性能的影响有限。</p></li></ul><h3 id="4-const-与编译器优化的关系"><a href="#4-const-与编译器优化的关系" class="headerlink" title="4. const 与编译器优化的关系"></a>4. <strong><code>const</code> 与编译器优化的关系</strong></h3><ul><li><strong>常量传播(Constant Propagation)</strong>:编译器可以通过常量传播优化,将 <code>const</code> 变量的值直接替换到使用该变量的地方,从而减少内存访问次数。这种优化不仅适用于 <code>const</code> 变量,也适用于其他常量表达式。</li><li><strong>死代码消除(Dead Code Elimination)</strong>:如果 <code>const</code> 变量在程序中未被使用,编译器可能会将其完全移除,从而减少生成的代码大小。</li><li><strong>内联优化(Inline Optimization)</strong>:对于 <code>const</code> 修饰的内联函数或常量表达式,编译器可能会将其直接内联到调用处,从而减少函数调用的开销。</li></ul><h3 id="5-const-的使用建议"><a href="#5-const-的使用建议" class="headerlink" title="5. const 的使用建议"></a>5. <strong><code>const</code> 的使用建议</strong></h3><ul><li><strong>优先考虑代码清晰性和安全性</strong>:虽然 <code>const</code> 对性能的影响有限,但它可以显著提高代码的可读性和安全性。因此,建议在适当的地方使用 <code>const</code>,而不是仅仅为了性能优化。</li><li>**避免过度使用 <code>const</code>**:在某些情况下,过度使用 <code>const</code> 可能会导致代码变得复杂,尤其是在涉及指针和引用时。因此,使用 <code>const</code> 时应权衡代码的简洁性和安全性。</li></ul><h3 id="6-总结"><a href="#6-总结" class="headerlink" title="6. 总结"></a>6. <strong>总结</strong></h3><ul><li><strong>性能影响</strong>:使用 <code>const</code> 修饰符在大多数情况下对程序的性能没有显著影响,因为现代编译器能够对 <code>const</code> 和非 <code>const</code> 变量进行相似的优化。</li><li><strong>代码质量</strong>:<code>const</code> 的主要价值在于提高代码的可读性、可维护性和安全性,而不是性能优化。</li><li><strong>编译器优化</strong>:编译器在处理 <code>const</code> 变量时,通常会生成与非 <code>const</code> 变量相似的汇编代码或中间表示(IR),因此开发者无需过于担心 <code>const</code> 对性能的影响。</li></ul><p>总之,<code>const</code> 是一个非常有用的工具,但它的主要作用在于代码的清晰性和安全性,而不是性能优化。在编写代码时,应该优先考虑这些方面,而不是过度关注 <code>const</code> 对性能的潜在影响。</p>]]></content>
<tags>
<tag> Tips </tag>
</tags>
</entry>
<entry>
<title>csblog</title>
<link href="/2023/11/11/csblog/"/>
<url>/2023/11/11/csblog/</url>
<content type="html"><![CDATA[<ul><li><a href="https://xiaolincoding.com/">小林coding</a></li><li><a href="https://labuladong.online/algo/">labuladong的算法网站</a></li><li><a href="https://www.programmercarl.com/">代码随想录</a></li><li><a href="https://codetop.cc/home">CodeTop</a></li><li><a href="https://oi-wiki.org/">OI WIKI</a></li><li><a href="https://interviewguide.cn/">阿秀的学习笔记</a></li><li><a href="http://www.cyc2018.xyz/">CS-Notes</a></li><li><a href="https://interview.huihut.com/">C/C++ 技术面试基础知识总结</a></li><li><a href="https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5">cppreference</a></li><li><a href="https://changkun.de/modern-cpp/">现代C++教程</a></li><li><a href="http://jyywiki.cn/OS/2022/">南京大学操作系统</a></li><li><a href="https://15445.courses.cs.cmu.edu/fall2022/">CMU 15-445/645</a></li><li><a href="https://pdos.csail.mit.edu/6.828/2022/index.html">MIT 6.828</a></li><li><a href="https://cs144.github.io/">CS 144</a></li><li><a href="http://www.interdb.jp/pg/index.html">The Internals of PostgreSQL</a></li></ul>]]></content>
<tags>
<tag> blog </tag>
</tags>
</entry>
<entry>
<title>hello</title>
<link href="/2023/10/31/hello/"/>
<url>/2023/10/31/hello/</url>
<content type="html"><![CDATA[<p><del>青春</del>人生不过也就这些日子</p>]]></content>
</entry>
</search>