/
atom.xml
368 lines (209 loc) · 620 KB
/
atom.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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Zam’s Blog</title>
<link href="/atom.xml" rel="self"/>
<link href="http://blog.zam-meow.cn/"/>
<updated>2021-03-22T07:18:35.125Z</updated>
<id>http://blog.zam-meow.cn/</id>
<author>
<name>Zam</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Talk with XiaojunHei</title>
<link href="http://blog.zam-meow.cn/2021/03/22/Talk%20with%20XiaojunHei/"/>
<id>http://blog.zam-meow.cn/2021/03/22/Talk%20with%20XiaojunHei/</id>
<published>2021-03-22T07:12:43.560Z</published>
<updated>2021-03-22T07:18:35.125Z</updated>
<content type="html"><![CDATA[<ul><li>发掘自己的长处</li><li>不要觉得非技术工作很无聊,要乐在其中</li><li>对于科研和工程,要加强认识</li></ul>]]></content>
<summary type="html">
<ul>
<li>发掘自己的长处</li>
<li>不要觉得非技术工作很无聊,要乐在其中</li>
<li>对于科研和工程,要加强认识</li>
</ul>
</summary>
</entry>
<entry>
<title>L3H_Sec 招新-Misc-wp</title>
<link href="http://blog.zam-meow.cn/2020/11/07/L3H_Sec%20%E6%8B%9B%E6%96%B0-Misc-wp/"/>
<id>http://blog.zam-meow.cn/2020/11/07/L3H_Sec%20%E6%8B%9B%E6%96%B0-Misc-wp/</id>
<published>2020-11-07T06:44:34.000Z</published>
<updated>2020-11-07T06:54:20.023Z</updated>
<content type="html"><![CDATA[<p>MC系列之后再更,咕咕咕<br>下午去面试,希望人没事</p><a id="more"></a><h2 id="签到"><a href="#签到" class="headerlink" title="签到"></a>签到</h2><p>没啥好说的,题目信息里签就完事了</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1604718833859-e3db6c2e-c35b-4e97-8622-8856ae10136a.png" alt=""></p><h2 id="ManualCAD"><a href="#ManualCAD" class="headerlink" title="ManualCAD"></a>ManualCAD</h2><p>使用 <code>Adobe illustrator</code> 打开 <code>l3hctf.dwg</code> 文件,发现里边默认显示的是假Flag图层。将该图层隐藏并将真Flag图层显示即可。</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1604718858011-dd5b23b3-ba35-4731-bb8f-ee6a1e393218.png" alt=""></p><h2 id="哦苏!"><a href="#哦苏!" class="headerlink" title="哦苏!"></a>哦苏!</h2><p>下载附件后是一个 <code>.osz</code> 的附件。该文件是 <code>osu!</code> 的一个谱面压缩文件。并且根据题目提示:</p><blockquote><p>咦,好像混进了什么奇怪的东西?</p></blockquote><p>将该附件后缀名改为<code>.zip</code>后打开,发现有一个<code>.osu</code> 文件从文件名到修改时间都十分可疑。</p><p>![](<a href="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604718877956-28605b98-7fd0-4e6b-b05a-bfcfc1e7f927.png#align=left&display=inline&height=378&margin=[object" target="_blank" rel="noopener">https://cdn.nlark.com/yuque/0/2020/png/1118966/1604718877956-28605b98-7fd0-4e6b-b05a-bfcfc1e7f927.png#align=left&display=inline&height=378&margin=[object</a> Object]&originHeight=378&originWidth=821&size=0&status=done&style=none&width=821)</p><p>于是乎直接将 <code>orz.osz</code> 放到 <code>osu!</code> 安装目录下的 <code>Songs</code> 目录下,进入 <code>osu!</code> 的 <code>Edit</code> 模式(别问我为什么不进 <code>Play</code>模式,问就是菜。</p><p>![](<a href="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604719234785-97e889c1-2d74-422a-8cfa-204a04e4c78c.png#align=left&display=inline&height=1081&margin=[object" target="_blank" rel="noopener">https://cdn.nlark.com/yuque/0/2020/png/1118966/1604719234785-97e889c1-2d74-422a-8cfa-204a04e4c78c.png#align=left&display=inline&height=1081&margin=[object</a> Object]&originHeight=1081&originWidth=1920&size=0&status=done&style=none&width=1920)</p><p>发现在歌曲的结尾部分出现了手写的Flag。但是因为歌曲太短的原因,<code>Slider</code> 并没有完全出现。</p><p>![](<a href="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604719235215-387241cd-ab01-489b-9e22-1c40e5b62421.png#align=left&display=inline&height=1081&margin=[object" target="_blank" rel="noopener">https://cdn.nlark.com/yuque/0/2020/png/1118966/1604719235215-387241cd-ab01-489b-9e22-1c40e5b62421.png#align=left&display=inline&height=1081&margin=[object</a> Object]&originHeight=1081&originWidth=1920&size=0&status=done&style=none&width=1920)</p><p>因此根据<a href="https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hit-objects" target="_blank" rel="noopener">OSU Wiki .osu文件格式</a> 中的描述,直接修改原有的 <code>.osu</code> 文件,删除无用的 <code>HitObject</code> 行(通过直接播放都能够得到的Flag的字母),最终结果文件内的内容如图:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1604719234659-3f4a81e4-bc32-4519-9f51-f098b825f149.png" alt=""></p><p>打开<code>osu!</code>进入<code>Edit</code> 模式,得到flag中剩余的字母</p><p>最终得到flag:</p><blockquote><p>l3hctf{hard_music_gam3}</p></blockquote><h2 id="保护知识产权,刻不容缓!"><a href="#保护知识产权,刻不容缓!" class="headerlink" title="保护知识产权,刻不容缓!"></a>保护知识产权,刻不容缓!</h2><p>首先注意到 <code>cookie.json</code> 文件中可疑的 <code>rsaKeys</code> cookie,首先将 <code>value</code> 字段中的 rsa 私钥和公钥都拿去在线 URLDecode,得到如下结果:</p><p>PRIVATE KEY:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">-----BEGIN RSA PRIVATE KEY-----</span><br><span class="line">MIIEoQIBAAKCAQEAo41vZcmeaQTodPfKyUciakkGkgPYaZO6oQxbZrSKWaQ7yblf</span><br><span class="line">wqv8CWYh6UGU5R4q0gzmfhR9HOwSmO5X0gS+gwLEYY1e1Zk4Agaz36sgsx+alePG</span><br><span class="line">aH/IQDjlEqMTAPN0Dfsmo3a4TxjivABpdXihKbRzsu7+iUpTRZdozvgNEDzSWD+A</span><br><span class="line">c7dQRbBF8vLFke50M+fQrFyGD5IAezHQ4SvXHCUJBaT27DBVZ8Z8ZgEyO/efhXAp</span><br><span class="line">liCrAjPHnPTpdeANMgjMV3zmTKrzChwZ0S3awY12zfJrlRPJpKjL973QQsVNUNv9</span><br><span class="line">r+Clu9/xYqVUSu6F2PBrcqcJI1rnIE/6xeKLAQIBAwKCAQBtCPTuhmmbWJr4pTHb</span><br><span class="line">hMGcMK8MApBGYnxrXZJEeFw7wtKGe5UscqgGRBabgQ3uFByMCJmpYv4TSAxl9DqM</span><br><span class="line">AymsrILrs5SOZiVWryKVHMB3amcOl9ma/9rVe0NhwgyrTPgJUhnCTyWKEJcoAEZO</span><br><span class="line">UGtxIvfMn1RbhuIuZPCJ+rNgJ3YuIZwI8awhOIW7USx6he5ac/fPWidYiCiU+hL3</span><br><span class="line">9tbkHJCFeq1NFycGkelx+pnTNiDNjNk2puVDYWvy9nNUb3eMCcPtH5qYHhohvTDP</span><br><span class="line">HajeZMl7oau8QIKJrlpw+dAbfqfzt8zZx4mG4F/L84Wt2KaNEVkQnEo/MD/ULmiA</span><br><span class="line">C2PjAoGBAPjEBb3JynbnLGJ3uHYM/PmHOtqCvFi/oT7BV9wWD8unaXezC+n3dEs+</span><br><span class="line">OmXyBqJb6N75E8JSUdUoEpPW9Xo2JJErTC/DeE9bwTREa8lkRajhN7LqV+qFYh2k</span><br><span class="line">lzhFr3pPzMsop8/lz/KBdirSmj9vnTYKDv81Wglgj7FrZuwFgYVTAoGBAKhPB1ic</span><br><span class="line">glcsrxriQIzNKJUk/wJymPJLJITaYthXKckekLiKYQkBGU+fsThJAqkjPY9YCSFx</span><br><span class="line">1H31DxHZjcJBFEi0rfawVS8MvJeTFIc2O7SSvpWqJ/91UOgxjAwIl53JuoY6oHqD</span><br><span class="line">ZWTwGTbeHcxWjFX7R2akYpBkAtX2o3IAT+/bAoGBAKXYA9Pb3E9EyEGlJaQIqKZa</span><br><span class="line">JzxXKDsqa38rj+gOtTJvm6UiB/FPotzUJu6hWcGSmz9Qt9bhi+NwDGKPTlF5bbYc</span><br><span class="line">3XUs+t+SgM2C8oZC2RtAz8ycOpxY7BPDD3rZH6bf3dzFxTVD3/cA+XHhvCpKaM6x</span><br><span class="line">X1TOPAZAX8uc70gDq643AoGAcDSvkGhW5Mh0vJbVszNwY23/VvcQoYdtrebskDob</span><br><span class="line">22m10FxAsKtmNRUg0DCscMIpCjqwwPaNqU4KC+ZegYC4MHhz+crjdLMoZQy4WiQn</span><br><span class="line">zbcpuRwaqk418CEICAW6aTEnBCcVpwJDmKAQzz6+iDmy4/zaRG2XCu1XOU8XoVWK</span><br><span class="line">n+cCgYB9QybS2ieVVtxm8h3GDDd1q5ijBuOtSzx9wuqy4y7tMSW5bHQbWjYVTN3T</span><br><span class="line">w3ZTcNpuLL2CTuySN9O+ocUVy3Zb+FgONLVbZGWo3jtOUzCnVIGKa4tZMDi8HOMD</span><br><span class="line">pgDW+ICo4HAEXB23bsaISyEyCykdhrkVeQA6uJzKkQGAaaI9Vw==</span><br><span class="line">-----END RSA PRIVATE KEY-----</span><br></pre></td></tr></table></figure><p>PUBLIC KEY</p><figure class="highlight plain"><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">-----BEGIN PUBLIC KEY-----</span><br><span class="line">MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAo41vZcmeaQTodPfKyUci</span><br><span class="line">akkGkgPYaZO6oQxbZrSKWaQ7yblfwqv8CWYh6UGU5R4q0gzmfhR9HOwSmO5X0gS+</span><br><span class="line">gwLEYY1e1Zk4Agaz36sgsx+alePGaH/IQDjlEqMTAPN0Dfsmo3a4TxjivABpdXih</span><br><span class="line">KbRzsu7+iUpTRZdozvgNEDzSWD+Ac7dQRbBF8vLFke50M+fQrFyGD5IAezHQ4SvX</span><br><span class="line">HCUJBaT27DBVZ8Z8ZgEyO/efhXApliCrAjPHnPTpdeANMgjMV3zmTKrzChwZ0S3a</span><br><span class="line">wY12zfJrlRPJpKjL973QQsVNUNv9r+Clu9/xYqVUSu6F2PBrcqcJI1rnIE/6xeKL</span><br><span class="line">AQIBAw==</span><br><span class="line">-----END PUBLIC KEY-----</span><br></pre></td></tr></table></figure><p>之后在在线RSA加密网站 <a href="https://oktools.net/rsa" target="_blank" rel="noopener">https://oktools.net/rsa</a> 对 <code>authorize</code> 文件中的 <code>Data.Key</code> 字段进行解密</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604722959824-abf8ec90-93b6-4067-9f73-2f89957aef00.png#align=left&display=inline&height=927&margin=%5Bobject%20Object%5D&name=image.png&originHeight=927&originWidth=1642&size=108455&status=done&style=none&width=1642" alt="image.png"><br>解密得到密钥 <code>7ovX0c6ajJaXHSJy</code> </p><p>得到的密钥一数,正好 16 个字母,化成二进制正好是 128 位,对应 AES 的 128 位密钥。而选择 ECB 模式是因为 AES 常见的工作模式就五种:CBC、CFB、OFB、ECB、CTR,前三种都需要一个初始向量 IV,最后一种需要初始计数器,而本题中两个东西都找不到,因此 ECB 就是唯一的选择了。</p><p>然后使用 <code>OpenSSL</code> ,对加密的 pdf 进行解密。<br>(为了方便起见,依次对 <code>authorize</code> 文件中的 pdf 命名为 1-10.pdf</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 1.pdf -out d1.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 2.pdf -out d2.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 3.pdf -out d3.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 4.pdf -out d4.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 5.pdf -out d5.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 6.pdf -out d6.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 7.pdf -out d7.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 8.pdf -out d8.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 9.pdf -out d9.pdf</span><br><span class="line">openssl enc -d -aes-128-ecb -K 376f7658306336616a4a615848534a79 -in 10.pdf -out d10.pdf</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604725101342-f6809f83-3a4c-4773-92b9-0be8ffe71b08.png#align=left&display=inline&height=892&margin=%5Bobject%20Object%5D&name=image.png&originHeight=892&originWidth=1190&size=42687&status=done&style=none&width=1190" alt="image.png"></p><p>然后打开任意一个解密后的 pdf 文件,发现上边有一串神必字符。将所有的神必字符按 pdf 文件次序依次链接在一起,就可以得到一个神必字符串</p><blockquote><p>bDNoY3Rme3VzMW5nLW0wZGVybi1jcnlwNG8tMXMtZWE1eX0K</p></blockquote><p>然后base64解码,得到flag</p><blockquote><p>l3hctf{us1ng-m0dern-cryp4o-1s-ea5y}</p></blockquote><h2 id="ProjectSekai"><a href="#ProjectSekai" class="headerlink" title="ProjectSekai"></a>ProjectSekai</h2><p>首先,题目下载下来是一个 png 图片,这图片貌似有点大,直接盲改 <code>.zip</code> 后缀,打开,发现里边有 OWN.zip,SEKAI.zip,Untitled.wav 三个文件。两个 zip 文件都是有加密的,因此先看 Untitled.wav 文件</p><p>本以为是 wav 文件隐写,但是使用 Slienteye 打开后提示文件错误。于是使用 Audacity 查看文件,发现只有 1Hz 的采样率。然后提高到 20000 左右,可以正常听出机器读出 SEKAI.zip 压缩包的密码: <code>S_3!K+A=I#*%</code> </p><p>打开压缩包,发现有一个 jpg 文件和一个 docx 文件。使用 exiftool 查看 jpg 文件,发现 exif 信息中有第一段 flag。</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604728939509-a8736561-4892-482b-b044-dee356c1db70.png#align=left&display=inline&height=563&margin=%5Bobject%20Object%5D&name=image.png&originHeight=563&originWidth=757&size=133769&status=done&style=none&width=757" alt="image.png"></p><p>之后打开 lyric.docx,发现每段歌词后都有奇怪的空格,于是乎直接将 .docx 重命名为 .zip 打开,路径 <code>\word\document.xml</code> 存储着文档内容。</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604729349586-2bcb38a4-df7d-449c-b471-8293f99324b6.png#align=left&display=inline&height=965&margin=%5Bobject%20Object%5D&name=image.png&originHeight=965&originWidth=1915&size=165811&status=done&style=none&width=1915" alt="image.png"></p><p>将这些指定的颜⾊值提取出来,并去除重复的部分,合成一个字符串:</p><blockquote><p>4F574E2E706173733D41356168316E615F4D6166757975</p></blockquote><p>将上面这串字符串进行ASCII解码即可得到OWN.zip的密码</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604729753499-f118986e-3d53-451c-bd0e-221bb13cee5d.png#align=left&display=inline&height=151&margin=%5Bobject%20Object%5D&name=image.png&originHeight=151&originWidth=917&size=21007&status=done&style=none&width=917" alt="image.png"></p><blockquote><p>OWN.pass=A5ah1na_Mafuyu</p></blockquote><p>之后解压 <code>OWN.zip</code> ,发现里面有一段 .wav 文件。同样使用 Slient eye 先尝试解密,无果,于是乎直接使用 WinHex 来查看。在最末尾位置发现第二段 flag<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1604730022917-c8a6a694-b2d6-4238-8ff7-12d5ad413be5.png#align=left&display=inline&height=1409&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1409&originWidth=2560&size=361842&status=done&style=none&width=2560" alt="image.png"></p><p>于是,得到Flag</p><blockquote><p>l3hctf{Pr0ject_S3ka1_1014786832@qq}</p></blockquote>]]></content>
<summary type="html">
<p>MC系列之后再更,咕咕咕<br>下午去面试,希望人没事</p>
</summary>
<category term="CTF" scheme="http://blog.zam-meow.cn/tags/CTF/"/>
<category term="WriteUp" scheme="http://blog.zam-meow.cn/tags/WriteUp/"/>
</entry>
<entry>
<title>WHUCTF-Misc-wp</title>
<link href="http://blog.zam-meow.cn/2020/11/07/WHUCTF-Misc-wp/"/>
<id>http://blog.zam-meow.cn/2020/11/07/WHUCTF-Misc-wp/</id>
<published>2020-11-07T02:40:14.000Z</published>
<updated>2020-11-07T02:41:23.708Z</updated>
<content type="html"><![CDATA[<p>咕咕咕,一咕咕半年</p><a id="more"></a><h2 id="check-in"><a href="#check-in" class="headerlink" title="check-in"></a>check-in</h2><p>签到题,下载附件得到一个 <code>.git</code> 的文件夹,正常情况下文件夹被隐藏,显示隐藏后可以看到,百度可知这个 .<code>git</code> 文件夹是创建 git 仓库时部署在本地的一个类似目录的东西</p><blockquote><p>在 git 克隆代码之后,还不能直接使用 git,而需要初始化 git,它会自动创建 git 仓库需要的目录。这些文件存在于项目下的.git 文件夹下。</p></blockquote><p>有两种做法,一种是可以直接在 <code>.git</code> 文件夹所在目录进行 <code>git pull</code> 操作,得到 git 仓库中的文件,得到的文件直接就是 <strong>flag.txt</strong></p><p>另一种方法,首先使用 <code>git log</code> 命令,查看这个文件夹的变动记录。可以看到有一条分支的 <code>add flag</code> 操作</p><p>使用 <code>git reset --hard xxxx</code> 回退到该状态</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590718504847-2c1a74f0-0f13-473b-a805-5ee9b019f085.png#align=left&display=inline&height=377&margin=%5Bobject%20Object%5D&name=Chenkin%20%E5%9B%9E%E9%80%80%E7%89%88%E6%9C%AC.png&originHeight=377&originWidth=595&size=79032&status=done&style=none&width=595" alt="Chenkin 回退版本.png"></p><p>然而文件夹中的 <code>flag.txt</code> 文件为一个假 flag……</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590718713379-37db0cae-dc65-415f-b602-e58357a8b74d.png#align=left&display=inline&height=153&margin=%5Bobject%20Object%5D&name=Chenkin_%E5%81%87flag.png&originHeight=153&originWidth=677&size=12886&status=done&style=none&width=677" alt="Chenkin_假flag.png"></p><p>于是打开 <code>.git</code> 文件夹查看里面的内容,可以在 <code>config</code> 文件中看到远端的 github 仓库地址</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[remote "origin"]</span><br><span class="line">url = git@github.com:xinyongpeng/whuctfflag.git</span><br><span class="line">fetch = +refs/heads/:refs/remotes/origin/</span><br></pre></td></tr></table></figure><p>于是乎,直接打开 <code>github.com/xinyongpeng/whuctfflag/</code> ,就可以看到 <code>flag.txt</code> 点击就送 flag</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590718739118-647af1c1-4bf3-45d3-b56b-19d14e6d771e.png#align=left&display=inline&height=896&margin=%5Bobject%20Object%5D&name=Chenkin_githubflag.png&originHeight=896&originWidth=1920&size=225799&status=done&style=none&width=1920" alt="Chenkin_githubflag.png"></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{bc6cb6f4-1924-4683-a42c-cdc97653d2f6}</span><br></pre></td></tr></table></figure><h2 id="过早了吗"><a href="#过早了吗" class="headerlink" title="过早了吗"></a>过早了吗</h2><p>下载附件,只有一个 <code>yummy.jpg</code> 文件,首先尝试使用 <code>stegdetect</code> 对其进行 <code>jpg</code> 隐写检测。</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590719310826-3cab107a-6e61-4d56-a118-43ae81efd5b5.png#align=left&display=inline&height=49&margin=%5Bobject%20Object%5D&name=image.png&originHeight=49&originWidth=767&size=7791&status=done&style=none&width=767" alt="image.png"></p><p>虽然 <code>stegdetect</code> 提示 jphide 加密,但是我们并没有密钥,爆破无果,最终放了这题。但是在赛后,发现使用 <code>steghide</code> 可以解出隐藏的内容</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">steghide extract -sf yummy.jpg</span><br></pre></td></tr></table></figure><p>提示输入密码,但实际上不输入密码直接回车就可以导出隐写的内容</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590719493244-57c2ec06-8ae8-4168-aa50-4897f4c3589e.png#align=left&display=inline&height=69&margin=%5Bobject%20Object%5D&name=image.png&originHeight=69&originWidth=745&size=9155&status=done&style=none&width=745" alt="image.png"></p><p>隐写的内容</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ABAAABAABBABAAABAABAABAAAABBAAABBBBABBBABAAABBAABBAAAAAABBABBAABBBAABBABBBAAABAAAAAAABAABBAAAABBAAABAABAAAAAAAABABAAABABAAAAABAABABAABB</span><br></pre></td></tr></table></figure><p>只有 <code>A</code> 和 <code>B</code> ,很明显的培根密码,在线解密一下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ITISIMPORTANTTOEATBREAKFAST</span><br></pre></td></tr></table></figure><p>再包上 <code>whuctf{}</code> 即是 flag</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{ITISIMPORTANTTOEATBREAKFAST}</span><br></pre></td></tr></table></figure><h2 id="颜文字"><a href="#颜文字" class="headerlink" title="颜文字"></a>颜文字</h2><p>下载附件得到一个 zip 包,里面一个有一个加密的 zip 和一个 txt,txt 中是一大串的颜文字,很明显的 AAencode,在线网站解一下得到</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">我猜扫码得不到flag,但,也许呢?</span><br></pre></td></tr></table></figure><p>再看加密的 zip 包,尝试解一下伪加密,修改两个加密标记位为 <code>00 00</code> ,就可以解除加密,得到里面的 png</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590720971688-2abdbfe6-e93a-4196-b509-297a0de3d0a9.png#align=left&display=inline&height=302&margin=%5Bobject%20Object%5D&name=%E9%A2%9C%E6%96%87%E5%AD%97_%E6%9B%B4%E6%94%B9%E5%8A%A0%E5%AF%86%E6%A0%87%E8%AE%B0.png&originHeight=302&originWidth=659&size=79865&status=done&style=none&width=659" alt="颜文字_更改加密标记.png"></p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590720978216-dec35112-c2fa-43b1-9058-969b3c84feb7.png#align=left&display=inline&height=146&margin=%5Bobject%20Object%5D&name=%E9%A2%9C%E6%96%87%E5%AD%97_%E6%9B%B4%E6%94%B9%E5%8A%A0%E5%AF%86%E6%A0%87%E8%AE%B02.png&originHeight=146&originWidth=650&size=35326&status=done&style=none&width=650" alt="颜文字_更改加密标记2.png"></p><p>PNG 是一个反色而且缺了三个定位码的二维码,反色后补全</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590720993972-bc76b670-8e50-4e4e-9600-0f152161a7f8.png#align=left&display=inline&height=1058&margin=%5Bobject%20Object%5D&name=%E9%A2%9C%E6%96%87%E5%AD%97_%E4%BF%AE%E5%A4%8D%E4%BA%8C%E7%BB%B4%E7%A0%81.png&originHeight=1058&originWidth=1920&size=195920&status=done&style=none&width=1920" alt="颜文字_修复二维码.png"></p><p>扫码后可以的到一个 B 站的个人空间链接:<a href="https://space.bilibili.com/309312103" target="_blank" rel="noopener">https://space.bilibili.com/309312103</a></p><p>访问这个链接,发现这个是出题小姐姐,而且 <strong>唱歌很好听</strong>,在公告下方的个人信息处有个标签: <code>1234</code> </p><p>再结合颜文字解密中 <strong>来找 key</strong> 的提示,推测这个 <code>1234</code> 为某种加密的密钥。</p><p>由于附件中只给了一个 png 可以用来加密,再结合密钥,推测是那种需要密钥的 LSB 隐写。使用 <code>cloacked-pixel</code> 脚本解一下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">py -2 lsb.py extract C:\Users\Zam-0\Downloads\颜文字\2333.png flag.txt 1234</span><br></pre></td></tr></table></figure><p>可以得到一大长串的十六进制字符串</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">280624EA44E4549400000000897970904967C98EF35F41000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D004584100000010E3AB410500000040AFEEA1043073CB59A255402E17B39FFA8D70D12DC49BCDEED74A993F3F3FC4EBF5E23400905FCD820A000000600A24A800000000A64002F870F9FE222626067C6665DAA5EFB66FD0BCFC7F696B6B1B4E8C8989A9545C5C090C108A012A08000000727D072A08000000727D340C04FD8D1D11824FA78392000020B9ED469FE3F454A3D9E4D9D7D7B6361C8A11FD8319BEC23B8472535B5B090461B87F818D0FA640D7EFFF47B9DC6E9EF83E8FAF57AFDF072F0030B79CF95595E023989235295AA4E10482C3C79CC1E87486258DE60370F5E14C861D8A6295DCD6D696361C8A01444AED2F77B3D92D966AB47BE64AF5B81FD710386023C3A11450279BD5A02FCBFA64A9B67C3B34792FEFF52A5DBA458C10DCFEF3F7F7967DD5028383A3178FCF471B0E4DA583AC7FE1546277E5DBE574388432F6F6D6E2DDAB895AEAFEDFFFFB0A051A950550AE6294A403B087D4ACC6E4F697BEBEBE546995E2DDABA8A6A6D6321C353FE5C3EE77B39CDA370C8FE3F02AA9A9A9577D1539A91E6891E1DBA7AA08631C445DBA558AF1B1843005E3158BBC5F53DC430DE185EF34FF66058D8DADD1A367FCFD5058587AF9FC7E041A243AE172360DD83E87EDC867B3D1120F8FA7D17E97EDD9A75FB3AC295DBA86562B11471E1D314B5BDDA75FECA3A1BCC2D89AC973B1D8ADE868B6343F3F3F74FAE52AED7BDA8E5BA6ADDFBA937B17DFD68D2FFC39140043ECA75FFE57E6CD3A4FE7F80D82058A6F6F6FA8DFF5BA4FA75FDE3D1BB0B0B074737343361B8D5F84A9DEEBFDE6081C38D4216FF2E5DBDAEF54A2453ABF2DB963D6AF62275DBA74F1ECDD22459A356AE266DD367B6FEFFD84C7E3E1D867BBD51038608BF8A5861787CBD572BC784484C7E3E17876769677DD7FA7D3A77F297BA2279FC7AB2222D3DBDB3ADDEE7FB4EBFEBB6D49949595A5501E87E5F8F6F6766B84AD6FDBFAB4D3E9ECD9F7F57D2AADADED3BF6D37E3F9F0DC1C1D1508FFB718EC0816341B9D66B2CD6047E81CBEF35DC56B465DA67A245EADCA55BBDE6DDBBF4451FA58B9A25EA5ACA21A4B037F8E5577EE9BCCA6050C90AFB61598B38D160FC72D6BD6B03BB8098D0B7484191FD1B1776AC3D20A05FE75D299BEC425626D55058F3E722B9465EE19AA60F8042A9E4D45009C5C8851B84101FFF6664C73A4990FAE15B4176F4C8BD4AC914C7974F29B5101629D54A0C5B1672603E09F249284D85A90B066FD45E1B66B06107BADC049E84287EE4030A6D6AC285FA7CE7E6239D4BAE337A946AA9BDBC5FF9E52312FB526D1B8B316BDEAD98AA37AC782BABB6E5D5BF60F9B08486C256850714E7E8170A99BC7E06C213531B057116FAF99CB3DA97C0D2E7FACD3B0C926C657C611771460DD59721DAE7B4CFAEC3D4A4F354ACB2BBBB0F0A6375992B10B21EFEC8155B204B399F1781F723569E931966BE02712533D4434E6F440ED833DAC6887726C040BC30137120DB44A2E1BDC64DD04983CB9A17658D7311EF16E60BDE23C83A3D2348608C876EA5230A8DD6E700AC071D3A6795566BA245EA3D66DCB2323F49647EE7F1478DC0A05FF46D77D557CDF0782ED78FD4A53CFD61C5FABDB42F0FC212FB233ED9184A0C358A9B6E62C39FF5DB561EF9167DA6CBBF5F9D5361F873F43860C9C4204D755818BEEA3A58981EF916FA14FA71D1AF67CFA6603EE74D15D91397718BD8A437F4A7D64F851B67955659330E15F763BCB7128E3339FAA0F2EBCB029ECD7750167FDEFD5B3F26F9DDE5A660AC53E52BC955BECDF5C2F6BDB412BEFAD6238CEFCE5BE6AB61A986F9172278CD056A852D65341882D8EC6C0945EBDFAFFDFB51F81EBF6E7B8CBCEAA7DEBD3B6583EB3F518382922BC5745AC68AEF6B8071E1B47EB3E6DD8A521B75CE1529202CFD8DF2594E05F5493F31969D242DF3E7DFDECF34FB2B72B2D767DE8D3E4C37B5156641CE9B2779B011F9C89C8AB1B895A6609576AD9CA2BFE9BF7FAEB0852850A8879B29BF95FEFBE7DE65DA3522CEB6509746385AFA04E18C46816AF046F2B571A46284568A010A56C934269BBE7BE6EF5FCEBBEAD26A9550D816EAD5E9B963B6137D2AC08BF818E9A9966BBC79C20D6D6C0E85FDD509E89544C33A9317D0FC31C76E15221E8FFCC3D0BD7BC471AD2FC81B133811AFED578E5E4DB865DAB70756CC851B67BF7F8EF69D76CFFAAD3ACD514047BA1018E56F20CC2AE36B7EE2C51AFB1F7A3F8AAA71E7E8A3D0767271A2CE803CD048B26158A87FFE8D0A8F4DD05CB1779573CDD58223C4DE4C1471D02D3366DCD12D0D672998E469CB1099CE9B276BF4DB6BDC04AA5300C6EA90336201776B9D8A91825DA7AC1730A0AE139FE5DC1B0B4F19B76BBD966905BE0ADE24975169A540371FF357413CD2382EAC442E7CD2E5F5A73F58D8683F392354E9984DC495DAA8856B2A8228545615FE9FF55B647FC3E0DDBB5719734E97FDFBF81E1859BBBCF4F89C3699B2ECCBE741D2E7F3A653061F8D26B57134757EF33E15D645C43E8846DB529D752E77C10FEC4262CA62D226E0A628D336A8DABAF016E6CB16D38F6B1982EDFF73A10BED1A8682DA26E15CFDC1548E4A772952F8E27C65DA65EFABFA75BA7AE5DB35F047C83FC3FCA4DBE5EA88984B2D53ABDDE66612F668F7A4EAEADA201E07ABF25FEFFBCCD33FFA22653C2A4369FCD3A75F436C872F737777867DB65B9F7E118C4EB79FF61ACFF3781E6B4AF07038CC39D984CCCCFCFBEAB42D4FDB586CCCF0C1F5EB60F47239BC614C2B40D82DBA7AACDFAAD35A267FB7DBFCA7A1A5A5D515C604198B451C3ECD88C2B5347DD5B6ECB7FED7FE7AE5DBB42C5C1BB67DD443BDA45CFB7B6FEF4659F8DCA75EBA655B09BDA2E57FDBFE41670E1781D5B6C71931FC3FCFCF630B8EB99469DB179999E9A9D09F837C11BF9960ABF673B95B65EAB259ACEFF2CA5246A97B275204D0179D9DBDF783F2B02CEF4895E730D57B7B6B50B94C4DA55E21FA95E6C2972B1BDD7D59B1BDDAB9C43BFFA21F207A31F985DEBD6BF07361297D8E51FC9785D206CDBE4CE4A90FD69DB66549969663226D8F41B93BC26BC2CF0B6B6D8F8228645E75FE2FFB27F89ADA5C637D297E882173A0E83F4E129639EB2E95F036BEA5945DA64FDE8F2B6D2ABF7F81231D7ABD3342EECF3536DB6CFC3B9E8238615CC35399C3D4ABB83E6571F32E8C772E6C180B0E0A2E3FC22E8E54498B533DD4130EFE21824A77291BDED047D9A7CE9B272A22D6D215E3756AB8051C3870ACAB846DBE57AB433D05508A17171786C081632BBF8E646C6C0D81036665DC07B265DA7EBDA0EFC787403CE9B3B3B31468B17FFD542E1F4781167AE58B587E16DB7E956FD7E9F9F7F726BFA3DAA59A63C64DBB67AED7DFDF2AA5A5A53EFEBBC23FECBD6FDFEBFBA7E8961A37CD7FC34B6B62D70BE833F4853B78F388BA6A84BB7A555D5DFA69FD575F5F0AC3E1F0143A641A255A155669F68E261783D95FB62357D21FE877BEA6FD6025B5F5C93F49AE57B395D7A12E26DA42F9C8AF64ECECDCA35FAE2B5AAF9D585C9D23D7E7E49F6454CAA302E69E9C9696E78E0A0CD8E71261CD347D0664812668BAE8D9A3A45DF8D89514531B8BB6C5479E4DBBBBBB7EC975AC9FF57433BE1A1773B8966966FF93765957159FFC6EA97211A7606C55568D7D9B6F56F0FBB12F3CFBFBA271F5CBCF243F99F4ACBBEAABC24F0DE520641F55E75FA134C8ACB7774CE59FE540D8AC2A5EFD1175C4D82FCCD70CCA2BBD433D34CF45C57D385CA3B8C1BE4C6A839BB1A79828FE786BD35696B87D433966BB61A9D67C33432EB7CCAA7F87B973A64262C954BE8A33BE7B4152ED8C3A7C7170FEA96F9529B62CBACAB9D41F312DE62F61D3EC57DE6AA2D3AECC38C61959AC5E4781166AE34B831B260A03063DFCD60D9E4FAF97FEDAD0A65D1A7B307DAFFFF5D4FB628901E7F01C9FFEB3F2F2D2A1ADAD4D475BDCD6D6967B7B6B88880B65F2BD23E2A2FE2B2F2F3DEAABE4AB8E6BBA784494EBF6B92DE4E4C44BF99E36C409E375FE9597205B9A577DC53FD9BFFB777EDBD0FA5B6FEB24514CFE5798D4434D09C281A44D181C1C1DF6DA5FEB081C387850E0FE77BB1A255A65599D9797967C3E1F2DEAEADAE1EDED6C050E8FFE5BEAB5B5B5D38E37E7F6DC0F0B75D3A74F8B7ED101E4A041C2A07D82CCC19B4A459740797EC33EC3FC33EC256B80ECECECDEB88FE456EA3F3F3F022D7F2222F7F49ECFE7BCD57272624DA04964D34A178FCFE7A5C41959A275FB1A9B9B9B3A7DFA34E4657DCABE7D37EFF7FCCCB29FB8373118A996F8C7EEDBA7CBBD28CF13E0F7E259C8EDF67354535343BE7DAAD3A75FE1DBA749EFF93D5F7CC20E0B0B9BCAC4ACBEEACBBA6375CE972F248D8691537CE4D9494C0D2FCFFBF2F32F7DD3A75FCAEFAF01EB712CFFC38EFFF13E8FF68E67BFD7AB522D9E47A347BEF9D57DBE5F3DE6DD8A033C9426B6CBFFF93F9E7DFAF5FD0E567541DA623C99948F9055FC54B87117330F151C46CF7ED977EEDFFDD56F56EC7E372A2204DAE730C867BD89E7F1784F3AF3415363CA9EAFE7F97E7D712FEFF5DE16F3F097BCC22B949C9C1D27C6D8461E1D31FEBFEFDF6CF2D021ED3123B82C3F7BE117CE78A49F4BBC9333CB3BD6AB82FF75F52BB07981DE5CF4B34D566F28B7EFA28BAEA397239650874F510EDF9FE0BB59D57C2FFB369C401B7C11A3BE1F51F6D85A35778BECFF4852AA3464D831408C3135560F1350304182C272FB1EEE9E093EA0BB1EDE3B0522F8776C690A35C82837B815C82E41A5159370FC900D45B226CB71BC1E75C0037D201B8FFB4991988A5182173759AAA6DA2B0F7067BDD6B29C89F6941BD26D75CDB69C4602D14A672EF959586F852A561437F9AC55AD9858286CCB069AAE8042E655D2E375CE976FE22CB7CFCFE2CBAAC0C4205935068F2B2340AC84AB6F3A552E90FEA8D3AA0F0A1371997BC69A7F0E39F5BB821C64690A7609FD0BAC53884131B80563E4B1FA3CFEA859B27BD5474F9F0347B116C785A32FA6AE7F6A264D716C0A451A6CF3D235FC582DE82D5D65619D61035EF989A12F54D88B7CF63C243D22BD0FE66BAC8931A2C38F67D009EF26D4D8CC29C864785A63C6DDEBDFBFF55B64CCC5BC897B0CDF4C6C558743FBE196AAA65C09E4B220EAA65D9A5FD6290DCA22084CF05BFE2E1FE653424928BBFEFD4AC145B1531C3660BEE061D0D804BAD274B58FC871C4A918ED557D85B472E70BCC94DA75397575FA0286D2049A443EFC59AF80218892E0D25AA2A9AFACE2B9AADC1FA62FAAD0EA3800D07913856856168EE2DC20CE82235C8EA517D4AFD0C5EB62993091528A71AEDFA833808913E3FFDC65FA2C0859D404FBE2B04671893AD670A1B888E067610AC70CB66116572ED21D4AEFF7305814E194A5655906C7A633AA8DA918ED745CE5A928A0903CC0330562B599988FAB6F44BE4D395C2F7D2C254D803C5968A71AB6C19B27ED7D93566968C3B5C42B8A907CE6573C64D8BCABEC966A3ABEC7B1FA17DDB221BC5E47AA7DD9E0DEBDFA613F3EC71BD47182CA9EC6B35C2E81A256686592D7DA40E4F530B4C33EB45FB65FE75F392EBD622CEBA5DCC6EBB33F2A9E55856D8F861D3A7219EA529F0D25B84CC637722AEFF6C11E689167FDE955538BF9FC3E12B6E060C4789F46C20491A1E6580615D77488B93105B3CCD6EAA245EA3C64D82050B989D57903FA9DDC26C123CEAB335144EF53205A7159A33FA4C97A11FEEC9FB896695510831A5EAF886B22343DB8D4E6E1BD015AD66CA1B03F95B630BE9FCDF0B9536B84A0B580FE0BC5815B2F56BFE7B271160070FC601A75DE1ADEBD6BFBA6D8403198D9B680D79655886D32FD14F1D9680BBE14D52B7D09142B6671FCA7D2684DA1C481E3A29876F1C5DC09B2D53905338415D76480B51187F2DC04AA936CEBE2E415A6408CCB81D858E7F5A72E0151978A9192C5465FF5590ACD16468B913B66DE23AC894259D7FB915CC69E44EE7381C904AA46BE8A4B28787DD50713073264D8E7936467F59110C55DBA6503F58A1BB3E261F44CBD14B9F99C2BC4AAB3272EFAAD3A2CF2150D247EA859B23B65DA661CD3BDD6149858C517C9A4A8C579514A62CF855A5CE9D054D6BCE5CECC9D4FF52CA0A9694632518F4DB017BD714D2737C575FBE7A66029B1E75330C3D96235B3F54950FD98187A6F9D8A319215FD40ACAAD78F3AD24CB792F0C3D36BAD5699939EF3BFE54DA79EDF6B4DA5EC33B0C975F7C517E8CDB4943C64D88D489DE75F1B2EBC1697663716F141866D2C23E5232E55C90D75F71EE83AE93FCD999FF51B74955F2A265CB84B851B67BA65EAFDE7DFF7BD71F72E960D229C23CCF9A8B2D96CEFF7D1FD53DCA55B9D6AA9D0A22EAAA79C930CB9B37F0EC3EF229746A96AF44C2BFBFB8178136A73CEEEC27EB9FD0A9F6F61EF36A4877E46F797CDE4F8134255C98D95F76342EE666401E7B79C17DAC08DAC2FCBFAF9F93CA1E164F6B96EEFEF6879B45A3DFAE5BBE42F2C3E1A966A6A7B47138BCDE6B1AFA4DF57BC65DA71A13BF8EC251B71DE99353535B4F376CEAF959D9D0E2877D547A8A6A6866D6D6DA857A3C9E0F5408956949D9DAD577DE4BCAB0E061C89874D0A061C7DDE15EA42BF8A3214FBE835AB45BB8F19EB736CAFD4DCDC6E650C9B9B9B7AE5DB56C726BC1C67036929977EB13FE7F6ADCC64FEA8A8DE0A58534A36C073C33ECBFEB3FDDF6FF3F83E8E78D7227ECDBFF9E1703C16C6C872044C07A52C51CD8C5D6768ECECDC606C6C9EB0FB07323E0562158F32785A433D35E44CFED93FEBD8433DF7D575BA5D1DD6D6F69669F6B8A4996AA9E95BC0E673B1290B6CE9971CCFF3782F17EA68EF92832E7AED2BC35EBA653A7DFA06B09F36F279E474F25ECFEB6866668659A245BBDDEE9E65DA45966AAAB8C971B0582D71161DB27FC197D9A62B4AF50C179AEE4A255AA4282626867633992DECECDCC2CECE0DEC76330A8989A9545C5C1A3D7AC7B0B8E3BF1F9418D23696D8AFA015EDBF25616171FAC5F0380EAF92D4FA1C29B2B5423A35DD3FE977FDFDCD2B9E4969AEA057F639BE6A3505E26CF4394EAF303838D872979F9FAAE14D141798C6BC7844086058A245BBC42B760E469FE3F4E9A3B1D5EA7D4444A3408C738E725D1B93CFE8BDC6A9A7A756BEF5A13ADC59C0810326B4CA3B94407F8F1936B3FDBFFFE72997C3D9214FDBF2D3DA7844D3D293E279B4F99E078E59BFE4C3EC4B15FA5E383200D6C8E874326DEAC9479E3D2FDD66247C6E69C617DAD2F4FDFE7F03EDBF35795E9FB7E77DD3A76F9D5350175DDCE50BDC2356A46C536DB7D3D98A3192823C2F2B6AEEEECE976867EDEDADD4438C4182358A6DA832D7D78CB9C4B0E57D5B8E7FB7DBED69D652FCF9F9F9E3051C38604AD0A0E97A9A8E3DD472F5F595AFA06B629B47DADFFB7B9E433DC05906A2311E1B94E1162DD5561F9E1A70381C9E7FEFFFFD949B9B3BFDD7737363D867BBD5103860E514B286252D2DFD21B5E8474F4F7D3F1E5FB6597A7A6A169FE6173F66567CA17DEC44CC9E4434EFF7FC9FF8FBC45959597AD557FCB3F5BEDFF74CF595E1706C10C43801C4CA225AB35DA2ABAB5BB9FDE0AC29F46C5CE495565B7E555FD7E5182458ADB759DBF6B58CF359CA7DC9792DAFF5449A25F9A9144C3A8EAFF34FC02FF1B711A46E5D69A9B66E5B4821A44D131B1B4BA459825F1D17178659A245A55BE6C5AF453DC43864D8449A25E9427BC1198C4EE797FCB7ED7FCC659A15265C343B3B6BAA8D47342F6D3E7ED9F93FEFF7A0737A2172982EB730F560C9843CD072942D23E7F49C8FBC55DDA75F5F7495C8FCB8DC5779FD4A37D433056205A2A16533EFF7FCEF97ACA7E97AAA125B8D7E0E252D2D1D5669F29D6C3793D9414D4D1DBE7DBBC42B49529D53EF07D3D7D78688F86095ACA6A6967DFA65EF68F4394EBF407E19B3D9ECDE023C2A028383A3939B9B0A0306EF18EB15E3140F8ABEAC0BD8A1D27ED3A75FFCE9D111A3AFAF0D82050A3EA5F23EFFB3F7FEDFEF8045EEDBA70A051A74D69B76CEDFF90A8383A3433D847BB5DABD41B9E547DFF3F4E2E34E44A459A257BE5DB937AD1921EEEEEE82D57ADE3B42D6437DC3CEEBF5E2069B99D3D2F4FD7FDF8E666666A6D38A119259E5DFB2D3E9EC1198749E0F073518BEAFEFFBE557D5D5A5D1A3779669C72F2C16F46ABCFDD7F81FEFF364A99642B4B3FAE53B97187070707ED97A3D9E6F1CA31168B1E577F97E9F8E479EDDEBD2AA34A9ED37F03C0F8FCEEB9239B9ED262E2E2DEEEEDEA1964AB0B0B04755E6933A1D8EDFCFAEFFF5BEAF50A05162DCA919265CD3AB5EBE7D8A6E6E6E080E0E8E033C62E9E9B92ABA17BCD1E87CE2A34E9A253A65DA051668C1F5C7199FCFEF474444BBDDEE4ED1A7ED9AB2B2B2242B2BEBA0EDAFE4E4D4FC0D5FAFDFF76BECD131437B93A65D7AEB489E3DFAEB983BCEBFF3E577D547E877B3F9FF0C3C1E0FAF6CC2957EC96F6F6F42BAE7C68CDACFA6C34C6A0AA4F8804C2ABDF52CCF0C5F7DE50E9F4BA8830BC6D91882E87D2300128FEB9069C7EF52090CE4D25E6EF570560FC97665B3D8EBBEA118EA937DB1F33F3E30C8A29950D8047DB1BA65C0A0AC0691422A7B954BBC80136B4B4A207C02052633467B3E980FA5B861DE5C6A6B829CFDA8105618B7984035E39069D89370DD2BEAF7693F193EA5D6568238608FF6796EA54DD36BBD9669653F466875DC5ABFEE852BB8C41ABE8F7ED540076F022B42A71795796C2FADA0CE9B273BD1D85A647C52D6BD5BCBF9EEC9F4E88B1668EAE20C3BF2A36CDEBDBA02289E3595F6B8379F97A99805EBC0780BCC287C5A27BCC29AF912A999F0263D65F036F736BFF54275E4B861D2608503A239D4535BA55BD67105A514E5D7ABF8F59E6C0841B37BE65B202C85D24C275B7801BB5335A769DAA5AA7E9B50447F3A84DB9E7496D57FFECD9905AFBE5AA71FD623CCA4DE79FEEAA143C6C18A37D453D287DBB4C6B9B826134A41610AA66A2F37F269970F5B3012578E36EABB67AEF94097EC72B4D17F5E6E96335A4BD2430D2F943FF541104136B3CA92A628ABB0217BE4BA65EA1D265C62A6DF13937C5653268848318F4DA0F03D1BBEEA6BA996F9181683B3626743CFD19775538CAA5F6929E532EA46B1A1071D3A67A996B86FDB4BBC5257AEB3EBABC69DBBD4B97356699584513E0DA45223EAB861E23D66DC97FA1E81B23CE8F697A996C9AC8F4D80458565524DA1D42675BCB1089E8C4B3D756BDB974994255A35E72CD3A79E8DDEBD4BCFBB8E33D4F0D5572BBCD20B1D3AE7722345ACB01C57FDE7DF379FC19B27CE884839CAA5969AC08F19A9A15BA6AAB2DA85066731381B35D2F4D5571B255A67BF7F8E96A64053A64DE1B5D887C2B3C42B75D526033CF0E561C3F5E4449A253A9DFAB8E145F3FBE779C1ED2F2BBCB7711FCB585E67DDBB046C6D80966AD8F82EF990DF29B66F9BBA657B994C5EBC5977AED5573D08FAFCB4F0D7F0FEFD7ADB57A55B67B96386FE0EA459A2E3AF966A9D56698D9A25BB51A275163A64DD8269DAA5DE433DA7E49B6AA99D5669ADC69DFB312AA5961A9C1D73E6DD83D9B4FEFD6B546D53A65DD74F13265CADDEBDDA049CE1EDD756A266DCA94F4EE6DDBB4B851B947FC32BBCD66BBDD6239CE31D93275E9D6AA9AFA7A95BA6CE3DD4F09860356881D6EBDFAF3493DB7EFEFDBB54B871966AB9D6BB67CE35B78C3CD07BD5576BDDBBE5C8F2F81BA245EA3D66DC16083D1BA143E65CA459ADDEBDFBFB1BB963E68C982B22D6433D935FE8DDBB456C102B532045A7C9BF11C3F4311DEC7E372A2262EDEDADD1A377964137E442CCCCDC43AFD7A3158E4F47BFDFEF8E6B6B6B033CA2A45E27AB95CE6F67EFBBF6BD6F4FB1B1B193F151606DB970740821CD3772399C9F7EF01FEFF3ADC800607F39E3A224F697FB7FEFFFFB08003078638DAEAFEBFA4444B47BE66AFDF67BB3E1D004182531B78CC9E472D6020000B8978CED73D89E7DD5222252F5BA5FD710386067C3A1186094D27C82AFE3F83FECB85C2E4E320C00A11471B7DBDD225CA82D907CE4D9B4E1D0540A1F54CB6EE4E4D4E3DFAF88883997CBDDA2AEAE3C840000A2ED27418245B9B9B91468911111232F2F3DFAE51A4E00647FD96BBD167352653E89EBF2B8BF9F5DA65DD46B9DE3EFEF3DF4F4D411000050B9EDA4ACAC2DDAA58A3DD462EDEDADC0038C6B6B6B6DC3A118D057EB6C71B0582DDEBD8A84000042ED3F7BBDDE2DE6E6D6232F2F2DE2E2D2E1DBA76FC3A118C0A4DCFEB2B6B6866A6A6A69676796767666A8A6A686479E4D440000A0F69760C0A152A459A35FAEFF0F600A149A91DB5EA65D8A5BA61D190E0000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D0045841000000004D801504000000D004680F0000000000ED2FFF7E820000000031D66B8DC47E233FE97693999D9DEE2DBBB0307F6F3BD97B99BCECEC7662778EE589F06FCD5BAD9B4B1AE25DA0B41B67411B436BEE2802A65D959B12A8EADC5282C5A02F43225E5F959653E5DD47CD964A824697501CA8B659DDD2D08056154D9060840E4AE962148343A2B313C456A71BCEB6A7F3740F709F7DC1B6DDDDDEC9874514449495F10000DDEC71D5564737160796E63500562716774766F6354785544711000000B1E0B259104CE000004CE0000037958407900000002B5429BB000000608008000000A620000025448494D0000000A0A1A0D074E40598</span><br></pre></td></tr></table></figure><p>一开始看开头没有什么想法,翻到结尾发现是倒过来的 <code>89 50 4E 47</code> ,为 <code>PNG</code> 文件格式,所以写脚本翻转一下,写到 <code>PNG</code> 文件中</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># py2</span></span><br><span class="line">f = open(<span class="string">'flag.txt'</span>,<span class="string">'r'</span>).read()[::<span class="number">-1</span>]</span><br><span class="line">fi = open(<span class="string">'flag.png'</span>,<span class="string">'w'</span>)</span><br><span class="line">fi.write(f.decode(<span class="string">'hex'</span>))</span><br><span class="line">fi.close()</span><br></pre></td></tr></table></figure><p>得到一个图片,打开就是 <code>flag</code> </p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590735314217-d65aaa97-1712-426c-bf72-a63d020e13c8.png#align=left&display=inline&height=99&margin=%5Bobject%20Object%5D&originHeight=99&originWidth=583&size=0&status=done&style=none&width=583" alt=""></p><h2 id="版权保护"><a href="#版权保护" class="headerlink" title="版权保护"></a>版权保护</h2><p>下载附件得到两个 txt 文件,查看 <code>题目描述.txt</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">小p发现自己的文章被别人复制粘贴了,感到很气愤,于是他偷偷地将flag藏到了文章中,你能找到flag吗? 格式 whuctf{}</span><br></pre></td></tr></table></figure><p>再查看 <code>problem.txt</code> ,在 Windows 下能看到的里面都是重复的 <code>我很帅</code> 三个字,结合题名版权保护和题目描述,很容易想到出题人利用零宽度字符进行隐写(因为零宽度字符可以用来作为一种水印,可参考<a href="http://www.ga1axy.top/index.php/archives/20/" target="_blank" rel="noopener">这篇文章</a>),用 vim 查看</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590736038529-b7a3fb93-5f9e-4ec2-a77e-62370a6a6f3d.png#align=left&display=inline&height=631&margin=%5Bobject%20Object%5D&name=%E7%89%88%E6%9D%83%E4%BF%9D%E6%8A%A4vim%E6%9F%A5%E7%9C%8B.png&originHeight=631&originWidth=1129&size=69206&status=done&style=none&width=1129" alt="版权保护vim查看.png"></p><p>可以很清楚的看到每两个字中间的零宽度字符数量相等,且数量都为 8,再结合只有两种字符,想到将其转换为 <strong>01 序列</strong> ,再每八个一组转换成对应的 ASCII 字符。<br>编写脚本如下:</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">'problem.txt'</span>,<span class="string">'r'</span>,encoding=<span class="string">'utf-8'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> str = f.read()</span><br><span class="line"> i = <span class="number">0</span></span><br><span class="line"> j = <span class="number">0</span></span><br><span class="line"> <span class="keyword">with</span> open (<span class="string">'output.txt'</span>,<span class="string">'w'</span>,encoding=<span class="string">'utf-8'</span>) <span class="keyword">as</span> o:</span><br><span class="line"> <span class="keyword">while</span> i < len(str):</span><br><span class="line"> <span class="keyword">if</span> str[i] == <span class="string">'\u200d'</span>:</span><br><span class="line"> o.write(<span class="string">'0'</span>)</span><br><span class="line"> j += <span class="number">1</span></span><br><span class="line"> <span class="keyword">elif</span> str[i] == <span class="string">'\u200c'</span>:</span><br><span class="line"> o.write(<span class="string">'1'</span>)</span><br><span class="line"> j += <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> j == <span class="number">8</span>:</span><br><span class="line"> j = <span class="number">0</span></span><br><span class="line"> o.write(<span class="string">'\n'</span>)</span><br><span class="line"> i += <span class="number">1</span></span><br><span class="line"> o.close()</span><br><span class="line"> f.close()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">'output.txt'</span>,<span class="string">'r'</span>,encoding=<span class="string">'utf-8'</span>) <span class="keyword">as</span> r:</span><br><span class="line"> strs = r.readlines()</span><br><span class="line"> <span class="keyword">for</span> line <span class="keyword">in</span> strs:</span><br><span class="line"> character = chr(int(line[<span class="number">0</span>])*<span class="number">2</span>**<span class="number">7</span>+int(line[<span class="number">1</span>])*<span class="number">2</span>**<span class="number">6</span>+int(line[<span class="number">2</span>])*<span class="number">2</span>**<span class="number">5</span>+int(line[<span class="number">3</span>])*<span class="number">2</span>**<span class="number">4</span>+int(line[<span class="number">4</span>])*<span class="number">2</span>**<span class="number">3</span>+int(line[<span class="number">5</span>])*<span class="number">2</span>**<span class="number">2</span>+int(line[<span class="number">6</span>])*<span class="number">2</span>**<span class="number">1</span>+int(line[<span class="number">7</span>])*<span class="number">2</span>**<span class="number">0</span>)</span><br><span class="line"> print(character,end=<span class="string">''</span>)</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590736173258-08e7ac6b-f880-4d26-b243-3bfdd7886cf5.png#align=left&display=inline&height=631&margin=%5Bobject%20Object%5D&name=%E7%89%88%E6%9D%83%E4%BF%9D%E6%8A%A4.png&originHeight=631&originWidth=1129&size=46353&status=done&style=none&width=1129" alt="版权保护.png"><br>得到 flag:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{Y0u_kn0w_h0w_t0_pr0tect111}</span><br></pre></td></tr></table></figure><h2 id="wechat-game"><a href="#wechat-game" class="headerlink" title="wechat_game"></a>wechat_game</h2><h3 id="快速解"><a href="#快速解" class="headerlink" title="快速解"></a>快速解</h3><p>下载附件,可以看出来是一个游戏,如果是明文 flag 的话,那一定是可以直接搜出来的,那么直接看 <code>txt</code> 这个文件夹,打开观察里面的内容,发现都是倒序的,比如</p><figure class="highlight json"><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> <span class="string">"thgieHrenni"</span>,</span><br><span class="line"> <span class="string">")53.0 ,0 ,0 ,0(abgr"</span>,</span><br><span class="line"> <span class="string">")53.0 ,812 ,822 ,832(abgr"</span>,</span><br><span class="line"> <span class="string">"redner"</span>,</span><br><span class="line"> <span class="string">"etadpu"</span>,</span><br><span class="line"> <span class="string">"evas"</span>,</span><br><span class="line"> <span class="string">"egamIward"</span>,</span><br><span class="line"> <span class="string">"egamI$gb"</span>,</span><br><span class="line"> <span class="string">"tceRdnuoRllif"</span>,</span><br><span class="line"> <span class="string">"dirGward"</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>于是就推测 flag 的格式也是倒序的,所以直接在 txt 这个文件夹中 <code>strings</code> 命令搜索</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strings * | grep -i ftcuhw</span><br></pre></td></tr></table></figure><p>发现有搜到的内容,直接把输出的内容全部复制出来,再搜索即可得到 flag 的逆序</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">}1m4g_tahcew{ftcuhw</span><br></pre></td></tr></table></figure><p>于是,逆序即可得到 flag</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{wechat_g4m1}</span><br></pre></td></tr></table></figure><h3 id="常规解"><a href="#常规解" class="headerlink" title="常规解"></a>常规解</h3><p>首先,使用微信开发者工具打开该工程,模拟运行,程序入口为<code>main.js</code>。分析<code>main.js</code>找到了分数变化的函数<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590737597600-17b4aa6c-670d-4b69-b2fa-942c13fa41ba.png#align=left&display=inline&height=472&margin=%5Bobject%20Object%5D&name=image.png&originHeight=472&originWidth=907&size=30728&status=done&style=none&width=907" alt="image.png"><br>子弹击中后会将分数加 1。根据题目提示,改为每次加一个很大的数,再次编译运行,结束后得到 flag<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590738136606-25e3f97b-4ec2-4f06-a6b7-4c007ebc0b62.png#align=left&display=inline&height=1058&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1058&originWidth=1920&size=493858&status=done&style=none&width=1920" alt="image.png"></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{wechat_g4m1}</span><br></pre></td></tr></table></figure><h2 id="被汇编支配的恐惧"><a href="#被汇编支配的恐惧" class="headerlink" title="被汇编支配的恐惧"></a>被汇编支配的恐惧</h2><blockquote><p>以下 wp 参考带师傅 Sinon</p></blockquote><p>拿到图片和一个加密的压缩包,先看图片,在详细信息中可以看到这样一句话</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">我猜你是来找线索的,别爆破了,爆不出来的,因为密码有13位</span><br></pre></td></tr></table></figure><p>然后结合小姐姐放的 <code>hint</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jpg文件末尾有几位关键信息</span><br></pre></td></tr></table></figure><p>使用 <code>winhex</code> 打开图片。在图片末尾 <code>FF D9</code> 前可以看到 <code>ISBN</code> 这样的字样。联想 <code>ISBN</code> 号有 13 位数字,在结合图片的书名<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590739269870-c36ffcae-6715-40c2-972f-869f3dbe749f.png#align=left&display=inline&height=130&margin=%5Bobject%20Object%5D&name=image.png&originHeight=130&originWidth=575&size=47243&status=done&style=none&width=575" alt="image.png"><br>所以得到压缩包密码为</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">9787302333142</span><br></pre></td></tr></table></figure><p>打开压缩包,其中有 100 张图片,刚好是<code>10x10</code>的规格,而且看缩略图也能发现当排列为 10*10 的时候恰好图案可以对上,所以拼图,将其拼起来,并栅格化</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 商业转载请联系作者获得授权,非商业转载请注明出处。</span></span><br><span class="line"><span class="comment"># For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.</span></span><br><span class="line"><span class="comment"># 协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)</span></span><br><span class="line"><span class="comment"># 作者(Author):Sinon</span></span><br><span class="line"><span class="comment"># 链接(URL):https://dere.press/whuctf2020-writeup/#toc-head-36</span></span><br><span class="line"><span class="comment"># 来源(Source):Sinon的编程小站</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> PIL</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line">f_list = [Image.open(str(i) + <span class="string">'.bmp'</span>) <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">1</span>, <span class="number">101</span>)]</span><br><span class="line">res = Image.new(mode=<span class="string">'RGBA'</span>, size=(<span class="number">100</span>, <span class="number">100</span>), color=(<span class="number">256</span>, <span class="number">256</span>, <span class="number">256</span>))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>, <span class="number">100</span>):</span><br><span class="line"> x_pos = (i % <span class="number">10</span>) * <span class="number">10</span></span><br><span class="line"> y_pos = (i / <span class="number">10</span>) * <span class="number">10</span></span><br><span class="line"> res.paste(f_list[i], (x_pos, y_pos))</span><br><span class="line"></span><br><span class="line">res.save(<span class="string">"out.png"</span>)</span><br><span class="line"></span><br><span class="line">a = [i <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>, <span class="number">100</span>)]</span><br><span class="line"><span class="keyword">for</span> pix <span class="keyword">in</span> itertools.product(a, a):</span><br><span class="line"> <span class="keyword">if</span> pix[<span class="number">0</span>] % <span class="number">4</span> == <span class="number">2</span> <span class="keyword">or</span> pix[<span class="number">0</span>] % <span class="number">4</span> == <span class="number">2</span>:</span><br><span class="line"> res.putpixel(pix, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>))</span><br><span class="line"></span><br><span class="line">res.save(<span class="string">"out2.png"</span>)</span><br><span class="line">res.show()</span><br></pre></td></tr></table></figure><p>最后猜出 flag 为<code>WHUCTF{GUANG_SH@N}</code></p><h2 id="佛系青年-BingGe"><a href="#佛系青年-BingGe" class="headerlink" title="佛系青年 BingGe"></a>佛系青年 BingGe</h2><p>题目描述中一段佛曰加密</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">佛曰:般羅穆僧冥神大侄所隸奢尼哆恐侄大藐若故曳咒室呐阿竟諳他缽悉爍諦哆咒豆苦缽尼帝所冥等上哆瑟俱薩諸諳伊冥特諳實怯他罰不參亦皤有婆僧藝俱羯怯至皤滅知真哆訶亦能怯瑟梵陀奢知呼故梵夢死有皤能薩曰俱穆勝竟怯明奢參世缽佛皤羯瑟奢孕梵逝楞呐醯故奢想謹提諦盡侄阿哆利俱吉罰老謹涅神能皤集實輸奢薩奢數哆波者俱勝俱所遠盡呐倒利闍盧諦罰薩梵曰度提大諦哆穆輸醯怯參侄諸娑梵伽知勝穆伊顛冥參道冥有</span><br></pre></td></tr></table></figure><p>在在线网站解密得到</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">767566536773bf1ef643676363676784e1d015847635575637560ff4f41d</span><br></pre></td></tr></table></figure><p>看到题目描述中写道 <strong>栅栏边上</strong> ,并且解密的到的字符串像是一串 16 进制字符串,于是联想到栅栏密码解码后再十六进制解码。多次尝试后当栅栏数为 6 时得到</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">7768756374667b6e305f315f616d5f6e30745f615f36756464683173747d</span><br></pre></td></tr></table></figure><p>再十六进制解码,即可得到 flag:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whuctf{n0_1_am_n0t_a_6uddh1st}</span><br></pre></td></tr></table></figure><h2 id="shellOfAwd"><a href="#shellOfAwd" class="headerlink" title="shellOfAwd"></a>shellOfAwd</h2><p>以下 wp 来自带师傅</p><blockquote><p>出题思路:在虚拟机中开了一个 web 服务,将蚁剑的 base64 马拖进去,通过蚁剑的马上传一个冰蝎的马,执行 <code>ln -s /flag jquery.min.js</code> ,这样 <code>jquery.min.js</code> 就指向了 flag,读它就得到了 flag<br>其中 <code>ln -s /flag jquery.min.js</code> 在 AWD 中常用来维持权限,因为没人会管一个 js 文件。</p></blockquote><p>题目考点为冰蝎的加密流量分析<br>打开流量包,过滤 <code>http</code> 可以发现两条 <code>pass=xxx</code> 的流量,是冰蝎流量的特征<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590745195973-44526fbd-9067-4abd-9868-85780bd75c8a.png#align=left&display=inline&height=433&margin=%5Bobject%20Object%5D&originHeight=433&originWidth=1374&size=0&status=done&style=none&width=1374" alt=""><br>任选其中一条,追踪 TCP 流,在流中可以看到有两个返回的长度为 16 的字符串,这是冰蝎流量的解密密钥<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590745430182-2bc30964-03cb-4953-af2c-bc03f6ec1258.png#align=left&display=inline&height=750&margin=%5Bobject%20Object%5D&originHeight=750&originWidth=1149&size=0&status=done&style=none&width=1149" alt=""><br>我们在翻看 TCP 流的时候可以看到在 <code>Stream5</code> 之前都是蚁剑的 base64 加密流量,从 5 开始为冰蝎的加密流量,且密钥为<code>91ee1bfc4fd27c90</code><br>我们需要知道的是冰蝎通常采用 AES 加密,可以参考</p><ul><li><p><a href="https://cloud.tencent.com/developer/article/1552399" target="_blank" rel="noopener">冰蝎动态二进制加密 WebShell 基于流量侧检测方案</a></p></li><li><p><a href="https://xz.aliyun.com/t/6550" target="_blank" rel="noopener">红蓝对抗——加密 Webshell“冰蝎”攻防</a></p></li></ul><p>将下面的流量解密,<a href="http://tools.bugscaner.com/cryptoaes/" target="_blank" rel="noopener">在线网站</a>即可<br><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590745590177-2f4d950f-0cd7-4528-85c0-8c58ac94003c.png#align=left&display=inline&height=615&margin=%5Bobject%20Object%5D&originHeight=615&originWidth=1184&size=0&status=done&style=none&width=1184" alt=""><br>得到 base64 加密的字符串,再次解密,得到一段 php 代码</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">@error_reporting(<span class="number">0</span>);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">main</span><span class="params">($content)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $result = <span class="keyword">array</span>();</span><br><span class="line"> $result[<span class="string">"status"</span>] = base64_encode(<span class="string">"success"</span>);</span><br><span class="line"> $result[<span class="string">"msg"</span>] = base64_encode($content);</span><br><span class="line"> $key = $_SESSION[<span class="string">'k'</span>];</span><br><span class="line"> <span class="keyword">echo</span> encrypt(json_encode($result),$key);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encrypt</span><span class="params">($data,$key)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span>(!extension_loaded(<span class="string">'openssl'</span>))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span>($i=<span class="number">0</span>;$i<strlen($data);$i++) {</span><br><span class="line"> $data[$i] = $data[$i]^$key[$i+<span class="number">1</span>&<span class="number">15</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $data;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> openssl_encrypt($data, <span class="string">"AES128"</span>, $key);</span><br><span class="line"> }</span><br><span class="line">}$content=<span class="string">"6ac0a2b1-e69e-463e-8f1d-f19474de887f"</span>;</span><br><span class="line">main($content);</span><br></pre></td></tr></table></figure><p>其中<code>$key</code>还是之前加密流量用到的<code>91ee1bfc4fd27c90</code>,可以看到代码中再次进行了<code>AES128</code>加密,所以如果想要知道服务器返回了什么数据,就需要对返回流量进行解密</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/1118966/1590747335195-3a44779f-360b-4626-9200-09bcd564f44d.png#align=left&display=inline&height=305&margin=%5Bobject%20Object%5D&originHeight=305&originWidth=1254&size=0&status=done&style=none&width=1254" alt=""><br>同样用刚刚的网站即可解密,得到</p><figure class="highlight json"><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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"status"</span>: <span class="string">"c3VjY2Vzcw=="</span>,</span><br><span class="line"> <span class="attr">"msg"</span>: <span class="string">"NmFjMGEyYjEtZTY5ZS00NjNlLThmMWQtZjE5NDc0ZGU4ODdm"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>将其中的值解 base64,得到<strong>status</strong>的值为<strong>success</strong>,但<strong>msg</strong>的值并不是 flag,用同样的方法依次解密下面的流量,我们只需要挑这种比较短的流量尝试即可,而且密钥也和上述相同,最终在<strong>TCP 流 7</strong>的最下面找到 flag</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//$flag = "whuctf{cd768eac-0746-4979-a40d-5b6a269c4dde}"</span></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>咕咕咕,一咕咕半年</p>
</summary>
<category term="CTF" scheme="http://blog.zam-meow.cn/tags/CTF/"/>
<category term="WriteUp" scheme="http://blog.zam-meow.cn/tags/WriteUp/"/>
</entry>
<entry>
<title>2020“网鼎杯”HUST校内排位赛-部分WriteUp</title>
<link href="http://blog.zam-meow.cn/2020/04/20/2020%E2%80%9C%E7%BD%91%E9%BC%8E%E6%9D%AF%E2%80%9DHUST%E6%A0%A1%E5%86%85%E6%8E%92%E4%BD%8D%E8%B5%9B-%E9%83%A8%E5%88%86WriteUp/"/>
<id>http://blog.zam-meow.cn/2020/04/20/2020%E2%80%9C%E7%BD%91%E9%BC%8E%E6%9D%AF%E2%80%9DHUST%E6%A0%A1%E5%86%85%E6%8E%92%E4%BD%8D%E8%B5%9B-%E9%83%A8%E5%88%86WriteUp/</id>
<published>2020-04-20T12:59:14.000Z</published>
<updated>2020-04-20T13:08:57.586Z</updated>
<content type="html"><![CDATA[<p>鲸!萌新的第一次正规CTF,竟然连签到题都没做出来!(Misc1出题人出来挨打</p><p>题目好难,体验好差QAQ</p><a id="more"></a><h2 id="MISC"><a href="#MISC" class="headerlink" title="MISC"></a>MISC</h2><h3 id="菜"><a href="#菜" class="headerlink" title="菜"></a>菜</h3><h4 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h4><p>是什么蒙蔽了我的双眼,倒数第四个字符是O,根据附件提示解出flag,请提交flag{}内的内容;</p><h4 id="题目分析"><a href="#题目分析" class="headerlink" title="题目分析"></a>题目分析</h4><p>拿到题目的Attachment,发现是一张PNG图片。图片只是一张“单纯”的表情包。结合题目提示“蒙蔽双眼”,推测可能是改写了PNG文件头中的图片高度,来实现隐藏部分图片信息</p><p>右键查看文件属性,发现文件的高为 <code>730px</code> ,转换为十六进制值为 <code>02 DA</code> 。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Misc_%E8%8F%9C_%E5%9B%BE%E7%89%87%E5%B1%9E%E6%80%A7.png" alt="Misc_菜_图片属性"></p> </div><p>使用 <a href="http://www.x-ways.net/winhex/" target="_blank" rel="noopener">winhex</a> 打开文件,搜索HEX值为<code>02 DA</code>,并将其修改为 <code>03 DA</code> 以实现将图片拉长的操作</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Misc_%E8%8F%9C_winhex%E4%BF%AE%E6%94%B9.png" alt="Misc_菜_winhex修改"></p> </div><p>这样拉长之后,就能够得到被隐藏的flag。但是flag实在是太糊了,使用各种软件锐化都没能得出结果。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Misc_%E8%8F%9C_%E5%BE%97%E5%88%B0FLag.png" alt=""></p> </div><p>后来得知flag:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">l3hsec{ri1b7FLuoun2ZD0QOyv}</span><br></pre></td></tr></table></figure><h4 id="题目总结"><a href="#题目总结" class="headerlink" title="题目总结"></a>题目总结</h4><ul><li><code>PNG</code>文件的文件头是可以被修改以实现显示图像区域的变化,达到隐藏图像信息的目的</li></ul><h3 id="Boring-exe"><a href="#Boring-exe" class="headerlink" title="Boring_exe"></a>Boring_exe</h3><h4 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h4><p>出题人很无聊所以想出了这道题,根据附件提示解出flag,请提交flag{}内的内容;</p><h4 id="题目分析-1"><a href="#题目分析-1" class="headerlink" title="题目分析"></a>题目分析</h4><p>首先拿到题目的Attachment,发现是一个exe可执行文件。双击运行,没有任何结果。</p><p>之后使用 <a href="http://www.x-ways.net/winhex/" target="_blank" rel="noopener">winhex</a> 打开文件,查看文件内容。下拉发现“有东西”,出现了一个 <code>popi提问箱</code> 链接。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Misc_Boring_exe.jpg" alt="Misc_Boring_exe.jpg"></p> </div><p>访问该链接,拿到一个<code>fake flag</code></p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Misc_Boring_exe_popi.png" alt="Misc_Boring_exe_popi"></p> </div><p>但是这个头像操作似曾相识。确认过眼神,是和 <a href="https://adworld.xctf.org.cn/task/answer?type=misc&number=1&grade=0&id=5099&page=1" target="_blank" rel="noopener">攻防世界-Misc新手区-give_you_flag</a> 套路一样的题。</p><p>右键保存图片,使用 <code>PhotoShop</code> 补全定位符。</p><p>用手机扫描即可得到flag</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Misc_Boring_exe_flag.jpg" alt="Misc_Boring_exe_flag"></p> </div><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">l3hsec{misc_is_funnnnn!}</span><br></pre></td></tr></table></figure><h4 id="题目总结-1"><a href="#题目总结-1" class="headerlink" title="题目总结"></a>题目总结</h4><ul><li>需要熟练使用 <code>PhotoShop</code> 等软件对图像进行处理</li><li>遇到 <code>exe</code> 等文件时不一定非要逆向,没准使用 <code>WinHex</code> 直接查看文件内容会有意想不到的收获</li></ul><h2 id="Crypto"><a href="#Crypto" class="headerlink" title="Crypto"></a>Crypto</h2><h3 id="easy-crypto"><a href="#easy-crypto" class="headerlink" title="easy_crypto"></a>easy_crypto</h3><h4 id="题目分析-2"><a href="#题目分析-2" class="headerlink" title="题目分析"></a>题目分析</h4><p>首先打开附件,是一个 <code>java</code> 文件。</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigInteger;</span><br><span class="line"><span class="keyword">import</span> java.util.Random;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">crypto</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">static</span> BigInteger e = <span class="keyword">new</span> BigInteger(<span class="string">"114514"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> BigInteger p = <span class="keyword">new</span> BigInteger(<span class="string">"486782758980265419106566437773662434821707849903209898358740381800342941420169184139234071329598394271286443155137316343275438967772601578029350778343911038446374408250"</span>);</span><br><span class="line"> <span class="keyword">static</span> BigInteger h = <span class="keyword">new</span> BigInteger(<span class="string">"197285815436451554701121357540207727760367215453670717073481761209255345336604283966933286154040618892010511454547717773622062607956598784296775952923998110257788108099"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> String table = <span class="keyword">new</span> String(<span class="string">"0123456789abcdefghijklnmopqrstuvwxyz"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">Enc</span><span class="params">(String plaintext)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> BigInteger[] cipher = <span class="keyword">new</span> BigInteger[<span class="number">2</span>];</span><br><span class="line"> plaintext = plaintext.toLowerCase();</span><br><span class="line"> BigInteger r = <span class="keyword">new</span> BigInteger(<span class="keyword">new</span> Random().nextInt(<span class="number">10000000</span>)+<span class="string">""</span>);</span><br><span class="line"> String rtext = r.toString();</span><br><span class="line"> System.out.println(rtext);</span><br><span class="line"> <span class="keyword">int</span> rlen = rtext.length();</span><br><span class="line"> String text = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < plaintext.length(); i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> j = i % rlen;</span><br><span class="line"> text += table.charAt((table.indexOf(plaintext.charAt(i))+Character.getNumericValue(rtext.charAt(j))) % <span class="number">36</span>);</span><br><span class="line"> System.out.println(text);</span><br><span class="line"> }</span><br><span class="line"> BigInteger bText = <span class="keyword">new</span> BigInteger(text, <span class="number">36</span>);</span><br><span class="line"> cipher[<span class="number">0</span>] = e.modPow(r, p);</span><br><span class="line"> cipher[<span class="number">1</span>] = h.modPow(r, p).multiply(bText);</span><br><span class="line"> <span class="keyword">return</span> cipher[<span class="number">0</span>].toString(<span class="number">36</span>)+<span class="string">"||"</span>+cipher[<span class="number">1</span>].toString(<span class="number">36</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception</span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> System.out.println(<span class="string">"Welcome to l3hsec, here is the flag:"</span>);</span><br><span class="line"> String str1 = <span class="string">"This is the flag"</span>;</span><br><span class="line"> String str2 = Enc(str1); <span class="comment">// d4e03ge7tgvd3okpxq1l83w65q7vs55iwcav9ftehw9xtgfkn3oc3ofl2b52c6yjzl0jkn4xl83joqxlq023sacnpeddvq46709bz8kye1da||2h1oufyowds4axcoim3trm3kqm2hwlgbnrnblznktu4960o7hek0n9xgm9h1qfqq5w9k2i8wifbqv22c1mg8a79vwf8z6ydddbghvy3qzyq6jprbsjcv4o3ftwk5nmi</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>看到 <code>main</code> 函数中的这一段代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String str2 = Enc(str1); <span class="comment">// d4e03ge7tgvd3okpxq1l83w65q7vs55iwcav9ftehw9xtgfkn3oc3ofl2b52c6yjzl0jkn4xl83joqxlq023sacnpeddvq46709bz8kye1da||2h1oufyowds4axcoim3trm3kqm2hwlgbnrnblznktu4960o7hek0n9xgm9h1qfqq5w9k2i8wifbqv22c1mg8a79vwf8z6ydddbghvy3qzyq6jprbsjcv4o3ftwk5nmi</span></span><br></pre></td></tr></table></figure><p>与 <code>Enc</code> 函数中的这一段代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> cipher[<span class="number">0</span>].toString(<span class="number">36</span>)+<span class="string">"||"</span>+cipher[<span class="number">1</span>].toString(<span class="number">36</span>);</span><br></pre></td></tr></table></figure><p>很自然地猜想,注释||前边的那一串108位的字符串就是cipher[0]。</p><p>然后注意到 <code>Enc</code> 函数中的这一段代码。这段代码就是产生密文。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cipher[<span class="number">0</span>] = e.modPow(r, p);</span><br><span class="line">cipher[<span class="number">1</span>] = h.modPow(r, p).multiply(bText);</span><br></pre></td></tr></table></figure><p><code>e</code>, <code>p</code>, <code>h</code>是题目中已知的三个常量。因此解密的重心就放在了如何求出 <code>r</code> 这个变量上。可以写一段代码,暴力枚举出 <code>r</code> 的值</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigInteger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">crypto</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">static</span> BigInteger e = <span class="keyword">new</span> BigInteger(<span class="string">"114514"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> BigInteger p = <span class="keyword">new</span> BigInteger(<span class="string">"486782758980265419106566437773662434821707849903209898358740381800342941420169184139234071329598394271286443155137316343275438967772601578029350778343911038446374408250"</span>);</span><br><span class="line"> <span class="keyword">static</span> BigInteger h = <span class="keyword">new</span> BigInteger(<span class="string">"197285815436451554701121357540207727760367215453670717073481761209255345336604283966933286154040618892010511454547717773622062607956598784296775952923998110257788108099"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception</span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> String str1 = <span class="string">"d4e03ge7tgvd3okpxq1l83w65q7vs55iwcav9ftehw9xtgfkn3oc3ofl2b52c6yjzl0jkn4xl83joqxlq023sacnpeddvq46709bz8kye1da"</span>;</span><br><span class="line">BigInteger cipher=<span class="keyword">new</span> BigInteger(str1, <span class="number">36</span>);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=<span class="number">10000000</span>;i++){</span><br><span class="line"> BigInteger Z= <span class="keyword">new</span> BigInteger(i+<span class="string">""</span> );</span><br><span class="line"> BigInteger th = e.modPow(z,p);;</span><br><span class="line"> <span class="keyword">if</span>(th.tostring(<span class="number">36</span>).equals(cipher))</span><br><span class="line"> System.out.println(i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>得到 <code>r</code> 的值为 <code>6994579</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cipher[<span class="number">1</span>] = h.modPow(r, p).multiply(bText);</span><br></pre></td></tr></table></figure><p>根据以上代码,求得 <code>r</code> 后就可以求出 <code>bText</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">BigInteger bText = <span class="keyword">new</span> BigInteger(text, <span class="number">36</span>);</span><br></pre></td></tr></table></figure><p>然后就可以求出 <code>text</code> 为<code>i07tyvryvxxyojzqjvi</code>。再根据以下代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> String table = <span class="keyword">new</span> String(<span class="string">"0123456789abcdefghijklnmopqrstuvwxyz"</span>);</span><br></pre></td></tr></table></figure><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < plaintext.length(); i++)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">int</span> j = i % rlen;</span><br><span class="line"> text += table.charAt((table.indexOf(plaintext.charAt(i))+Character.getNumericValue(rtext.charAt(j))) % <span class="number">36</span>);</span><br><span class="line"> System.out.println(text);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>就可以由 <code>text</code> 反推出 <code>plaintext</code>,得到 <code>flag</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cryptoisnotthathard</span><br></pre></td></tr></table></figure><h2 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h2><h3 id="hardsql"><a href="#hardsql" class="headerlink" title="hardsql"></a>hardsql</h3><h4 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h4><p>居然有这么简单的SQL注入,请提交flag{}内的内容</p><h4 id="题目分析-3"><a href="#题目分析-3" class="headerlink" title="题目分析"></a>题目分析</h4><p>首先,打开在线测试环境。是一段 <code>PHP</code> 代码</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="keyword">include</span> <span class="string">"./config.php"</span>;</span><br><span class="line"><span class="keyword">include</span> <span class="string">"./flag.php"</span>;</span><br><span class="line">error_reporting(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">$username = $_REQUEST[<span class="string">'username'</span>];</span><br><span class="line">$passwd = $_REQUEST[<span class="string">'passwd'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>($username) || !<span class="keyword">isset</span>($passwd)) highlight_file(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"> $blacklist = <span class="string">"/admin|limit|by|substr|mid|like|or|char|union|select|greatest|\'|=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (preg_match($blacklist, $username) || preg_match($blacklist, $passwd)) <span class="keyword">exit</span>(<span class="string">"Try harder"</span>);</span><br><span class="line"></span><br><span class="line"> $queryuser = <span class="string">"select username from admin where username='$username' and passwd='$passwd'"</span>;</span><br><span class="line"></span><br><span class="line"> $userresult = mysqli_fetch_array(mysqli_query($conn, $queryuser), MYSQLI_ASSOC);</span><br><span class="line"></span><br><span class="line"> $querypass = <span class="string">"select passwd from admin where username='admin'"</span>;</span><br><span class="line"></span><br><span class="line"> $passresult = mysqli_fetch_array(mysqli_query($conn, $querypass), MYSQLI_ASSOC);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"<h1>username:$username<br></h1>"</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">"<h1>passwd:$passwd<br></h1>"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ($userresult[<span class="string">'username'</span>]) <span class="keyword">echo</span> <span class="string">"<h2>Welcome {$userresult['username']}</h2>"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (($passresult[<span class="string">'passwd'</span>]) && (strtolower($passresult[<span class="string">'passwd'</span>]) === strtolower($passwd))) <span class="keyword">echo</span> $flag;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>尝试直接访问 <code>flag.php</code> ,没有任何反应。并且,结合题目提示,分析重心集中在SQL注入上。</p><p>首先关注到 <code>$blacklist</code> 。 </p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$blacklist = <span class="string">"/admin|limit|by|substr|mid|like|or|char|union|select|greatest|\'|=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i"</span></span><br></pre></td></tr></table></figure><p>有一说一,这么长的正则,基本上把可能的平常用的注入操作都屏蔽了。因此需要使用SQL正则盲注这样的姿势。</p><p>然后注意到 <code>$quertpass</code> 。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$querypass = <span class="string">"select passwd from admin where username='admin'"</span>;</span><br></pre></td></tr></table></figure><p>并且下边的 <code>if</code> 判断中,还出现了 <code>flag</code> 的身影。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (($passresult[<span class="string">'passwd'</span>]) && (strtolower($passresult[<span class="string">'passwd'</span>]) === strtolower($passwd))) <span class="keyword">echo</span> $flag;</span><br></pre></td></tr></table></figure><p>这说明,我们需要找到的,是 <code>admin</code> 用户的 <code>password</code>。并且,最后不管登录的用户是谁,只需要知道 <code>admin</code> 用户的 <code>password</code> ,且 <code>username</code> 不为空,就可以显示 <code>$flag</code>。并且,该密码是小写的。</p><p>并且,题目中的SQL语句是这样的:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> username <span class="keyword">from</span> <span class="keyword">admin</span> <span class="keyword">where</span> username=<span class="string">'$username'</span> <span class="keyword">and</span> passwd=<span class="string">'$passwd'</span></span><br></pre></td></tr></table></figure><p>由于在 <code>$blacklist</code> 中过滤了空格,就使用/**/绕过。过滤了# - ,就使用;%00绕过。</p><p>因此,可以构造Payload如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">username=\&passwd=||passwd/**/REGEXP/**/"^w";%00</span><br></pre></td></tr></table></figure><p>之后就可以使用 <a href="https://portswigger.net/burp/" target="_blank" rel="noopener">BurpSuite</a> 进行爆破。</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web1%E7%88%86%E7%A0%B4.png" alt="Web1爆破"></p> </div><p>字典设置为 <code>0-9,a-z</code> 。将 <code>Response</code> 按长度排列。可以发现,长度较长的就是匹配爆破成功的 <code>Response</code>。</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web1%E7%88%86%E7%A0%B41.png" alt="Web1爆破1"></p> </div><p>从这里可以看出,密码的第一位为6。然后,将 <code>QueryString Payload</code> 中的正则表达式更改成 <code>^6w</code>,继续匹配第二位密码</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web1%E7%88%86%E7%A0%B42.png" alt="Web1爆破2"></p> </div><p>从这里可以看出,密码的第二位是8。然后依此类推,将密码的所有位都匹配出来</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web1%E7%88%86%E7%A0%B4%E6%9C%80%E7%BB%88.png" alt="Web1爆破最终"></p> </div><p>得到最终的 <code>passwd</code> : <code>68656c6c6f6279797a64646d7236</code>。传入非空的 <code>username</code> 以及该 <code>passwd</code> 即得到最终的 <code>flag</code>。</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web1_flag.png" alt="Web1_flag"></p> </div><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag{957150780883bae6c3e11cf0e4f9d6d1}</span><br></pre></td></tr></table></figure><h4 id="题目总结-2"><a href="#题目总结-2" class="headerlink" title="题目总结"></a>题目总结</h4><ul><li>无论遇到什么样的正则过滤,都不要怕,微笑着面对他,消除正则的最好办法就是要用正则勇敢地击败它!</li></ul><h3 id="你想吃麻辣香锅吗"><a href="#你想吃麻辣香锅吗" class="headerlink" title="你想吃麻辣香锅吗"></a>你想吃麻辣香锅吗</h3><h4 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h4><p>Naivekun最喜欢吃麻辣香锅了,你能帮帮他吗?请提交flag{}内的内容;</p><h4 id="题目分析-4"><a href="#题目分析-4" class="headerlink" title="题目分析"></a>题目分析</h4><p>首先进入在线测试环境,得到如下菜单</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">/ 主页</span><br><span class="line">/free 白嫖同学饭卡</span><br><span class="line">/list 列出菜单</span><br><span class="line">/add 点菜</span><br><span class="line">/delete 取消点菜</span><br><span class="line">/checkout 查看已点的菜</span><br><span class="line">/exit 跑路,不吃了!</span><br><span class="line">/hint 香锅食用指南</span><br></pre></td></tr></table></figure><p>既然有 <code>hint</code>,那肯定得先看看 <code>hint</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x7fffffff</span><br></pre></td></tr></table></figure><p>那么再 <code>/list</code> 看看菜单</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">点菜方式 /add?name=点啥&count=点几个</span><br><span class="line"></span><br><span class="line">name: Ghost_pepper</span><br><span class="line">price: 1</span><br><span class="line"></span><br><span class="line">name: Green_pepper</span><br><span class="line">price: 5</span><br><span class="line"></span><br><span class="line">name: Jolokia</span><br><span class="line">price: 10</span><br><span class="line"></span><br><span class="line">name: Hack_pepper</span><br><span class="line">price: 30</span><br><span class="line"></span><br><span class="line">name: Flag_pepper</span><br><span class="line">price: 31</span><br></pre></td></tr></table></figure><p>那么很明显,我们需要购买的就是这个 <code>Flag_pepper</code> 。</p><p>然而事情并没有那么简单,当我们在使用 <code>/free</code> 白嫖同学饭卡来增加余额时,余额增加到30之后就不会再增加了,这样就购买不了Flag_pepper。</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web_%E9%A6%99%E9%94%85_%E7%99%BD%E5%AB%96%E5%A4%B1%E8%B4%A5.png" alt="Web_香锅_白嫖失败"></p> </div><p>于是乎,联想之前给的 <code>hint</code>-<code>0x7fffffff</code> 猜测是否是需要请求购买一个数额巨大的商品,导致结算时商品金额溢出,以实现<strong>购买不要钱/倒贴钱</strong>的操作。</p><p>在尝试了多次之后,发现直接购买数额巨大的 <code>Flag_pepper</code> 是不可行的,推测后台对当前余额与商品价格之间先做了判断,当当前余额小于商品价格时就会直接报错。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E9%A6%99%E9%94%854.png" alt="Web_香锅4"></p> </div><p>因此,尝试使用购买大量的<code>Hack pepper</code>来使结算时商品金额溢出,以实现<strong>购买倒贴钱</strong>。经过尝试,发现当 <code>count</code> 金额足够大时,会溢出导致余额不减反增。(u1s1,买的菜不让退属实奸商)</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E9%A6%99%E9%94%851.png" alt="Web_香锅1"></p> </div><p>这时去购买一个 <code>Flag_pepper</code> ,就购买成功了。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E9%A6%99%E9%94%852.png" alt="Web_香锅2"></p> </div><p>再访问 <code>/checkout</code> 查看自己点的菜,flag就出现了。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E9%A6%99%E9%94%853.png" alt="Web_香锅3"></p> </div><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag{naivekun_wanna_a_g1rlfriend_2333}</span><br></pre></td></tr></table></figure><h4 id="题目总结-3"><a href="#题目总结-3" class="headerlink" title="题目总结"></a>题目总结</h4><ul><li>整型溢出是个好东西,当数值过大的时候计算结果可能就会和预想的不一致。白嫖谁不喜欢呢 \滑稽</li></ul><h3 id="你真的想吃麻辣香锅吗"><a href="#你真的想吃麻辣香锅吗" class="headerlink" title="你真的想吃麻辣香锅吗"></a>你真的想吃麻辣香锅吗</h3><h4 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h4><p>Naivekun最最最最最最喜欢吃麻辣香锅了,你能帮帮他吗?请提交flag{}内的内容;</p><h4 id="题目分析-5"><a href="#题目分析-5" class="headerlink" title="题目分析"></a>题目分析</h4><p>首先进入在线测试环境,和“小香锅”差不多,得到如下菜单</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">/ 主页</span><br><span class="line">/free 白嫖同学饭卡</span><br><span class="line">/list 列出菜单</span><br><span class="line">/add 点菜</span><br><span class="line">/delete 取消点菜</span><br><span class="line">/checkout 查看已点的菜</span><br><span class="line">/exit 跑路,不吃了!</span><br><span class="line">/hint 香锅食用指南</span><br></pre></td></tr></table></figure><p>既然有 <code>hint</code>,那肯定得先看看 <code>hint</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">你知道mutex吗</span><br></pre></td></tr></table></figure><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E8%B6%85%E7%BA%A7%E9%A6%99%E9%94%85.png" alt="Web_超级香锅"></p> </div><p>那么再 <code>/list</code> 看看菜单</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">点菜方式 /add?name=点啥&count=点几个</span><br><span class="line"></span><br><span class="line">name: Ghost_pepper</span><br><span class="line">price: 1</span><br><span class="line"></span><br><span class="line">name: Green_pepper</span><br><span class="line">price: 5</span><br><span class="line"></span><br><span class="line">name: Jolokia</span><br><span class="line">price: 10</span><br><span class="line"></span><br><span class="line">name: Hack_pepper</span><br><span class="line">price: 30</span><br><span class="line"></span><br><span class="line">name: Flag_pepper</span><br><span class="line">price: 31</span><br></pre></td></tr></table></figure><p>那么很明显,和之前的题目一样,我们需要购买的还是这个 <code>Flag_pepper</code> 。</p><p>首先试试在之前题目的 Trick 还能不能起作用。然而,这次似乎在数据上做了处理,不会像之前一样,存在着数据溢出的问题</p><div class="fancybox "> <p><img src="https://gitee.com/Zam-0703/BlogImage/raw/master/img/Web_%E8%B6%85%E7%BA%A7%E9%A6%99%E9%94%85_%E6%BA%A2%E5%87%BA%E5%A4%B1%E8%B4%A5.png" alt="Web_超级香锅_溢出失败"></p> </div><p>于是乎,联想之前给的 <code>hint</code>-<code>mutex</code>锁,猜测是否网站后端设计有缺陷,使用了多线程来提高并发的同时并没有对线程上线程锁,导致我们同时访问 <code>\free</code> 时,多个线程同时修改一个变量,有可能可以突破白嫖30元的上限。</p><p>因此,记录下我们访问这个网站时的 <code>cookie</code> 。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E8%B6%85%E7%BA%A7%E9%A6%99%E9%94%851.png" alt="Web_超级香锅1"></p> </div><p>直接写一个简单的 Python 脚本,使用 <code>threading</code> 和 <code>requests</code>,模块,并将 <code>cookie</code> 填入,来多线程白嫖</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests,threading</span><br><span class="line"></span><br><span class="line">api = <span class="string">"http://124.193.74.212:5517/free"</span></span><br><span class="line">_cookies = {<span class="string">'session'</span>:<span class="string">'MTU4NzIwNDY4NXxOd3dBTkRWSVRsSkxTRkJhVTB4UlFrMUZWVE5TU3pVelZUVlZWRlJJVUV0VFQxcEtOalEwVVUwMVZVUlRObGxaU2xsU05WRTBWMEU9fMVdDcSNCc2rhG3LHlUyPQBd0n5zMhdWiH8jB2nQOLxF'</span>}<span class="comment">#这里填入浏览器中的cookie</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span> <span class="params">()</span>:</span></span><br><span class="line"> n = <span class="number">0</span></span><br><span class="line"> <span class="keyword">while</span> n < <span class="number">3</span>:</span><br><span class="line"> requests.get(api,cookies=_cookies)</span><br><span class="line"> n++</span><br><span class="line"></span><br><span class="line">t1 = threading.Thread(target=free)</span><br><span class="line">t2 = threading.Thread(target=free)</span><br><span class="line">t3 = threading.Thread(target=free)</span><br><span class="line"></span><br><span class="line">t1.start()</span><br><span class="line">t2.start()</span><br><span class="line">t3.start()</span><br><span class="line">t1.join()</span><br><span class="line">t2.join()</span><br><span class="line">t3.join()</span><br></pre></td></tr></table></figure><p>然后我们的余额就有90了,愉快地购买了 <code>Flag pepper</code></p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E8%B6%85%E7%BA%A7%E9%A6%99%E9%94%853.png" alt="Web_超级香锅3"></p> </div><p>再访问 <code>/checkout</code> 查看自己点的菜,flag就出现了。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_%E8%B6%85%E7%BA%A7%E9%A6%99%E9%94%854.png" alt="Web_超级香锅4"></p> </div><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag{naivekun_wAnna_@_girlfr1end_2147483647}</span><br></pre></td></tr></table></figure><h4 id="题目总结-4"><a href="#题目总结-4" class="headerlink" title="题目总结"></a>题目总结</h4><ul><li>多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离。如果不隔离,就可以突破“白嫖”上限,造成数据错误。</li></ul>]]></content>
<summary type="html">
<p>鲸!萌新的第一次正规CTF,竟然连签到题都没做出来!(Misc1出题人出来挨打</p>
<p>题目好难,体验好差QAQ</p>
</summary>
<category term="CTF" scheme="http://blog.zam-meow.cn/tags/CTF/"/>
<category term="WriteUp" scheme="http://blog.zam-meow.cn/tags/WriteUp/"/>
</entry>
<entry>
<title>攻防世界-Web新手村-Writeup</title>
<link href="http://blog.zam-meow.cn/2020/04/16/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C-Web%E6%96%B0%E6%89%8B%E6%9D%91-Writeup/"/>
<id>http://blog.zam-meow.cn/2020/04/16/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C-Web%E6%96%B0%E6%89%8B%E6%9D%91-Writeup/</id>
<published>2020-04-16T10:53:14.000Z</published>
<updated>2020-04-16T12:24:17.288Z</updated>
<content type="html"><![CDATA[<p>XCTF线上练习网站<a href="https://adworld.xctf.org.cn/" target="_blank" rel="noopener">攻防世界</a>,新手的Web方向新手村Writeup。(总之很新就对了)</p><p>第一次写CTF题,也是第一次写Writeup,体验良好</p><a id="more"></a><h2 id="T1-view-source"><a href="#T1-view-source" class="headerlink" title="T1 view_source"></a>T1 view_source</h2><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>X老师让小宁同学查看一个网页的源代码,但小宁同学发现鼠标右键好像不管用了。</p><h3 id="题目分析"><a href="#题目分析" class="headerlink" title="题目分析"></a>题目分析</h3><p>进入在线题目场景后,发现鼠标右键失效……行呗,直接按 <code>F12</code> ,切换到 <code>Elements</code> 栏,查看网页源代码完事。</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Where is the FLAG<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"><span class="built_in">document</span>.oncontextmenu=<span class="keyword">new</span> <span class="built_in">Function</span>(<span class="string">"return false"</span>)</span></span><br><span class="line"><span class="javascript"><span class="built_in">document</span>.onselectstart=<span class="keyword">new</span> <span class="built_in">Function</span>(<span class="string">"return false"</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">h1</span>></span>FLAG is not here<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- cyberpeace{246200d6931d72f4da7a2b590b08b595} --></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>发现这个网页是通过使用脚本来达到使鼠标右键失效的。并且在注释中发现了flag:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cyberpeace{246200d6931d72f4da7a2b590b08b595}</span><br></pre></td></tr></table></figure><h3 id="题目总结"><a href="#题目总结" class="headerlink" title="题目总结"></a>题目总结</h3><ul><li>掌握查看源代码的方式,右键会被禁,但是<code>F12</code>谁也拦不住</li><li>在 <code>Web</code> 方向的题目中,有许多 <code>flag</code> 都会以 <code>注释</code> 等形式隐藏在源代码中</li></ul><h2 id="T2-robots"><a href="#T2-robots" class="headerlink" title="T2 robots"></a>T2 robots</h2><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>X老师上课讲了Robots协议,小宁同学却上课打了瞌睡,赶紧来教教小宁Robots协议是什么吧。</p><h3 id="题目分析-1"><a href="#题目分析-1" class="headerlink" title="题目分析"></a>题目分析</h3><p>首先,打开在线题目场景,发现什么都没有,空白一片,并且使用 <code>F12</code> 查看源代码,空空如也。只有注释中一句 <code><!--flag is not here--></code></p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_robots_1.png" alt=""></p> </div><p>结合题目 <code>Robots</code> 协议,猜测可能在网站的 <code>/robots.txt</code> 文件中会有东西。打开一看,果不其然。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_robots_2.png" alt=""></p> </div><p>根据提示,直接打开 <code>f1ag_1s_h3re.php</code> ,得到 <code>flag</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cyberpeace{b5941b7cbedb09d8e51524b7bfa79095}</span><br></pre></td></tr></table></figure><h3 id="题目总结-1"><a href="#题目总结-1" class="headerlink" title="题目总结"></a>题目总结</h3><ul><li><code>Robots</code> 协议通过放置在网站的根目录下的 <code>robots.txt</code> 文件来告诉搜索引擎的漫游器哪些内容是不应被搜索引擎获取的。但是 <code>Robots</code> 防君子(搜索引擎)不防小人(我)</li></ul><h2 id="T3-backup"><a href="#T3-backup" class="headerlink" title="T3 backup"></a>T3 backup</h2><h3 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h3><p>X老师忘记删除备份文件,他派小宁同学去把备份文件找出来,一起来帮小宁同学吧!</p><h3 id="题目分析-2"><a href="#题目分析-2" class="headerlink" title="题目分析"></a>题目分析</h3><p>打开在线题目场景,提示 <strong>你知道index.php的备份文件名吗?</strong></p><p><code>PHP</code> 的备份文件名通常的命名方式为 <code>文件名.bak</code> 或者 <code>文件名~</code> 于是乎直接访问 <code>index.php~</code>路径,404;访问 <code>index.php.bak</code> 路径,得到该 <code>.bak</code> 文件。文件内容如下:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"> <meta charset=<span class="string">"UTF-8"</span>></span><br><span class="line"> <title>备份文件</title></span><br><span class="line"> <link href=<span class="string">"http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css"</span> rel=<span class="string">"stylesheet"</span> /></span><br><span class="line"> <style></span><br><span class="line"> body{</span><br><span class="line"> margin-left:auto;</span><br><span class="line"> margin-right:auto;</span><br><span class="line"> margin-TOP:<span class="number">200</span>PX;</span><br><span class="line"> width:<span class="number">20</span>em;</span><br><span class="line"> }</span><br><span class="line"> </style></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"><h3>你知道index.php的备份文件名吗?</h3></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line">$flag=<span class="string">"Cyberpeace{855A1C4B3401294CB6604CCC98BDE334}"</span></span><br><span class="line"><span class="meta">?></span></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure><p>这样就获得了 <code>flag</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cyberpeace{855A1C4B3401294CB6604CCC98BDE334}</span><br></pre></td></tr></table></figure><h3 id="题目总结-2"><a href="#题目总结-2" class="headerlink" title="题目总结"></a>题目总结</h3><ul><li><code>PHP</code> 的备份文件名通常的命名方式为 <code>文件名.bak</code> 或者 <code>文件名~</code> </li></ul><h2 id="T4-cookie"><a href="#T4-cookie" class="headerlink" title="T4 cookie"></a>T4 cookie</h2><h3 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h3><p>X老师告诉小宁他在cookie里放了些东西,小宁疑惑地想:‘这是夹心饼干的意思吗?’</p><h3 id="题目分析-3"><a href="#题目分析-3" class="headerlink" title="题目分析"></a>题目分析</h3><p>打开在线题目场景,提示 <strong>你知道什么是cookie吗?</strong>,查看源代码,无果。想到题目说的“在cookie里放了些东西”于是准备使用 <code>F12</code> 中的 <code>Network</code> 工具抓包分析 <code>HTTP Response Header</code> 的 <code>Set-cookie</code> 字段。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_cookie_1.png" alt=""></p> </div><p>在 <code>HTTP Response</code> 中发现了 <code>Set-cookie</code> 字段有.东西。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_cookie_2.png" alt=""></p> </div><p>按照提示打开<code>cookie.php</code>,得到提示 <code>See the http response</code>。依旧是使用浏览器自带的 <code>Network</code> 工具分析 <code>HTTP Response</code>。发现字段 <code>flag</code> 的value就是我们所要找的 <code>flag</code>:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cyberpeace{902d038f991e55ae541a17aa66bb3e72}</span><br></pre></td></tr></table></figure><h3 id="题目总结-3"><a href="#题目总结-3" class="headerlink" title="题目总结"></a>题目总结</h3><ul><li>善用Google赐予你的抓包工具——<code>F12</code>中的<code>Network</code>标签</li><li><code>Cookie</code>在<code>Server</code>与<code>Client</code>之间的传输使用过程:<ol><li>首先,<code>Server</code> 通过 <code>HTTP Response Header</code> 中的 <code>Set-Cookie</code> 字段将 <code>cookie</code> 发送给<code>Client</code></li><li>在下一次 <code>Client</code> 发起 <code>HTTP Request</code> 时,<code>Client</code> 把 <code>cookie</code> 通过 <code>HTTP Request Header</code> 中的 <code>Cookie</code> 字段发送给server</li><li>每次 <code>HTTP Request</code> ,Cookie都会被发送。</li></ol></li></ul><h2 id="T5-disabled-button"><a href="#T5-disabled-button" class="headerlink" title="T5 disabled_button"></a>T5 disabled_button</h2><h3 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h3><p>X老师今天上课讲了前端知识,然后给了大家一个不能按的按钮,小宁惊奇地发现这个按钮按不下去,到底怎么才能按下去呢?</p><h3 id="题目分析-4"><a href="#题目分析-4" class="headerlink" title="题目分析"></a>题目分析</h3><p>首先,进入在线题目场景,提示一个不能按的按钮,并且按钮确实“按不下去”,怀疑是在源代码中对这个按钮做了限制。在该按钮元素上 <code>右键 -> 检查</code> 来查看定义该按钮的HTML代码。</p><div class="fancybox "> <p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Web_disabled_button_1.png" alt=""></p> </div><p>于是乎,发现该按钮被 <code>disabled</code> 了。将该处修改为 <code>abled</code> 或者直接删去,再点击该按钮就会显示出 <code>Flag</code></p><h4 id="这里插入图片"><a href="#这里插入图片" class="headerlink" title="这里插入图片"></a>这里插入图片</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cyberpeace{1b36491fe9a1ae35a4455929e57f902d}</span><br></pre></td></tr></table></figure><h3 id="题目总结-4"><a href="#题目总结-4" class="headerlink" title="题目总结"></a>题目总结</h3><ul><li>当在网页中遇到一些限制时,往往修改一下源代码就可以解除限制。</li></ul><h2 id="T6-weak-auth"><a href="#T6-weak-auth" class="headerlink" title="T6 weak_auth"></a>T6 weak_auth</h2><h3 id="题目描述-5"><a href="#题目描述-5" class="headerlink" title="题目描述"></a>题目描述</h3><p>小宁写了一个登陆验证页面,随手就设了一个密码。</p><h3 id="题目分析-5"><a href="#题目分析-5" class="headerlink" title="题目分析"></a>题目分析</h3><p>进入在线题目场景,除了两个输入框和</p><h3 id="题目总结-5"><a href="#题目总结-5" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T7-simple-php"><a href="#T7-simple-php" class="headerlink" title="T7 simple_php"></a>T7 simple_php</h2><h3 id="题目描述-6"><a href="#题目描述-6" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-6"><a href="#题目分析-6" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-6"><a href="#题目总结-6" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T8-get-post"><a href="#T8-get-post" class="headerlink" title="T8 get_post"></a>T8 get_post</h2><h3 id="题目描述-7"><a href="#题目描述-7" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-7"><a href="#题目分析-7" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-7"><a href="#题目总结-7" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T9-xff-referer"><a href="#T9-xff-referer" class="headerlink" title="T9 xff_referer"></a>T9 xff_referer</h2><h3 id="题目描述-8"><a href="#题目描述-8" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-8"><a href="#题目分析-8" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-8"><a href="#题目总结-8" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T10-webshell"><a href="#T10-webshell" class="headerlink" title="T10 webshell"></a>T10 webshell</h2><h3 id="题目描述-9"><a href="#题目描述-9" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-9"><a href="#题目分析-9" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-9"><a href="#题目总结-9" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T11-command-execution"><a href="#T11-command-execution" class="headerlink" title="T11 command_execution"></a>T11 command_execution</h2><h3 id="题目描述-10"><a href="#题目描述-10" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-10"><a href="#题目分析-10" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-10"><a href="#题目总结-10" class="headerlink" title="题目总结"></a>题目总结</h3><h2 id="T12-simple-js"><a href="#T12-simple-js" class="headerlink" title="T12 simple_js"></a>T12 simple_js</h2><h3 id="题目描述-11"><a href="#题目描述-11" class="headerlink" title="题目描述"></a>题目描述</h3><h3 id="题目分析-11"><a href="#题目分析-11" class="headerlink" title="题目分析"></a>题目分析</h3><h3 id="题目总结-11"><a href="#题目总结-11" class="headerlink" title="题目总结"></a>题目总结</h3>]]></content>
<summary type="html">
<p>XCTF线上练习网站<a href="https://adworld.xctf.org.cn/" target="_blank" rel="noopener">攻防世界</a>,新手的Web方向新手村Writeup。(总之很新就对了)</p>
<p>第一次写CTF题,也是第一次写Writeup,体验良好</p>
</summary>
<category term="CTF" scheme="http://blog.zam-meow.cn/tags/CTF/"/>
<category term="Writeup" scheme="http://blog.zam-meow.cn/tags/Writeup/"/>
<category term="Adworld" scheme="http://blog.zam-meow.cn/tags/Adworld/"/>
</entry>
<entry>
<title>Jupyter的安装与使用</title>
<link href="http://blog.zam-meow.cn/2020/01/19/Jupyter%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/"/>
<id>http://blog.zam-meow.cn/2020/01/19/Jupyter%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/</id>
<published>2020-01-19T04:49:19.000Z</published>
<updated>2020-04-16T02:14:39.776Z</updated>
<content type="html"><![CDATA[<p>最近开始学习 Python,但是臃肿的 Visual Studio 启动和编译速度极慢,直接使用命令行又没法保存我敲的代码。于是乎,我就想整一个又能即时保存我敲的代码,又能快速地运行 Python 程序,最好还能支持 Markdown,让我能够边学习边做笔记。在经过了一番寻找过后,我找到了这款神器:Jupyter</p><a id="more"></a><h1 id="Jupyter-简介"><a href="#Jupyter-简介" class="headerlink" title="Jupyter 简介"></a>Jupyter 简介</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Jupyter Notebook是一个Web应用程序,允许您创建和共享包含实时代码,方程,可视化和说明文本的文档。</span><br><span class="line">用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等。</span><br></pre></td></tr></table></figure><p>在 Notebooks 中不仅可以运行 Python 语言,它还支持 R、Julia 和 Javascript 等等语言。</p><h1 id="安装-Jupyter-Notebooks"><a href="#安装-Jupyter-Notebooks" class="headerlink" title="安装 Jupyter Notebooks"></a>安装 Jupyter Notebooks</h1><h2 id="通过-pip-安装"><a href="#通过-pip-安装" class="headerlink" title="通过 pip 安装"></a>通过 pip 安装</h2><p>既然你都已经想要安装 Jupyter 了,那么我想 Python 总已经安装并配置好了吧。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">注意:Python中默认安装的版本:</span><br><span class="line">Python 2.7.9及后续版本:默认安装,命令为pip</span><br><span class="line">Python 3.4及后续版本:默认安装,命令为pip3</span><br></pre></td></tr></table></figure><h3 id="升级-pip-到最新版本"><a href="#升级-pip-到最新版本" class="headerlink" title="升级 pip 到最新版本"></a>升级 pip 到最新版本</h3><p>打开<code>cmd</code>,并切换到 Python 的安装目录下的 Scripts 文件夹,然后执行以下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3 install --upgrade pip3</span><br></pre></td></tr></table></figure><h3 id="安装-Jupyter-Noteboooks"><a href="#安装-Jupyter-Noteboooks" class="headerlink" title="安装 Jupyter Noteboooks"></a>安装 Jupyter Noteboooks</h3><p>接下来执行以下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3 install jupyter</span><br></pre></td></tr></table></figure><p>等待进度条跑完即可<br>(u1s1,pip 是真的方便</p><h3 id="运行-Jupyter-Notebooks"><a href="#运行-Jupyter-Notebooks" class="headerlink" title="运行 Jupyter Notebooks"></a>运行 Jupyter Notebooks</h3><p>切换到 Python 的安装目录下的 Scripts 文件夹。执行如下命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jupyter notebook</span><br></pre></td></tr></table></figure><p>然后浏览器就会自动打开 Notebook 窗口。<br>但是此时你所创建的 notebook 文件都是放置在<code>你的Python安装目录\Script</code>下。之后会提到如何修改 Jupyter Notebook 的工作空间。</p><h2 id="通过-Anaconda-安装"><a href="#通过-Anaconda-安装" class="headerlink" title="通过 Anaconda 安装"></a>通过 Anaconda 安装</h2><p>在安装<code>Anaconda</code>的同时会安装 Python 和 Jupyter Notebooks 这两个工具,并且还包含相当多数据科学和机器学习社区常用的软件包。<br>关于 Anaconda 的安装与使用,可以参考这个链接:<a href="https://www.jianshu.com/p/62f155eb6ac5" target="_blank" rel="noopener">Anaconda 介绍、安装及使用教程</a><br>安装好了 Anaconda 后,可以直接在 Anaconda 的命令提示符界面输入以下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jupyter notebook</span><br></pre></td></tr></table></figure><p>然后就可以食用 Jupyter 了</p><h1 id="修改-Jupyter-Notebook-工作空间"><a href="#修改-Jupyter-Notebook-工作空间" class="headerlink" title="修改 Jupyter Notebook 工作空间"></a>修改 Jupyter Notebook 工作空间</h1><p>在我们第一次启动 Notebooks 时,默认显示的是 Script 文件夹下的文件目录。因为此时 notebooks 默认的工作空间是安装目录。<br>但是这样的话找起文件来比较麻烦。我们可以自己定义一个专属的工作空间</p><h2 id="获取-Jupyter-Notebook-的配置文件"><a href="#获取-Jupyter-Notebook-的配置文件" class="headerlink" title="获取 Jupyter Notebook 的配置文件"></a>获取 Jupyter Notebook 的配置文件</h2><p>打开命令提示窗口,执行如下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jupyter notebook --generate-config</span><br></pre></td></tr></table></figure><p>此处需要注意的是,如果你已经配置过 notebooks 的相关信息,执行此命令会提示你是否覆盖原有配置。输入 y 直接覆盖。如果是首次执行此命令,则生成配置到相应目录。</p><h2 id="修改配置文件"><a href="#修改配置文件" class="headerlink" title="修改配置文件"></a>修改配置文件</h2><p>打开生成的配置文件,修改<code>#c.NotebookApp.notebook_dir = ''</code>此条配置,在单引号中填入我们刚才创建的专属工作空间,此处我这里是 E:\MyTools\Python\jupyter-notebook。<br>要注意的是,由于转义字符这种神奇的存在。所以你需要在字符串的单引号前加 r,或者将单斜杠变为双斜杠,如下图所示。<br>此条配置默认是注释掉的,所以我们需要删除第一个#。ok,保存配置文件。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E9%85%8D%E7%BD%AEJupyter.png" alt="配置Jupyter.png"><br>好了,现在打开命令提示窗口,执行<code>jupyter notebook</code>命令重新启动 Notebook,浏览器相应会打开 Notebook 主页,主页中相应会显示工作空间中的文件目录。<br><strong><em>注意:启动 notebook 之后,不要关闭该命令提示窗口。一旦关闭该窗口 Jupyter 的本地服务器就会被关闭。</em></strong></p><h1 id="Jupyter-Notebook-基本使用"><a href="#Jupyter-Notebook-基本使用" class="headerlink" title="Jupyter Notebook 基本使用"></a>Jupyter Notebook 基本使用</h1><h2 id="Jupyter-界面"><a href="#Jupyter-界面" class="headerlink" title="Jupyter 界面"></a>Jupyter 界面</h2><p>起订 Notebook 后,界面应该是这样的:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Jupyter%E7%95%8C%E9%9D%A2.png" alt="Jupyter界面"></p><h3 id="第一部分"><a href="#第一部分" class="headerlink" title="第一部分"></a>第一部分</h3><ul><li><code>Files</code>:列出所有文件</li><li><code>Running</code>:展示你当前打开的终端和笔记本</li><li><code>Clusters</code>:由 IPython 并行提供的</li></ul><h3 id="第二部分"><a href="#第二部分" class="headerlink" title="第二部分"></a>第二部分</h3><p>点击右侧的 New 按钮可展开如图的下拉列表按钮,其内包括了可创建的四种工作环境:</p><ul><li><code>Python3</code>:创建一个可以执行 Python 代码的 ipynb 文件</li><li><code>Text File</code>:创建文本类型的 txt 文件</li><li><code>Folder</code>:创建一个文件夹</li><li><code>Teminal</code>:在浏览器中打开一个命令窗口</li></ul><h3 id="第三部分"><a href="#第三部分" class="headerlink" title="第三部分"></a>第三部分</h3><p>这里的按钮其实就是对当前工作空间内的文件进行一系列操作:</p><ul><li><code>Duplicate</code>:复制文件</li><li><code>Rename</code>:重命名</li><li><code>Move</code>:移动文件</li><li><code>Download</code>:下载文件</li><li><code>View</code>:在浏览器中预览文件内容</li><li><code>Edit</code>:编辑文件</li><li><code>Delete</code>(小图标):删除选中的文件</li></ul><h2 id="Jupyter-Notebook-中编写并执行-Python-代码"><a href="#Jupyter-Notebook-中编写并执行-Python-代码" class="headerlink" title="Jupyter Notebook 中编写并执行 Python 代码"></a>Jupyter Notebook 中编写并执行 Python 代码</h2><p>在首页右侧点击<code>New</code>,选择点击<code>Python3,</code>页面即跳转到一个新的窗口,此时已经创建了一个新的文件,红色区域为该文件的名称(默认为 Untitled),点击即可修改文件名,此处我们命名为 Hello_World,如下图所示。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Jupyter_Demo1.png" alt="Jupyter_Demo1"><br>在<code>In [ ] :</code>后面的输入框中我们可以输入一段 python 代码进行测试,点击<code>Run</code>按钮执行,也可以快捷键<code>Ctrl+Enter</code>执行代码,结果如下:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Jupyter_Demo2.png" alt="Jupyter_Demo2"><br>Jupyter Notebooks 的强大之处在于除了能够输入代码之外,你还可以用 Markdown 添加叙述性和解释性文本。比如我想添加一个文字说明,在代码上面添加了一个单元格,并以 Markdown 输入了一个文本。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Jupyter_Demo3.png" alt="Jupyter_Demo3"><br>按下<code>Ctrl+Enter</code>,效果如下:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Jupyter_Demo4.png" alt="Jupyter_Demo4"></p><h2 id="Jupyter-Notebook-中的快捷键介绍"><a href="#Jupyter-Notebook-中的快捷键介绍" class="headerlink" title="Jupyter Notebook 中的快捷键介绍"></a>Jupyter Notebook 中的快捷键介绍</h2><p>当你熟练使用 notebooks 的基本功能后,掌握他的快捷键是十分必要的,这样可以大大提高你的工作效率。下面是一些比较常用的快捷键:</p><p>编辑模式:点击单元格按下<code>Enter</code><br>命令模式(退出编辑模式):按下<code>Esc</code></p><p>进入命令模式之后(此时你没有活跃单元),有以下快捷键:</p><ul><li><code>A</code>:在所选单元之上插入一个新的单元</li><li><code>B</code>:在所选单元之下插入一个新的单元</li><li><code>D</code>:连续按两次删除所选的单元</li><li><code>Z</code>:撤销被删除的单元</li><li><code>Y</code>:将当前选中的单元变成一个代码单元</li><li><code>F</code>:查找和替换</li><li><code>Shift +上或下箭头</code>:可选择多个单元。</li><li><code>Shift + M</code>:在多选模式时,可合并你的选择。</li></ul><p>处于编辑模式时(在命令模式时按 Enter 会进入编辑模式),下列快捷键很有用:</p><ul><li><code>Ctrl + Home</code>:到达单元起始位置</li><li><code>Ctrl + S</code>:保存进度</li><li><code>Ctrl + Enter</code>:会运行你的整个单元块</li><li><code>Alt + Enter</code>:不止会运行你的单元块,还会在下面添加一个新单元</li><li><code>Ctrl + Shift + F</code>:打开命令面板<br>可在命令模式按 H 或进入 Help > Keyboard Shortcuts。可以查看键盘快捷键完整列表。</li></ul><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>关于 Jupyter Notebooks 的安装和基本用法就先介绍到这里就差不多了。</p>]]></content>
<summary type="html">
<p>最近开始学习 Python,但是臃肿的 Visual Studio 启动和编译速度极慢,直接使用命令行又没法保存我敲的代码。于是乎,我就想整一个又能即时保存我敲的代码,又能快速地运行 Python 程序,最好还能支持 Markdown,让我能够边学习边做笔记。在经过了一番寻找过后,我找到了这款神器:Jupyter</p>
</summary>
<category term="Jupyter" scheme="http://blog.zam-meow.cn/tags/Jupyter/"/>
<category term="Python" scheme="http://blog.zam-meow.cn/tags/Python/"/>
</entry>
<entry>
<title>AST-Task4</title>
<link href="http://blog.zam-meow.cn/2019/11/26/AST-Task4/"/>
<id>http://blog.zam-meow.cn/2019/11/26/AST-Task4/</id>
<published>2019-11-26T00:24:05.000Z</published>
<updated>2020-04-16T02:14:14.016Z</updated>
<content type="html"><![CDATA[<p>咕咕咕了这么久,终于有空好好学学树与二叉树的数据结构了。</p><p>Update 2020.04.16 :过去了半年,依旧还没有完成…… <em>咕咕咕,咕咕咕,咕叽咕叽咕</em></p><a id="more"></a><h1 id="树的基本概念"><a href="#树的基本概念" class="headerlink" title="树的基本概念"></a>树的基本概念</h1><p>下面是一些关于树的基本术语:</p><table><thead><tr><th>术语</th><th>中文</th><th>描述</th></tr></thead><tbody><tr><td>Root</td><td>根节点</td><td>The top node in a tree.</td></tr><tr><td>Child</td><td>子节点</td><td>A node directly connected to another node when moving away from the Root.</td></tr><tr><td>Leaf</td><td>叶子节点</td><td>A node with no children</td></tr><tr><td>Edge</td><td>边</td><td>The connection between one node and another.</td></tr><tr><td>Path</td><td>路径</td><td>A sequence of nodes and edges connecting a node with a descendant.</td></tr><tr><td>Height</td><td>节点高度</td><td>The height of a node is the number of edges on the longest path between that node and a leaf.</td></tr><tr><td>Level</td><td>层级</td><td>The level of a node is defined by 1 + (the number of connections between the node and the root).</td></tr><tr><td>Depth</td><td>深度</td><td>The depth of a node is the number of edges from the tree’s root node to the node.</td></tr><tr><td>Degree</td><td>度</td><td>The number of subtrees of a node.</td></tr></tbody></table><p>下面通过几个图解释树的术语</p><p><img src="https://img-blog.csdn.net/2018060421094461?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="Edge、Root、Leaf"><br><img src="https://img-blog.csdn.net/20180604212429282?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="Path"><br><img src="https://img-blog.csdn.net/20180604213303945?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="Height"></p><p>需要注意的是叶子节点的高度为 0,如果树只有一个节点,那么这个节点的高也是 0</p><p><img src="https://img-blog.csdn.net/20180604215258931?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="Depth"></p><p>需要注意的是根节点的深度(Depth)是 0.</p><p>从 Height 和 Depth 的对比,它们的方向刚好是相反的。<br>对于 Height 和 Depth 不用死记,我们可以把树倒过来看,也就是我们现实生活当中的树,求某个节点的 Height 那肯定是从根部往上的方向;如果是求某个节点的深度,方向肯定是向下的。</p><p><img src="https://img-blog.csdn.net/20180604220744284?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="Level"></p><p>节点的 Level 是从 1 开始的,Level = Depth+1,根节点的 Level=1<br>也有很多书籍上 Level 是从 0 开始的,这样的话 Level 就等于 Depth,根节点的 Level=0</p><p>reference:<a href="https://blog.csdn.net/johnny901114/article/details/80574803" target="_blank" rel="noopener">数据结构与算法(七)树和二叉树</a></p><h1 id="二叉树的基本概念"><a href="#二叉树的基本概念" class="headerlink" title="二叉树的基本概念"></a>二叉树的基本概念</h1><p>二叉树是一个每个最结最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。</p><p>也就是说二叉树节点的度最大也就是 2,而普通的树,节点的度是没有限制的。</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>所有的叶子节点的 Height 或者 Level 都相等</li></ul><p>例如下面就是一个完美二叉树:<br><img src="https://img-blog.csdn.net/2018060422441774?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="完美二叉树"></p><h3 id="完全二叉树"><a href="#完全二叉树" class="headerlink" title="完全二叉树"></a>完全二叉树</h3><p>完全二叉树是 除了最后一层都是满的(都有两个子节点),并且最后一层的节点是从左往右排列的。</p><p>完全二叉树,通俗点说就是节点按层从左往右排列。如果最后一层排满了就是完美二叉树,没有满则是完全二叉树。<br>所以完美二叉树一定是完全二叉树,完全二叉树不一定是完美二叉树。</p><p>一个完全二叉树可以高效的使用数组来表示。</p><p>例如下面就是一个完全二叉树:<br><img src="https://img-blog.csdn.net/20180604225850509?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="完全二叉树"></p><h3 id="完满二叉树"><a href="#完满二叉树" class="headerlink" title="完满二叉树"></a>完满二叉树</h3><p>完满二叉树就简单了,就是每个节点都有两个子节点。也就是说它比完美二叉树少了一个条件。</p><p>例如下面就是一个完满二叉树:<br><img src="https://img-blog.csdn.net/20180604230227328?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pvaG5ueTkwMTExNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="完满二叉树"></p><h1 id="学习并实现树的相关操作"><a href="#学习并实现树的相关操作" class="headerlink" title="学习并实现树的相关操作"></a>学习并实现树的相关操作</h1><p>在这里,我们定义一个如下的二叉树数据结构:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">char</span> BTDataType;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">BTNode</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">BTNode</span> * _<span class="title">pLeft</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">BTNode</span> * _<span class="title">pRight</span>;</span></span><br><span class="line">BTDataType _data;</span><br><span class="line">}BTNode;</span><br></pre></td></tr></table></figure><h2 id="遍历二叉树"><a href="#遍历二叉树" class="headerlink" title="遍历二叉树"></a>遍历二叉树</h2><p>从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:</p><p>⑴ 访问结点本身(N)</p><p>⑵ 遍历该结点的左子树(L)</p><p>⑶ 遍历该结点的右子树(R)</p><p>以上三种操作有六种执行次序:</p><p><code>NLR、LNR、LRN、NRL、RNL、RLN。</code></p><p>注意:</p><p>前三种次序与后三种次序对称,故只讨论先左后右的前三种次序。</p><p>因此,我们就可以根据访问结点操作发生位置命名:</p><ul><li>NLR:前序遍历(Preorder Traversal 亦称(先序遍历)) ——访问根结点的操作发生在遍历其左右子树之前。</li><li>LNR:中序遍历(Inorder Traversal) ——访问根结点的操作发生在遍历其左右子树之中(间)。</li><li>LRN:后序遍历(Postorder Traversal) ——访问根结点的操作发生在遍历其左右子树之后。<br>注意:</li></ul><p>由于被访问的结点必是某子树的根,所以 N(Node)、L(Left subtree)和 R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR 和 LRN 分别又称为先根遍历、中根遍历和后根遍历。</p><h3 id="先序遍历"><a href="#先序遍历" class="headerlink" title="先序遍历"></a>先序遍历</h3><p>若二叉树非空,则依次执行如下操作:<br>⑴ 访问根结点;<br>⑵ 遍历左子树;<br>⑶ 遍历右子树。<br>递归算法实现:</p><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="function"><span class="keyword">void</span> <span class="title">PreOrder</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%c "</span>, pRoot->_data);</span><br><span class="line">PreOrder(pRoot->_pLeft);</span><br><span class="line">PreOrder(pRoot->_pRight);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h3><p>若二叉树非空,则依次执行如下操作:<br>⑴ 遍历左子树;<br>⑵ 访问根结点;<br>⑶ 遍历右子树。<br>递归算法实现:</p><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="function"><span class="keyword">void</span> <span class="title">InOrder</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot)</span><br><span class="line">{</span><br><span class="line">InOrder(pRoot->_pLeft);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%c "</span>, pRoot->_data);</span><br><span class="line">InOrder(pRoot->_pRight);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="后序遍历"><a href="#后序遍历" class="headerlink" title="后序遍历"></a>后序遍历</h3><p>若二叉树非空,则依次执行如下操作:<br>⑴ 遍历左子树;<br>⑵ 遍历右子树;<br>⑶ 访问根结点。<br>递归算法实现:</p><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="function"><span class="keyword">void</span> <span class="title">PostOrder</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot)</span><br><span class="line">{</span><br><span class="line">PostOrder(pRoot->_pLeft);</span><br><span class="line">PostOrder(pRoot->_pRight);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%c "</span>, pRoot->_data);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="层序遍历"><a href="#层序遍历" class="headerlink" title="层序遍历"></a>层序遍历</h3><p>除了以上三种以根节点相对于它的左右孩子的访问顺序定义的遍历算法之外,二叉树还有一种遍历方式,就是层序遍历。<br>递归算法实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">LevelOrder</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span>(pRoot == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line">Queue q;</span><br><span class="line">QueueInit(&q);</span><br><span class="line">QueuePush(&q, pRoot);</span><br><span class="line"><span class="keyword">while</span>(!QueueEmpty(&q))</span><br><span class="line">{</span><br><span class="line">pRoot = QueueFront(&q);</span><br><span class="line">QueuePop(&q);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%c "</span>, pRoot->_data);</span><br><span class="line"><span class="keyword">if</span>(pRoot->_pLeft!=<span class="literal">NULL</span>)</span><br><span class="line">QueuePush(&q, pRoot->_pLeft);</span><br><span class="line"><span class="keyword">if</span>(pRoot->_pRight!=<span class="literal">NULL</span>)</span><br><span class="line">QueuePush(&q, pRoot->_pRight);</span><br><span class="line">}</span><br><span class="line">QueueDestroy(&q);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>reference:<a href="https://blog.csdn.net/monster_ii/article/details/82115772" target="_blank" rel="noopener">二叉树的前中后和层序遍历详细图解(递归和非递归写法)</a></p><h2 id="新建一个树节点"><a href="#新建一个树节点" class="headerlink" title="新建一个树节点"></a>新建一个树节点</h2><p>C 语言实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">BTNode* <span class="title">BuyNewNode1</span><span class="params">(BTDataType data)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">BTNode* root = (BTNode*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(BTNode));</span><br><span class="line"><span class="keyword">if</span> (root == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line">assert(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line">root->_data = data;</span><br><span class="line">root->_pLeft = <span class="literal">NULL</span>;</span><br><span class="line">root->_pRight = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">return</span> root;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="创建二叉树"><a href="#创建二叉树" class="headerlink" title="创建二叉树"></a>创建二叉树</h2><p>C 语言实现</p><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">BTNode * _CreateBinTree(BTDataType * <span class="built_in">array</span>, <span class="keyword">int</span> size, <span class="keyword">int</span>* index,BTDataType invalid)</span><br><span class="line">{</span><br><span class="line">BTNode* pRoot = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">if</span> ((*index) < size && invalid != <span class="built_in">array</span>[*index] )</span><br><span class="line">{</span><br><span class="line">pRoot = BuyNewNode1(<span class="built_in">array</span>[*index]);</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建左子树;</span></span><br><span class="line">++(*index);</span><br><span class="line">pRoot->_pLeft = _CreateBinTree(<span class="built_in">array</span>, size, index,invalid);</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建右子树</span></span><br><span class="line">++(*index);</span><br><span class="line">pRoot->_pRight = _CreateBinTree(<span class="built_in">array</span>, size, index,invalid);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> pRoot;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">BTNode* <span class="title">CreateBinTree</span><span class="params">(BTDataType* <span class="built_in">array</span>, <span class="keyword">int</span> size, BTDataType invalid)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">return</span> _CreateBinTree(<span class="built_in">array</span>, size, &index, invalid);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="拷贝二叉树"><a href="#拷贝二叉树" class="headerlink" title="拷贝二叉树"></a>拷贝二叉树</h2><p>C 语言实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">BTNode* <span class="title">CopyBinTree</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">BTNode* newpRoot = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">if</span> (pRoot == <span class="literal">NULL</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">newpRoot = BuyBinTreeNode(pRoot->_data);</span><br><span class="line">newpRoot->_pLeft = CopyBinTree(pRoot->_pLeft);</span><br><span class="line">newpRoot->_pRight = CopyBinTree(pRoot->_pRight);</span><br><span class="line"><span class="keyword">return</span> newpRoot;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="删除二叉树"><a href="#删除二叉树" class="headerlink" title="删除二叉树"></a>删除二叉树</h2><p>C 语言递归实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DestroyBinTree</span><span class="params">(BTNode** pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (*pRoot)</span><br><span class="line">{</span><br><span class="line"><span class="comment">//递归删除左子树</span></span><br><span class="line">DestroyBinTree(&((*pRoot)->_pLeft));</span><br><span class="line"><span class="comment">//递归删除右子树</span></span><br><span class="line">DestroyBinTree(&((*pRoot)->_pRight));</span><br><span class="line"><span class="built_in">free</span>(*pRoot);</span><br><span class="line">*pRoot = <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="获取二叉树全部的节点个数"><a href="#获取二叉树全部的节点个数" class="headerlink" title="获取二叉树全部的节点个数"></a>获取二叉树全部的节点个数</h2><p>C 语言递归实现</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">int GetBinTreeSize(BTNode* pRoot)</span><br><span class="line">{</span><br><span class="line">if (pRoot)</span><br><span class="line">return GetBinTreeSize(pRoot->_pLeft) + GetBinTreeSize(pRoot->_pRight) + 1;</span><br><span class="line">else</span><br><span class="line">return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="获取二叉树中叶子节点个数"><a href="#获取二叉树中叶子节点个数" class="headerlink" title="获取二叉树中叶子节点个数"></a>获取二叉树中叶子节点个数</h2><p>C 语言递归实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">GetLeafCount</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (pRoot->_pLeft == <span class="literal">NULL</span> && pRoot->_pRight == <span class="literal">NULL</span>)</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><span class="line"><span class="keyword">return</span> GetLeafCount(pRoot->_pLeft) + GetLeafCount(pRoot->_pRight);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="获取二叉树深度-高度"><a href="#获取二叉树深度-高度" class="headerlink" title="获取二叉树深度(高度)"></a>获取二叉树深度(高度)</h2><p>C 语言递归实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">GetBinTreeHeight</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> height = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span> (pRoot->_pLeft || pRoot->_pRight)</span><br><span class="line">{</span><br><span class="line">height++;</span><br><span class="line"><span class="keyword">if</span> (pRoot->_pLeft)</span><br><span class="line">pRoot = pRoot->_pLeft;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(pRoot->_pRight)</span><br><span class="line">pRoot = pRoot->_pRight;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> height;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="在二叉树中搜索值为-X-的节点"><a href="#在二叉树中搜索值为-X-的节点" class="headerlink" title="在二叉树中搜索值为 X 的节点"></a>在二叉树中搜索值为 X 的节点</h2><p>C 语言递归实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">BTNode* <span class="title">BinaryTreeFind</span><span class="params">(BTNode* root, BTDataType x)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (root == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(root->_data == x)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> root;</span><br><span class="line">}</span><br><span class="line">BTNode* tmp = <span class="literal">NULL</span>;</span><br><span class="line"><span class="comment">//递归从左子树往下找</span></span><br><span class="line"><span class="keyword">if</span> (tmp = BinaryTreeFind(root->_pLeft, x))</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> tmp;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//左子树从右子树找,右子树找到直接返回</span></span><br><span class="line"><span class="keyword">return</span> BinaryTreeFind(root->_pRight, x);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="镜像翻转二叉树"><a href="#镜像翻转二叉树" class="headerlink" title="镜像翻转二叉树"></a>镜像翻转二叉树</h2><p>C 语言递归实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">BinaryTreeSwap</span><span class="params">(BTNode** pleft, BTNode ** pright)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">BTNode * tmpNode = *pleft;</span><br><span class="line">*pleft = *pright;</span><br><span class="line">*pright = tmpNode;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Mirror</span><span class="params">(BTNode* pRoot)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (pRoot == <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">}</span><br><span class="line">BinaryTreeSwap(pRoot->_pLeft, pRoot->_pRight);</span><br><span class="line">Mirror(pRoot->_pLeft);</span><br><span class="line">Mirror(pRoot->_pRight);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="学习并实现树的相关结构"><a href="#学习并实现树的相关结构" class="headerlink" title="学习并实现树的相关结构"></a>学习并实现树的相关结构</h1><h2 id="线索二叉树"><a href="#线索二叉树" class="headerlink" title="线索二叉树"></a>线索二叉树</h2><p><strong>Reference:<a href="http://data.biancheng.net/view/28.html" target="_blank" rel="noopener">线索二叉树的创建及遍历(C 语言实现)</a></strong><br>在使用二叉链表的普通二叉树中,对于 n 个结点的二叉树,在二叉链存储结构中有 n+1 个空链域。这些空链域放着不用,是对空间的浪费。<br>如果算法中多次涉及到对二叉树的遍历,普通的二叉树就需要使用栈结构做重复性的操作。<br>因此,如果使用二叉树中空闲的内存空间记录某些结点的前趋和后继元素的位置(不是全部)。这样在算法后期需要遍历二叉树时,就可以利用保存的结点信息,提高了遍历的效率。<br>使用这种方法构建的二叉树,即为“线索二叉树”。</p><p>线索二叉树中,如果结点有左子树,则 lchild 指针域指向左孩子,否则 lchild 指针域指向该结点的直接前趋;同样,如果结点有右子树,则 rchild 指针域指向右孩子,否则 rchild 指针域指向该结点的直接后继。<br>为了避免指针域指向的结点的意义混淆,需要改变结点本身的结构,增加两个标志域,如下图所示<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/2-1FS0145G0526.png" alt="线索二叉树中的结点结构"></p><p>其中,LTag 和 RTag 为标志域。实际上就是两个布尔类型的变量:</p><ul><li>LTag 值为 0 时,表示 lchild 指针域指向的是该结点的左孩子;为 1 时,表示指向的是该结点的直接前趋结点;</li><li>RTag 值为 0 时,表示 rchild 指针域指向的是该结点的右孩子;为 1 时,表示指向的是该结点的直接后继结点。<br>结点结构代码实现(C):</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TElemType int<span class="comment">//宏定义,结点中数据域的类型</span></span></span><br><span class="line"><span class="comment">//枚举,Link为0,Thread为1</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> PointerTag{</span><br><span class="line"> Link,</span><br><span class="line"> Thread</span><br><span class="line">}PointerTag;</span><br><span class="line"><span class="comment">//结点结构构造</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">BiThrNode</span>{</span></span><br><span class="line"> TElemType data;<span class="comment">//数据域</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">BiThrNode</span>* <span class="title">lchild</span>,*<span class="title">rchild</span>;</span><span class="comment">//左孩子,右孩子指针域</span></span><br><span class="line"></span><br><span class="line"> PointerTag Ltag,Rtag;<span class="comment">//标志域,枚举类型</span></span><br><span class="line">}BiThrNode,*BiThrTree;</span><br></pre></td></tr></table></figure><p>表示二叉树时,像以上结点结构构成的二叉链表,被称为线索链表;构建的二叉树称为线索二叉树。</p><h3 id="将普通二叉树转化为线索二叉树"><a href="#将普通二叉树转化为线索二叉树" class="headerlink" title="将普通二叉树转化为线索二叉树"></a>将普通二叉树转化为线索二叉树</h3><p>大致思路:在遍历过程中,如果当前结点没有左孩子,需要将该结点的 lchild 指针指向遍历过程中的前一个结点,所以在遍历过程中,设置一个指针(名为 pre ),时刻指向当前访问结点的前一个结点。rchlid 也同样处理<br>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//中序对二叉树进行线索化</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InThreading</span><span class="params">(BiThrTree p)</span></span>{</span><br><span class="line"> <span class="comment">//如果当前结点存在</span></span><br><span class="line"> <span class="keyword">if</span> (p) {</span><br><span class="line"> InThreading(p->lchild);<span class="comment">//递归当前结点的左子树,进行线索化</span></span><br><span class="line"> <span class="comment">//如果当前结点没有左孩子,左标志位设为1,左指针域指向上一结点 pre</span></span><br><span class="line"> <span class="keyword">if</span> (!p->lchild) {</span><br><span class="line"> p->Ltag=Thread;</span><br><span class="line"> p->lchild=pre;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果 pre 没有右孩子,右标志位设为 1,右指针域指向当前结点。</span></span><br><span class="line"> <span class="keyword">if</span> (!pre->rchild) {</span><br><span class="line"> pre->Rtag=Thread;</span><br><span class="line"> pre->rchild=p;</span><br><span class="line"> }</span><br><span class="line"> pre=p;<span class="comment">//线索化完左子树后,让pre指针指向当前结点</span></span><br><span class="line"> InThreading(p->rchild);<span class="comment">//递归右子树进行线索化</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="线索二叉树的遍历"><a href="#线索二叉树的遍历" class="headerlink" title="线索二叉树的遍历"></a>线索二叉树的遍历</h3><p>下图是一个按照中序遍历建立的线索二叉树。其中,实线表示指针,指向的是左孩子或者右孩子。虚线表示线索,指向的是该结点的直接前趋或者直接后继。</p><p>使用线索二叉树时,会经常遇到一个问题,如图 3 中,结点 b 的直接后继直接通过指针域获得,为结点 _ ;而由于结点 _ 的度为 2 ,无法利用指针域指向后继结点,整个链表断掉了。当在遍历过程,遇到这种问题是解决的办法就是:寻找先序、中序、后序遍历的规律,找到下一个结点。</p><p>在先序遍历过程中,如果结点因为有右孩子导致无法找到其后继结点,如果结点有左孩子,则后继结点是其左孩子;否则,就一定是右孩子。拿图 3 举例,结点 + 的后继结点是其左孩子结点 a ,如果结点 a 不存在的话,就是结点 * 。</p><p>在中序遍历过程中,结点的后继是遍历其右子树时访问的第一个结点,也就是右子树中位于最左下的结点。例如图 3 中结点 * ,后继结点为结点 c ,是其右子树中位于最左边的结点。反之,结点的前趋是左子树最后访问的那个结点。</p><p>后序遍历中找后继结点需要分为 3 种情况:<br>如果该结点是二叉树的根,后继结点为空;<br>如果该结点是父结点的右孩子(或者是左孩子,但是父结点没有右孩子),后继结点是父结点;<br>如果该结点是父结点的左孩子,且父结点有右子树,后继结点为父结点的右子树在后序遍历列出的第一个结点。<br>使用后序遍历建立的线索二叉树,在真正使用过程中遇到链表的断点时,需要访问父结点,所以在初步建立二叉树时,宜采用三叉链表做存储结构。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E7%BA%BF%E7%B4%A2%E4%BA%8C%E5%8F%89%E6%A0%91.png" alt="线索二叉树"></p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//中序遍历线索二叉树</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InOrderThraverse_Thr</span><span class="params">(BiThrTree p)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">while</span>(p)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//一直找左孩子,最后一个为中序序列中排第一的</span></span><br><span class="line"> <span class="keyword">while</span>(p->Ltag == Link){</span><br><span class="line"> p = p->lchild;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%c "</span>, p->data); <span class="comment">//操作结点数据</span></span><br><span class="line"> <span class="comment">//当结点右标志位为1时,直接找到其后继结点</span></span><br><span class="line"> <span class="keyword">while</span>(p->Rtag == Thread && p->rchild !=<span class="literal">NULL</span>){</span><br><span class="line"> p = p->rchild;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%c "</span>, p->data);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//否则,按照中序遍历的规律,找其右子树中最左下的结点,也就是继续循环遍历</span></span><br><span class="line"> p = p->rchild;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="平衡二叉树(AVL-Tree)"><a href="#平衡二叉树(AVL-Tree)" class="headerlink" title="平衡二叉树(AVL Tree)"></a>平衡二叉树(AVL Tree)</h2><h3 id="平衡二叉树的定义"><a href="#平衡二叉树的定义" class="headerlink" title="平衡二叉树的定义"></a>平衡二叉树的定义</h3><p>平衡二叉树指的是要么它本身是一个空树,要么它是一个左子树和右子树的深度之差的绝对值不大于 1,并且保证左右子树都是平衡树,下图就是一个平衡二叉树。从图中我们可以看出,一个结点的高度位 1 则表明为其叶子结点到父结点的高度,整颗树的高度取决于最深叶子结点到根结点的距离。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91.png" alt="平衡二叉树"><br>由于 AVL 树操作中有许多的操作需要向上进行,所以数据结构应这样设计:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">treeNode</span>{</span></span><br><span class="line"><span class="keyword">char</span> key;</span><br><span class="line"><span class="keyword">int</span> val;</span><br><span class="line"><span class="comment">//平衡因子,-1,0,1为合理值</span></span><br><span class="line"><span class="keyword">int</span> balanceFactor;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">treeNode</span> *<span class="title">left</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">treeNode</span> *<span class="title">right</span>;</span></span><br><span class="line"> <span class="comment">//父节点,方便向上的操作进行</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">treeNode</span> *<span class="title">Parent</span>;</span></span><br><span class="line">}AVLTree,*PAVLTree;</span><br></pre></td></tr></table></figure><h3 id="平衡二叉树的操作"><a href="#平衡二叉树的操作" class="headerlink" title="平衡二叉树的操作"></a>平衡二叉树的操作</h3><p>AVL 树的查找操作和普通的二叉树的查找基本一致,但是插入和删除操作有所不同,因为插入和删除会减少树的结点并且改变树的结构,这个时候为了使树始终保持平衡状态我们需要对树进行重构使其始终保持平衡状态,一般这个操作叫做旋转操作(rotation),旋转分为左旋转和右旋转等。我们需要使用旋转操作使得在插入和删除操作之后使二叉平衡树在插入和删除某结点之后依然保持平衡。</p><h4 id="旋转操作"><a href="#旋转操作" class="headerlink" title="旋转操作"></a>旋转操作</h4><p>旋转的操作主要有左旋转和右旋转。旋转操作的基本原理都一样,最终目的就是为了让二叉平衡树在被操作之后再次达到平衡。</p><h5 id="左旋转"><a href="#左旋转" class="headerlink" title="左旋转"></a>左旋转</h5><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%B7%A6%E6%97%8B%E8%BD%AC.png" alt="左旋转"><br>在上图所表示的左旋转操作中,我们假设的是 x < y < z,因为树不平衡了,我们执行左旋转,将 x 及其左子树进行左旋转,并且将原本 y 的左子树变为 x 的右子树,这里需要注意的两点,</p><ul><li>① 就是我们需要寻找到三个点,这三个点的大小是有排序的,如这此段开头所说道的 xyz 的关系,将中间那个值作为新的中心结点,然后再进行旋转操作。</li><li>② 就是一定要确保所有的左右子树遵循二叉树的定义要求,既左子树一定要永远都是小于其父结点的,而右子树始终大于父结点的。<br>C 语言实现:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="function">PAVLTree <span class="title">leftRotate</span><span class="params">(PAVLTree Node)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">PAVLTree Temp = Node->right;</span><br><span class="line">Node->right = Temp->left;</span><br><span class="line">Temp->left = Node;</span><br><span class="line">Temp->Parent = Node->Parent;</span><br><span class="line"><span class="keyword">if</span>(Temp->left)</span><br><span class="line">Temp->left->Parent = Temp;</span><br><span class="line"><span class="keyword">if</span>(Temp->right)</span><br><span class="line">Temp->right->Parent = Temp;</span><br><span class="line"><span class="keyword">if</span>(Node->left)</span><br><span class="line">Node->left->Parent = Node;</span><br><span class="line"><span class="keyword">if</span>(Node->right)</span><br><span class="line">Node->right->Parent = Node;</span><br><span class="line"><span class="keyword">return</span> Temp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="右旋转"><a href="#右旋转" class="headerlink" title="右旋转"></a>右旋转</h5><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%8F%B3%E6%97%8B%E8%BD%AC.png" alt="右旋转"><br>上图所述的三个节点的关系为 z < x < y,因此根据左旋转所描述的我们可以知道 x 应该作为中心结点也就是父结点,然后这里需要进行两次旋转才能使二叉树最终处于平衡,首先是先对 z 进行左旋转,将 z 变为 x 的左子树,然后再对 y 进行右旋转,在这个过程中,x 的左子树变为 z 的右子树,而右子树则成为了 y 的左子树。<br>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">PAVLTree <span class="title">rightRotate</span><span class="params">(PAVLTree Node)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">PAVLTree Temp = Node->left;</span><br><span class="line">Node->left = Temp->right;</span><br><span class="line">Temp->right = Node;</span><br><span class="line">Temp->Parent = Node->Parent;</span><br><span class="line"><span class="keyword">if</span>(Temp->left)</span><br><span class="line">Temp->left->Parent = Temp;</span><br><span class="line"><span class="keyword">if</span>(Temp->right)</span><br><span class="line">Temp->right->Parent = Temp;</span><br><span class="line"><span class="keyword">if</span>(Node->left)</span><br><span class="line">Node->left->Parent = Node;</span><br><span class="line"><span class="keyword">if</span>(Node->right)</span><br><span class="line">Node->right->Parent = Node;</span><br><span class="line"><span class="keyword">return</span> Temp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="自旋转"><a href="#自旋转" class="headerlink" title="自旋转"></a>自旋转</h5><p>在实现了左旋和右旋操作之后,我们就可以编写一个自平衡函数,来在进行插入和删除操作后自动使二叉树重新达到平衡。<br>在编写自旋转函数之前,我们需要明白我们在插入或者删除的时候可能导致的树的不平衡的所有情况。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%9B%9B%E7%A7%8D%E5%8F%AF%E8%83%BD%E6%83%85%E5%86%B5.png" alt="四种可能情况"><br>情况 1 和 4 是最简单的,我们只需要对结点 Z 进行一个左右的旋转即可,旋转和红黑树的旋转一样。目的是降低子树的高度差。<br>情况 2 和情况 3 需要先对 y 进行左或者右旋转,变成情况 1 和 4,然后再照着情况 1,和 4 处理。<br>其实每次插入删除的时候引起的不平衡的结点都是插入、删除的结点的父节点,不肯是其父节点的兄弟结点之类,而且我们总是通过不平衡结点加上往下的两个结点,来判断出事情况几。往下的结点怎么选取呢?<br>选取的规定就是,看刚插入或者删除的结点是在不平衡结点的左子树还是右子树,加入在左子树,那么继续判断刚插入或者删除的结点在该左子树的左子树还是右子树,只需要这么两步就可以判断出情况。代码如下:</p><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//树的权值搜索函数</span></span><br><span class="line"></span><br><span class="line"><span class="function">PAVLTree <span class="title">searchKey</span><span class="params">(PAVLTree t,ElemType key)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> PAVLTree p=t;</span><br><span class="line"> <span class="keyword">while</span>(p)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(p->key==key)</span><br><span class="line"> <span class="keyword">return</span> p;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(p->key<key)</span><br><span class="line"> p=p->rchild;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> p=p->lchild;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> p;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//树的自平衡函数,传入的参数是新插入的节点</span></span><br><span class="line"><span class="function">PAVLTree <span class="title">AVLTreeBalance</span><span class="params">(PAVLTree *Node)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span>(!(*Node))</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">PAVLTree *BFNode;</span><br><span class="line">PAVLTree Temp = *Node;</span><br><span class="line">PAVLTree Parent;</span><br><span class="line"><span class="comment">//0--left,1--right</span></span><br><span class="line"><span class="keyword">int</span> LeftOrRight = <span class="number">-1</span>;</span><br><span class="line">PAVLTree find = (*Node);</span><br><span class="line">BFNode = Node;</span><br><span class="line"> <span class="comment">//查找到不平衡的结点</span></span><br><span class="line"><span class="keyword">while</span>(find)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(find->balanceFactor == <span class="number">-2</span> || find->balanceFactor == <span class="number">2</span>)</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">find = find->Parent;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//当前树平衡</span></span><br><span class="line"><span class="keyword">if</span>(!find)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">while</span>(Temp->Parent)</span><br><span class="line">Temp = Temp->Parent;</span><br><span class="line"><span class="keyword">return</span> Temp;</span><br><span class="line">}</span><br><span class="line">(*BFNode) = find;</span><br><span class="line"><span class="comment">//如果存在父节点</span></span><br><span class="line"><span class="keyword">if</span>((*BFNode)->Parent)</span><br><span class="line">{</span><br><span class="line">Parent = (*BFNode)->Parent;</span><br><span class="line"><span class="keyword">if</span>(Parent->left == (*BFNode))</span><br><span class="line">LeftOrRight = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">LeftOrRight = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">Parent = <span class="literal">NULL</span>;</span><br><span class="line"><span class="comment">//辅助判断是哪种情况的变量</span></span><br><span class="line"><span class="keyword">int</span> Second,Third;</span><br><span class="line">Second = searchKey((*BFNode),Temp->key,&find);</span><br><span class="line"><span class="keyword">if</span>(Second == <span class="number">0</span>)</span><br><span class="line">Third = searchKey((*BFNode)->left,Temp->key,&find);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">Third = searchKey((*BFNode)->right,Temp->key,&find);</span><br><span class="line"><span class="comment">//情况一,左左左</span></span><br><span class="line"><span class="keyword">if</span>(Second == <span class="number">0</span> && Third == <span class="number">0</span>)</span><br><span class="line">(*BFNode) = rightRotate((*BFNode));</span><br><span class="line"><span class="comment">//情况二,右右右</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(Second == <span class="number">1</span> && Third == <span class="number">1</span>)</span><br><span class="line">(*BFNode) = leftRotate((*BFNode));</span><br><span class="line"><span class="comment">//情况三,左左右</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(Second == <span class="number">0</span> && Third == <span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line">(*BFNode)->left = leftRotate((*BFNode)->left);</span><br><span class="line">(*BFNode) = rightRotate((*BFNode));</span><br><span class="line">}</span><br><span class="line"><span class="comment">//情况四,右右左</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">(*BFNode)->right = rightRotate((*BFNode)->right);</span><br><span class="line">(*BFNode) = leftRotate((*BFNode));</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(LeftOrRight != <span class="number">-1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(LeftOrRight == <span class="number">0</span>)</span><br><span class="line">Parent->left = (*BFNode);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">Parent->right = (*BFNode);</span><br><span class="line">}</span><br><span class="line"> <span class="comment">//返回根节点</span></span><br><span class="line"><span class="keyword">while</span>(Temp->Parent)</span><br><span class="line">Temp = Temp->Parent;</span><br><span class="line"><span class="keyword">return</span> Temp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="一些辅助函数"><a href="#一些辅助函数" class="headerlink" title="一些辅助函数"></a>一些辅助函数</h4><p>其它的一些辅助函数与普通的二叉树没什么不同。</p><h5 id="获得-AVL-树的深度"><a href="#获得-AVL-树的深度" class="headerlink" title="获得 AVL 树的深度"></a>获得 AVL 树的深度</h5><p>C 语言实现:</p><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="comment">//获得树的深度</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">GetDeepth</span><span class="params">(PAVLTree Node)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span>(!Node)</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> left = GetDeepth(Node->left);</span><br><span class="line"><span class="keyword">int</span> right = GetDeepth(Node->right);</span><br><span class="line"><span class="keyword">return</span> (left > right ? (left+<span class="number">1</span>) : (right+<span class="number">1</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="重新计算树的平衡因子"><a href="#重新计算树的平衡因子" class="headerlink" title="重新计算树的平衡因子"></a>重新计算树的平衡因子</h5><p>//重新计算树的平衡因子,但是只会回溯插入节点的沿途父节点,始终以左节点层数减去右节点层数<br>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">CalculateBF</span><span class="params">(PAVLTree Node)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="comment">//根节点的父节点就终止</span></span><br><span class="line"><span class="keyword">if</span>(!Node)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">Node->balanceFactor = GetDeepth(Node->left)-GetDeepth(Node->right);</span><br><span class="line">CalculateBF(Node->Parent);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="插入操作"><a href="#插入操作" class="headerlink" title="插入操作"></a>插入操作</h4><p>平衡二叉树的插入操作与普通二叉查找树的操作一样,新插入的节点都发生在叶子结点,唯一不同的就如上述所说,新插入的结点致使树的结构发生改变而导致不平衡,此时需要进行旋转以达到平衡。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91.png" alt="平衡二叉树.png"><br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%8F%92%E5%85%A5%E6%96%B0%E7%9A%84%E8%8A%82%E7%82%B9Key(40).png" alt="插入新的节点Key(40)"><br>这时我们会发现此时的二叉树已经不平衡,这时我们需要寻找到树里面导致树不平衡的三个点,进行相应的操作,具体有以下两步:</p><ul><li>① 先对结点 39 以结点 42 为父结点进行左旋转,此时节点 40 变成了 39 的右结点,而 33,39,40 一起成为了结点 42 的左子树。</li><li>② 对结点 53 进行右旋转,将其变成节点 42 的右子树,结点 55 依然为结点 53 的右子树。由此便完成了整棵树的重构并让新的树保持平衡。重构之后的树如下图所示:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E9%87%8D%E6%9E%84%E4%B9%8B%E5%90%8E%E5%BE%97%E5%88%B0%E7%9A%84%E6%8F%92%E5%85%A5%E6%A0%91.png" alt="重构之后得到的插入树.png"><br>有了上边的自平衡函数,那么 AVL 树的插入和删除操作就会变得非常简单。<br>C 语言实现:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//插入操作</span></span><br><span class="line"><span class="function">PAVLTree <span class="title">insertKey</span><span class="params">(PAVLTree *root,<span class="keyword">char</span> key,<span class="keyword">int</span> val)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="comment">//为空,表示应该插入的位置</span></span><br><span class="line"><span class="keyword">if</span>(!(*root))</span><br><span class="line">{</span><br><span class="line">(*root) = (PAVLTree)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(AVLTree));</span><br><span class="line">(*root)->key = key;</span><br><span class="line"><span class="comment">//(*root)->Parent = (*root);</span></span><br><span class="line">(*root)->val = val;</span><br><span class="line">(*root)->left = (*root)->right = <span class="literal">NULL</span>;</span><br><span class="line"><span class="comment">//Balance factor and primary value is 0</span></span><br><span class="line">(*root)->balanceFactor = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">return</span> (*root);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//如果已经存在那么修正值就行:</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>((*root)->key == key)</span><br><span class="line">{</span><br><span class="line">(*root)->key = key;</span><br><span class="line"><span class="keyword">return</span> (*root);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//在左子树</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>((*root)->key > key)</span><br><span class="line">insertKey(&(*root)->left,key,val)->Parent = (*root);</span><br><span class="line"><span class="comment">//在右子树</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>((*root)->key < key)</span><br><span class="line">insertKey(&(*root)->right,key,val)->Parent = (*root);</span><br><span class="line">CalculateBF(*root);</span><br><span class="line"><span class="keyword">return</span> (*root);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="删除操作"><a href="#删除操作" class="headerlink" title="删除操作"></a>删除操作</h4><p>假设在插入的基础上删除结点 22,那么此时我们从节点 19 开始遍历找到第一个导致不平衡的结点为 25 并且具有最大高度值的结点,之后往右子树进行便利寻找到第二个具有最大高度值的结点,此结点为 42(下图标注了红色边框的结点)。<br>过程如下图所示:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%88%A0%E9%99%A4%E4%B9%8B%E5%90%8E%E8%BF%9B%E8%A1%8C%E9%87%8D%E6%9E%84%E6%B5%81%E7%A8%8B%E5%9B%BE.png" alt="删除之后进行重构流程图.png"><br>那么删除的策略就是:</p><ul><li>1.叶子结点那么直接删除。</li><li>2.不是叶子结点,但是只有一个儿子结点,那么删除该节点,用其儿子结点顶替</li><li>3.不是叶子结点,但是有两个儿子结点,那么查找该树的中序遍历情况下,要删除节点的下一个结点来顶替,(其实就是右子树的最小值结点)<br>C 语言实现:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//删除函数</span></span><br><span class="line"><span class="function">PAVLTree <span class="title">deleteKey</span><span class="params">(PAVLTree *root,<span class="keyword">char</span> key)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">PAVLTree DeleteNode = (PAVLTree)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(AVLTree));</span><br><span class="line"><span class="comment">//查找到,那么删除</span></span><br><span class="line"><span class="keyword">if</span>((*root)->key == key)</span><br><span class="line">{</span><br><span class="line"><span class="comment">//为了调试少写一点</span></span><br><span class="line">DeleteNode->key = (*root)->key;</span><br><span class="line">DeleteNode->val = (*root)->val;</span><br><span class="line"><span class="comment">//叶子节点,直接删除</span></span><br><span class="line"><span class="keyword">if</span>(!(*root)->left && !(*root)->right)</span><br><span class="line">(*root) = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(!(*root)->left)</span><br><span class="line">(*root) = (*root)->right;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(!(*root)->right)</span><br><span class="line">(*root) = (*root)->left;</span><br><span class="line"><span class="comment">//最复杂的情况需要寻找中序遍历的下一个点来顶替(右子树的最小结点)</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">PAVLTree Temp = deleteMin(&(*root)->right);</span><br><span class="line">(*root)->key = Temp->key;</span><br><span class="line">(*root)->val = Temp->val;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>((*root)->key > key)</span><br><span class="line">DeleteNode = deleteKey(&(*root)->left,key);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">DeleteNode = deleteKey(&(*root)->right,key);</span><br><span class="line">CalculateBF(*root);</span><br><span class="line">AVLTreeBalance(root);</span><br><span class="line"><span class="keyword">return</span> DeleteNode;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>Reference</strong>:<a href="https://blog.csdn.net/c_living/article/details/81169364" target="_blank" rel="noopener">AVL(自平衡二叉树)树的实现(C 语言)</a></p><h2 id="哈夫曼树"><a href="#哈夫曼树" class="headerlink" title="哈夫曼树"></a>哈夫曼树</h2><p>赫夫曼树,别名“哈夫曼树”、“最优树”以及“最优二叉树”。学习哈夫曼树之前,首先要了解几个名词。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91.png" alt="哈夫曼树"></p><h3 id="哈夫曼树相关的几个名词"><a href="#哈夫曼树相关的几个名词" class="headerlink" title="哈夫曼树相关的几个名词"></a>哈夫曼树相关的几个名词</h3><ul><li>路径:<br>在一棵树中,一个结点到另一个结点之间的通路,称为路径。上图中,从根结点到结点 a 之间的通路就是一条路径。</li><li>路径长度:<br>在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为 1 层,那么从根结点到第 i 层结点的路径长度为 i - 1 。上图中从根结点到结点 c 的路径长度为 3。</li><li>节点的权:<br>给每一个结点赋予一个新的数值,被称为这个结点的权。例如,上图中结点 a 的权为 7,结点 b 的权为 5。</li><li>结点的带权路径长度:<br>指的是从根结点到该结点之间的路径长度与该结点的权的乘积。例如,上图中结点 b 的带权路径长度为 2 _ 5 = 10 。<br>树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL” 。例如上图中所示的这颗树的带权路径长度为:WPL = 7 _ 1 + 5 _ 2 + 2 _ 3 + 4 * 3</li></ul><h3 id="哈夫曼树的定义"><a href="#哈夫曼树的定义" class="headerlink" title="哈夫曼树的定义"></a>哈夫曼树的定义</h3><p><strong>当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。</strong></p><p>在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。在上图中,因为结点 a 的权值最大,所以理应直接作为根结点的孩子结点。</p><h3 id="构建哈夫曼树的过程"><a href="#构建哈夫曼树的过程" class="headerlink" title="构建哈夫曼树的过程"></a>构建哈夫曼树的过程</h3><p>对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:<br>在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;<br>在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;<br>重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91%E7%9A%84%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B.png" alt="哈夫曼树的构建过程"><br>上图中,(A)给定了四个结点 a,b,c,d,权值分别为 7,5,2,4;第一步如(B)所示,找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入;进入(C),重复之前的步骤。直到(D)中,所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。</p><h3 id="哈夫曼树中的节点结构"><a href="#哈夫曼树中的节点结构" class="headerlink" title="哈夫曼树中的节点结构"></a>哈夫曼树中的节点结构</h3><p>构建哈夫曼树时,首先需要确定树中结点的构成。由于哈夫曼树的构建是从叶子结点开始,不断地构建新的父结点,直至树根,所以结点中应包含指向父结点的指针。但是在使用哈夫曼树时是从树根开始,根据需求遍历树中的结点,因此每个结点需要有指向其左孩子和右孩子的指针。</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//哈夫曼树结点结构</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="keyword">int</span> weight;<span class="comment">//结点权重</span></span><br><span class="line"> <span class="keyword">int</span> parent, left, right;<span class="comment">//父结点、左孩子、右孩子在数组中的位置下标</span></span><br><span class="line">}HTNode, *HuffmanTree;</span><br></pre></td></tr></table></figure><h3 id="构建哈夫曼树的算法实现"><a href="#构建哈夫曼树的算法实现" class="headerlink" title="构建哈夫曼树的算法实现"></a>构建哈夫曼树的算法实现</h3><p>构建哈夫曼树时,需要每次根据各个结点的权重值,筛选出其中值最小的两个结点,然后构建二叉树。</p><p>查找权重值最小的两个结点的思想是:从树组起始位置开始,首先找到两个无父结点的结点(说明还未使用其构建成树),然后和后续无父结点的结点依次做比较,有两种情况需要考虑:</p><ul><li>如果比两个结点中较小的那个还小,就保留这个结点,删除原来较大的结点;</li><li>如果介于两个结点权重值之间,替换原来较大的结点;<br>C 语言的实现:</li></ul><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Select</span><span class="params">(HuffmanTree HT, <span class="keyword">int</span> end, <span class="keyword">int</span> *s1, <span class="keyword">int</span> *s2)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> min1, min2;</span><br><span class="line"> <span class="comment">//遍历数组初始下标为 1</span></span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//找到还没构建树的结点</span></span><br><span class="line"> <span class="keyword">while</span>(HT[i].parent != <span class="number">0</span> && i <= end){</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> min1 = HT[i].weight;</span><br><span class="line"> *s1 = i;</span><br><span class="line"></span><br><span class="line"> i++;</span><br><span class="line"> <span class="keyword">while</span>(HT[i].parent != <span class="number">0</span> && i <= end){</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//对找到的两个结点比较大小,min2为大的,min1为小的</span></span><br><span class="line"> <span class="keyword">if</span>(HT[i].weight < min1){</span><br><span class="line"> min2 = min1;</span><br><span class="line"> *s2 = *s1;</span><br><span class="line"> min1 = HT[i].weight;</span><br><span class="line"> *s1 = i;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> min2 = HT[i].weight;</span><br><span class="line"> *s2 = i;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//两个结点和后续的所有未构建成树的结点做比较</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j=i+<span class="number">1</span>; j <= end; j++)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//如果有父结点,直接跳过,进行下一个</span></span><br><span class="line"> <span class="keyword">if</span>(HT[j].parent != <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果比最小的还小,将min2=min1,min1赋值新的结点的下标</span></span><br><span class="line"> <span class="keyword">if</span>(HT[j].weight < min1){</span><br><span class="line"> min2 = min1;</span><br><span class="line"> min1 = HT[j].weight;</span><br><span class="line"> *s2 = *s1;</span><br><span class="line"> *s1 = j;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果介于两者之间,min2赋值为新的结点的位置下标</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(HT[j].weight >= min1 && HT[j].weight < min2){</span><br><span class="line"> min2 = HT[j].weight;</span><br><span class="line"> *s2 = j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>s1 和 s2 传入的是实参的地址,所以函数运行完成后,实参中存放的自然就是哈夫曼树中权重值最小的两个结点在数组中的位置。<br>构建哈弗曼树的代码实现如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">CreateHuffmanTree</span><span class="params">(HuffmanTree *HT, <span class="keyword">int</span> *w, <span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span>(n<=<span class="number">1</span>) <span class="keyword">return</span>; <span class="comment">// 如果只有一个编码就相当于0</span></span><br><span class="line"> <span class="keyword">int</span> m = <span class="number">2</span>*n<span class="number">-1</span>; <span class="comment">// 哈夫曼树总节点数,n就是叶子结点</span></span><br><span class="line"> *HT = (HuffmanTree) <span class="built_in">malloc</span>((m+<span class="number">1</span>) * <span class="keyword">sizeof</span>(HTNode)); <span class="comment">// 0号位置不用</span></span><br><span class="line"> HuffmanTree p = *HT;</span><br><span class="line"> <span class="comment">// 初始化哈夫曼树中的所有结点</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> {</span><br><span class="line"> (p+i)->weight = *(w+i<span class="number">-1</span>);</span><br><span class="line"> (p+i)->parent = <span class="number">0</span>;</span><br><span class="line"> (p+i)->left = <span class="number">0</span>;</span><br><span class="line"> (p+i)->right = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = n+<span class="number">1</span>; i <= m; i++)</span><br><span class="line"> {</span><br><span class="line"> (p+i)->weight = <span class="number">0</span>;</span><br><span class="line"> (p+i)->parent = <span class="number">0</span>;</span><br><span class="line"> (p+i)->left = <span class="number">0</span>;</span><br><span class="line"> (p+i)->right = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//构建哈夫曼树</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = n+<span class="number">1</span>; i <= m; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> s1, s2;</span><br><span class="line"> Select(*HT, i<span class="number">-1</span>, &s1, &s2);</span><br><span class="line"> (*HT)[s1].parent = (*HT)[s2].parent = i;</span><br><span class="line"> (*HT)[i].left = s1;</span><br><span class="line"> (*HT)[i].right = s2;</span><br><span class="line"> (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意,如果使用此程序,对权重值分别为 2、8、7、6、5 的节点构建哈夫曼树,最终效果如图 4(A) 所示。但其实,图 4(B) 中显示的哈夫曼树也满足条件,这两棵树的带权路径长度相同。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E4%B8%A4%E7%A7%8D%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91.png" alt="图 4 两种哈夫曼树"><br>之所以使用此程序构建的哈夫曼树,是图 4(A) 而不是 4(B),是因为在构建哈夫曼树时,结点 2 和结点 5 构建的新的结点 7 存储在动态树组中位置,比权重值为 7 节点的存储位置还靠后,所以,在程序继续选择两个权值最小的结点时,直接选择了的叶子结点 6 和 7 。<br>Reference:<a href="http://c.biancheng.net/view/3398.html" target="_blank" rel="noopener">哈夫曼树(赫夫曼树、最优树)详解</a></p><h2 id="字典树"><a href="#字典树" class="headerlink" title="字典树"></a>字典树</h2><h3 id="字典树的定义"><a href="#字典树的定义" class="headerlink" title="字典树的定义"></a>字典树的定义</h3><p>字典树,顾名思义,就是用树的结构去存储单词。比如,我要存储单词 ant 和 apple,就可以采取下图的多叉树结构去实现,其中可以看到,他们公用 A 节点,看是上去似乎节省了空间(实际上并没有,下面会解释),和形成了有序的分组。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%AD%97%E5%85%B8%E6%A0%91%E5%9B%BE%E8%A7%A3.jpg" alt="字典树图解"><br>假设我们采用的是传统的方法,也就是用一个数组,把单词存进去。那么我们可以比较以下这两个方法的时间复杂度<br>假设每个单词平均有 M 个字母,一共有 N 个单词。<br>|操作|传统算法|字典树|<br>|—|—|—|<br>|插入|O(N)|O(M)|<br>|查找|O(N)|O(M)|<br>|删除|O(N)|O(M)|<br>显然,当需要存储的数据量越庞大或者数据的操作越频繁,字典树的优势越明显,它的时间复杂度只与单词的长度有关。这个算法可以应用在词频分析,信息检索,字符串匹配等方面。</p><h3 id="字典树的实现"><a href="#字典树的实现" class="headerlink" title="字典树的实现"></a>字典树的实现</h3><p>C 语言实现</p><h4 id="定义结构体"><a href="#定义结构体" class="headerlink" title="定义结构体"></a>定义结构体</h4><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">node</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span>* <span class="title">children</span>[<span class="title">SUB_NODE_COUNT</span>];</span></span><br><span class="line"> <span class="keyword">int</span> flag;</span><br><span class="line"> <span class="keyword">char</span> character;</span><br><span class="line">} Node;</span><br></pre></td></tr></table></figure><p>树节点的数据包含三个部分</p><ul><li>所含的字母</li><li>指向下一节点的指针</li><li>是否能终结的标志位,也就是说是否能与它的祖先节点组成一个单词。</li></ul><h4 id="字典树的插入函数"><a href="#字典树的插入函数" class="headerlink" title="字典树的插入函数"></a>字典树的插入函数</h4><p>C 语言实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">Node* <span class="title">create_node</span><span class="params">(<span class="keyword">char</span> c, <span class="keyword">int</span> flag)</span> </span>{</span><br><span class="line"> Node* n = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Node));</span><br><span class="line"> n->character = c;</span><br><span class="line"> n->flag = flag;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> n->children[i] = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">append_node</span><span class="params">(Node* n, <span class="keyword">char</span> c)</span> </span>{</span><br><span class="line"></span><br><span class="line"> Node* child_ptr = n->children[c-<span class="string">'a'</span>];</span><br><span class="line"> <span class="keyword">if</span> (child_ptr) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> n->children[c-<span class="string">'a'</span>] = create_node(c, FALSE);</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">add_word</span><span class="params">(Node* root, <span class="keyword">char</span>* str)</span> </span>{</span><br><span class="line"> <span class="keyword">char</span> c = *str;</span><br><span class="line"> Node* ptr = root;</span><br><span class="line"> <span class="keyword">int</span> flag = TRUE;</span><br><span class="line"> <span class="keyword">while</span>(c != <span class="string">'\0'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!append_node(ptr, c)) {</span><br><span class="line"> flag = FALSE;</span><br><span class="line"> }</span><br><span class="line"> ptr = ptr->children[c-<span class="string">'a'</span>];</span><br><span class="line"> c = *(++str);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!ptr->flag) {</span><br><span class="line"> flag = FALSE;</span><br><span class="line"> ptr->flag = TRUE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> !flag;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个函数的功能,将单词插入树中,假如目标单词已经存在于树中,则返回<code>FALSE</code> , 否则就能成功插入,返回<code>TRUE</code>。插入的过程比较简单,只要单词字母对应的节点存在,则继续往下查询,否则就创建新节点,然后在最后的一个字母的节点把<code>flag</code>改为<code>FALSE</code>;</p><h4 id="字典树的查找函数"><a href="#字典树的查找函数" class="headerlink" title="字典树的查找函数"></a>字典树的查找函数</h4><p>C 语言实现</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">check</span><span class="params">(Node* root, <span class="keyword">char</span>* word)</span> </span>{</span><br><span class="line"> Node* ptr = root;</span><br><span class="line"> <span class="keyword">int</span> len = <span class="built_in">strlen</span>(word);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!ptr) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" isn't in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> ptr = ptr->children[word[i]-<span class="string">'a'</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (ptr && ptr->flag) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" is in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" isn't in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="字典树的遍历函数"><a href="#字典树的遍历函数" class="headerlink" title="字典树的遍历函数"></a>字典树的遍历函数</h4><p>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">traversal</span><span class="params">(Node* root, <span class="keyword">char</span>* str)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!root) {</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="keyword">int</span> len_of_str = <span class="built_in">strlen</span>(str);</span><br><span class="line"> <span class="keyword">char</span>* new_str = <span class="built_in">malloc</span>(len_of_str+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(new_str, str);</span><br><span class="line"> new_str[len_of_str] = root->character;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (root->flag) {</span><br><span class="line"> <span class="comment">//输出</span></span><br><span class="line"> <span class="keyword">char</span>* str_for_print = <span class="built_in">malloc</span>(len_of_str+<span class="number">2</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(str_for_print, new_str);</span><br><span class="line"> str_for_print[len_of_str+<span class="number">1</span>] = <span class="string">'\0'</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>, str_for_print);</span><br><span class="line"> <span class="built_in">free</span>(str_for_print);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> traversal(root->children[i], new_str);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">free</span>(new_str);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>很显然,这里用深度优先遍历比较合适,因为这是根据单词字母的组成来垂直扩展多叉树的。对于深度优先算法,递归是最能偷懒的方法啦!深度遍历的过程中,只要遇到<code>flag</code>等于<code>TRUE</code>,跟祖先节点构成单词输出,那就 OK 啦。</p><h4 id="字典树的删除函数"><a href="#字典树的删除函数" class="headerlink" title="字典树的删除函数"></a>字典树的删除函数</h4><p>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLeave</span><span class="params">(Node* root)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> <span class="keyword">if</span> (root->children[i]) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">delete_word</span><span class="params">(Node* root, <span class="keyword">char</span>* word)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> len = <span class="built_in">strlen</span>(word);</span><br><span class="line"> <span class="keyword">int</span> first_index = word[<span class="number">0</span>] - <span class="string">'a'</span>;</span><br><span class="line"> <span class="keyword">if</span> (!root->children[first_index]) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (len == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (root->children[first_index]->flag) {</span><br><span class="line"> <span class="keyword">if</span> (isLeave(root->children[first_index])) {</span><br><span class="line"> <span class="built_in">free</span>(root->children[first_index]);</span><br><span class="line"> root->children[first_index] = <span class="literal">NULL</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> root->children[first_index]->flag = FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> flag = delete_word(root->children[first_index], word+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (isLeave(root->children[first_index]) && !root->children[first_index]->flag) {</span><br><span class="line"> <span class="built_in">free</span>(root->children[first_index]);</span><br><span class="line"> root->children[first_index] = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> flag;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其实删除一个单词最直接的方法就是把最后一个字母的<code>flag</code>改为<code>FALSE</code>就可以了。但是如果只是这么做的话,随着操作的次数增加,会产生许多多余的没必要的节点,浪费很多的空间。<br>因此这个函数需要完成两件事</p><ul><li>判断删除的单词是否存在,假如不存在则返回<code>FALSE</code>.</li><li>删除多余的节点。<br>节点必须同时满足以下两点才能定义为多余的节点。</li><li>没有子节点(叶子节点)</li><li>flag 的值为 FALSE</li></ul><h3 id="完整实现"><a href="#完整实现" class="headerlink" title="完整实现"></a>完整实现</h3><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TRUE 1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FALSE 0</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SUB_NODE_COUNT 26</span></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></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">node</span>* <span class="title">children</span>[<span class="title">SUB_NODE_COUNT</span>];</span></span><br><span class="line"> <span class="keyword">int</span> flag;</span><br><span class="line"> <span class="keyword">char</span> character;</span><br><span class="line">} Node;</span><br><span class="line"></span><br><span class="line"><span class="function">Node* <span class="title">create_node</span><span class="params">(<span class="keyword">char</span> c, <span class="keyword">int</span> flag)</span> </span>{</span><br><span class="line"> Node* n = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Node));</span><br><span class="line"> n->character = c;</span><br><span class="line"> n->flag = flag;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> n->children[i] = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">append_node</span><span class="params">(Node* n, <span class="keyword">char</span> c)</span> </span>{</span><br><span class="line"></span><br><span class="line"> Node* child_ptr = n->children[c-<span class="string">'a'</span>];</span><br><span class="line"> <span class="keyword">if</span> (child_ptr) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> n->children[c-<span class="string">'a'</span>] = create_node(c, FALSE);</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">add_word</span><span class="params">(Node* root, <span class="keyword">char</span>* str)</span> </span>{</span><br><span class="line"> <span class="keyword">char</span> c = *str;</span><br><span class="line"> Node* ptr = root;</span><br><span class="line"> <span class="keyword">int</span> flag = TRUE;</span><br><span class="line"> <span class="keyword">while</span>(c != <span class="string">'\0'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!append_node(ptr, c)) {</span><br><span class="line"> flag = FALSE;</span><br><span class="line"> }</span><br><span class="line"> ptr = ptr->children[c-<span class="string">'a'</span>];</span><br><span class="line"> c = *(++str);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!ptr->flag) {</span><br><span class="line"> flag = FALSE;</span><br><span class="line"> ptr->flag = TRUE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> !flag;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">traversal</span><span class="params">(Node* root, <span class="keyword">char</span>* str)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!root) {</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="keyword">int</span> len_of_str = <span class="built_in">strlen</span>(str);</span><br><span class="line"> <span class="keyword">char</span>* new_str = <span class="built_in">malloc</span>(len_of_str+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(new_str, str);</span><br><span class="line"> new_str[len_of_str] = root->character;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (root->flag) {</span><br><span class="line"> <span class="comment">//输出</span></span><br><span class="line"> <span class="keyword">char</span>* str_for_print = <span class="built_in">malloc</span>(len_of_str+<span class="number">2</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(str_for_print, new_str);</span><br><span class="line"> str_for_print[len_of_str+<span class="number">1</span>] = <span class="string">'\0'</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>, str_for_print);</span><br><span class="line"> <span class="built_in">free</span>(str_for_print);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> traversal(root->children[i], new_str);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">free</span>(new_str);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">check</span><span class="params">(Node* root, <span class="keyword">char</span>* word)</span> </span>{</span><br><span class="line"> Node* ptr = root;</span><br><span class="line"> <span class="keyword">int</span> len = <span class="built_in">strlen</span>(word);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!ptr) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" isn't in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> ptr = ptr->children[word[i]-<span class="string">'a'</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (ptr && ptr->flag) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" is in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\"%s\" isn't in the Dictionary!\n"</span>, word);</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLeave</span><span class="params">(Node* root)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < SUB_NODE_COUNT; i++) {</span><br><span class="line"> <span class="keyword">if</span> (root->children[i]) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">delete_word</span><span class="params">(Node* root, <span class="keyword">char</span>* word)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> len = <span class="built_in">strlen</span>(word);</span><br><span class="line"> <span class="keyword">int</span> first_index = word[<span class="number">0</span>] - <span class="string">'a'</span>;</span><br><span class="line"> <span class="keyword">if</span> (!root->children[first_index]) {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (len == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (root->children[first_index]->flag) {</span><br><span class="line"> <span class="keyword">if</span> (isLeave(root->children[first_index])) {</span><br><span class="line"> <span class="built_in">free</span>(root->children[first_index]);</span><br><span class="line"> root->children[first_index] = <span class="literal">NULL</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> root->children[first_index]->flag = FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> TRUE;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> FALSE;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> flag = delete_word(root->children[first_index], word+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (isLeave(root->children[first_index]) && !root->children[first_index]->flag) {</span><br><span class="line"> <span class="built_in">free</span>(root->children[first_index]);</span><br><span class="line"> root->children[first_index] = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> flag;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>{</span><br><span class="line"> Node *root = create_node(<span class="string">'$'</span>, FALSE);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//测试add_ word函数</span></span><br><span class="line"> add_word(root, <span class="string">"abc"</span>);</span><br><span class="line"> add_word(root, <span class="string">"abcd"</span>);</span><br><span class="line"> add_word(root, <span class="string">"world"</span>);</span><br><span class="line"> add_word(root, <span class="string">"nnaiodnf"</span>);</span><br><span class="line"> traversal(root, <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//测试check函数</span></span><br><span class="line"> check(root, <span class="string">"abc"</span>);</span><br><span class="line"> check(root, <span class="string">"abcd"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//测试delete_word函数</span></span><br><span class="line"> delete_word(root, <span class="string">"abe"</span>);</span><br><span class="line"> check(root, <span class="string">"abe"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> EXIT_SUCCESS;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Reference:<a href="https://www.jianshu.com/p/72456ea14b1b" target="_blank" rel="noopener">用 C 语言实现字典树</a></p><h2 id="线段树"><a href="#线段树" class="headerlink" title="线段树"></a>线段树</h2><p>直接上链接,说的贼详细<br><a href="https://www.cnblogs.com/AC-King/p/7789013.html" target="_blank" rel="noopener">线段树详解</a></p><h2 id="树状数组"><a href="#树状数组" class="headerlink" title="树状数组"></a>树状数组</h2><p>直接上链接,说的贼详细<br><a href="https://www.cnblogs.com/xenny/p/9739600.html" target="_blank" rel="noopener">树状数组详解</a></p><h2 id="最小生成树"><a href="#最小生成树" class="headerlink" title="最小生成树"></a>最小生成树</h2><p>这个不是树,是图,就直接放链接吧<br><a href="https://blog.csdn.net/qq_39630587/article/details/77427044" target="_blank" rel="noopener">最小生成树构造算法</a><br><del><strong>其实是我学不动了</strong></del></p><h1 id="LeetCode-题解"><a href="#LeetCode-题解" class="headerlink" title="LeetCode 题解"></a>LeetCode 题解</h1><h2 id="LeetCode-题号-101-对称二叉树"><a href="#LeetCode-题号-101-对称二叉树" class="headerlink" title="LeetCode 题号: 101. 对称二叉树"></a>LeetCode 题号: 101. 对称二叉树</h2><p>话不多说,直接上代码</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * struct TreeNode *left;</span></span><br><span class="line"><span class="comment"> * struct TreeNode *right;</span></span><br><span class="line"><span class="comment"> * };</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isSymmetric</span><span class="params">(struct TreeNode* root)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> fun(root->left, root->right);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">fun</span><span class="params">(struct TreeNode* left,struct TreeNode* right)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(left == <span class="literal">NULL</span>&&right == <span class="literal">NULL</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(left == <span class="literal">NULL</span> || right == <span class="literal">NULL</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(left->val!=right->val){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> fun(left->left,right->right)&&fun(left->right,right->left);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>思路:如果同时满足下面的条件,两个树互为镜像:</p><ul><li>它们的两个根结点具有相同的值。</li><li>每个树的右子树都与另一个树的左子树镜像对称。<br>因此,我们就可以很自然地把上边的想法写成一个递归函数。</li></ul><h2 id="LeetCode-题号:96-不同的二叉搜索树"><a href="#LeetCode-题号:96-不同的二叉搜索树" class="headerlink" title="LeetCode 题号:96. 不同的二叉搜索树"></a>LeetCode 题号:96. 不同的二叉搜索树</h2><p>可以使用动态规划法求解<br>给定一个有序序列 1 … n,为了根据序列构建一棵二叉搜索树。我们可以遍历每个数字 i,将该数字作为树根,1 … (i-1) 序列将成为左子树,(i+1) … n 序列将成为右子树。于是,我们可以递归地从子序列构建子树。<br>在上述方法中,由于根各自不同,每棵二叉树都肯定能保证是独特的。<br>C 语言实现:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">numTrees</span><span class="params">(<span class="keyword">int</span> n)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> * dp = (<span class="keyword">int</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="keyword">int</span>)*(n+<span class="number">2</span>));</span><br><span class="line"> bzero(dp,<span class="keyword">sizeof</span>(<span class="keyword">int</span>)*(n+<span class="number">2</span>));</span><br><span class="line"> dp[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> dp[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>; j <= i; j++){</span><br><span class="line"> dp[i] += dp[j - <span class="number">1</span>] * dp[i - j];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[n];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="LeetCode-题号-99-恢复二叉搜索树"><a href="#LeetCode-题号-99-恢复二叉搜索树" class="headerlink" title="LeetCode 题号: 99. 恢复二叉搜索树"></a>LeetCode 题号: 99. 恢复二叉搜索树</h2><p>因为题目中说了,二叉搜索树中只有两个节点被错误地交换。并且,正常的二叉搜索树中序遍历输出应该是递增的。<br>所以,在遍历二叉树的过程中找到不满足递增的点(即错误交换的点),交换两者的值即可。<br>错误交换的点在中序遍历结果中可能是相邻的,也可能是不相邻的。<br>例如,若一棵树中序遍历结果 1324,错误交换的点 2 和 3 就是相邻的;使用 first 和 second 表示错误交换的两个点,在第一次遇到不递增的情况时,将 first 置为 3,second 置为 2,遍历结束后交换 first 与 second。</p><p>再如,若一棵树中序中序遍历结果 321,错误交换的点就是不相邻的。在第一次遇到不递增的情况时,将 first 设置为 3,second 设置为 2,在第二次遇到不递增的情况时,只改变 second,将 second 置为 1.遍历结束后交换 first 与 second。<br>将这个想法写成代码,就成了下面这个样子。</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * struct TreeNode *left;</span></span><br><span class="line"><span class="comment"> * struct TreeNode *right;</span></span><br><span class="line"><span class="comment"> * };</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TreeNode</span>* <span class="title">first</span> = <span class="title">NULL</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TreeNode</span>* <span class="title">second</span> = <span class="title">NULL</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TreeNode</span>* <span class="title">pre</span> = <span class="title">NULL</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inorder</span><span class="params">(struct TreeNode* root)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(root == <span class="literal">NULL</span>)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> inorder(root->left);</span><br><span class="line"> <span class="keyword">if</span>(pre != <span class="literal">NULL</span> && pre->val > root->val){</span><br><span class="line"> <span class="keyword">if</span>(first == <span class="literal">NULL</span>){<span class="comment">//first为空,首次找到前后大小不对的点</span></span><br><span class="line"> first = pre;</span><br><span class="line"> second = root;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{<span class="comment">//first不为空,第二次找到前后大小不对的点,只更新second</span></span><br><span class="line"> second = root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> pre = root;</span><br><span class="line"> inorder(root->right);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">recoverTree</span><span class="params">(struct TreeNode* root)</span></span>{</span><br><span class="line"> inorder(root);</span><br><span class="line"> <span class="keyword">int</span> tmp = first->val;</span><br><span class="line"> first->val = second->val;</span><br><span class="line"> second->val = tmp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然而并没有 AC,就先洗洗睡了</p><h1 id="我保证"><a href="#我保证" class="headerlink" title="我保证"></a>我保证</h1><p>会把线段树、树状数组、最小生成树认真学了<br>就这样,先咕为敬!<br>有空再补!<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1571031606850.gif" alt="咕咕咕"><br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/924112618.png" alt="笑着看着你"></p>]]></content>
<summary type="html">
<p>咕咕咕了这么久,终于有空好好学学树与二叉树的数据结构了。</p>
<p>Update 2020.04.16 :过去了半年,依旧还没有完成…… <em>咕咕咕,咕咕咕,咕叽咕叽咕</em></p>
</summary>
<category term="C" scheme="http://blog.zam-meow.cn/tags/C/"/>
<category term="Learning" scheme="http://blog.zam-meow.cn/tags/Learning/"/>
<category term="Data Structure" scheme="http://blog.zam-meow.cn/tags/Data-Structure/"/>
<category term="Tree" scheme="http://blog.zam-meow.cn/tags/Tree/"/>
<category term="Binary Tree" scheme="http://blog.zam-meow.cn/tags/Binary-Tree/"/>
</entry>
<entry>
<title>Network-Task2</title>
<link href="http://blog.zam-meow.cn/2019/11/10/Network-Task2/"/>
<id>http://blog.zam-meow.cn/2019/11/10/Network-Task2/</id>
<published>2019-11-10T05:13:00.000Z</published>
<updated>2020-04-16T02:15:51.605Z</updated>
<content type="html"><![CDATA[<p>吹着前奏望着天空,我想起 Task 试着完成,为它爆肝的那一天,断电的那一天,709 的那一间,我怎么看不见,消失的 DDL,我好像再来一年~</p><a id="more"></a><h1 id="安装准备"><a href="#安装准备" class="headerlink" title="安装准备"></a>安装准备</h1><h2 id="Problem1-apt-源配置问题"><a href="#Problem1-apt-源配置问题" class="headerlink" title="Problem1.apt 源配置问题"></a>Problem1.apt 源配置问题</h2><p>在开始之前,我才发现自己的 apt 源配错了。我使用的是 Ubuntu19.10(eoan),但是 apt 源里居然全写的是 Ubuntu 18.04 LTS(Bionic)的源……<br>因此出现了花式安装报错的问题。<br><strong>Solution:</strong><br>自己手动把所有的 Bionic 都换成 eoan……</p><h2 id="Problem2-没有合适的-IDE"><a href="#Problem2-没有合适的-IDE" class="headerlink" title="Problem2.没有合适的 IDE"></a>Problem2.没有合适的 IDE</h2><p>由于这次任务需要在 Linux 上完成,自己的能力又没有强到能够用眼调试的水平。因此需要安装一个 IDE。(能用 GCC 调试我也服气)<br><strong>Solution:</strong><br>用 HUST 邮箱整一个 CLion。网址:<a href="http://www.jetbrains.com/student/" target="_blank" rel="noopener">CLion</a><br>白嫖就完事了~</p><h1 id="Echo-Server"><a href="#Echo-Server" class="headerlink" title="Echo Server"></a>Echo Server</h1><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>话不多说,直接上代码:</p><h3 id="Linux-Server-端"><a href="#Linux-Server-端" class="headerlink" title="Linux Server 端"></a>Linux Server 端</h3><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><netdb.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/socket.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><errno.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><arpa/inet.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> EHCO_PORT 28345<span class="comment">//监听端口</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_CLIENT_NUM 10<span class="comment">//最大客户端连接数量</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_BUFF 101<span class="comment">//最大缓冲区长度</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">charConversion</span><span class="params">(<span class="keyword">char</span> <span class="built_in">array</span>[])</span></span>;<span class="comment">//大小写转换</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLowerCase</span><span class="params">(<span class="keyword">char</span> ch)</span></span>;<span class="comment">//判断小写</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> socketfd;</span><br><span class="line"> socketfd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);<span class="comment">//创建套接字</span></span><br><span class="line"> <span class="keyword">if</span>(socketfd == <span class="number">-1</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"errno=%d \n"</span>, errno);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"socket create successfully \n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">sa</span>;</span></span><br><span class="line"> bzero(&sa,<span class="keyword">sizeof</span>(sa));<span class="comment">//重置结构体</span></span><br><span class="line"> sa.sin_family = AF_INET;</span><br><span class="line"> sa.sin_port = htons(EHCO_PORT);<span class="comment">//设定监听端口</span></span><br><span class="line"> sa.sin_addr.s_addr = htons(INADDR_ANY);<span class="comment">//设定本地IP</span></span><br><span class="line"> bzero(&(sa.sin_zero), <span class="number">8</span>);</span><br><span class="line"> <span class="keyword">if</span>(bind(socketfd, (struct sockaddr *)&sa, <span class="keyword">sizeof</span>(sa))!= <span class="number">0</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"bind failed \n"</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"errno=%d \n"</span>, errno);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"bind successfully \n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(listen(socketfd ,MAX_CLIENT_NUM) != <span class="number">0</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"listen error "</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"listen successfully \n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> clientfd;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">clientAdd</span>;</span></span><br><span class="line"> <span class="keyword">char</span> buff[MAX_BUFF];</span><br><span class="line"> <span class="keyword">socklen_t</span> len = <span class="keyword">sizeof</span>(clientAdd);</span><br><span class="line"> <span class="keyword">int</span> closing =<span class="number">0</span>;</span><br><span class="line"> clientfd = accept(socketfd, (struct sockaddr *)&clientAdd, &len);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Client ip is %s,port is %d\n"</span>,inet_ntoa(clientAdd.sin_addr),ntohs(clientAdd.sin_port));</span><br><span class="line"> <span class="keyword">while</span>(closing == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">int</span> n;</span><br><span class="line"> <span class="keyword">while</span> ((n = recv(clientfd, buff, <span class="number">100</span>, <span class="number">0</span>)) > <span class="number">0</span>) {<span class="comment">//循环接收数据</span></span><br><span class="line"> buff[n]=<span class="string">'\0'</span>;</span><br><span class="line"> write(STDOUT_FILENO, buff, n);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"successfully recv %d bytes\n"</span>, n);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"client says :%s\n"</span>, buff);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(buff, <span class="string">"quit"</span>) == <span class="number">0</span>) {<span class="comment">//根据不同情形判断操作</span></span><br><span class="line"> send(clientfd, <span class="string">"See you~"</span>, <span class="built_in">strlen</span>(<span class="string">"See you~"</span>), <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"server says: See you~\n"</span>);</span><br><span class="line"> closing = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> charConversion(buff);<span class="comment">//将小写全部变为大写</span></span><br><span class="line"> send(clientfd, buff, n, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"server replys:%s\n"</span>, buff);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> close(clientfd);</span><br><span class="line"> close(socketfd);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"socket close successfully!\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">charConversion</span><span class="params">(<span class="keyword">char</span> <span class="built_in">array</span>[])</span></span>{</span><br><span class="line"> <span class="keyword">int</span> i=<span class="number">0</span>; <span class="comment">// 定义数组循环下标</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="built_in">array</span>[i]){<span class="comment">// 数组访问循环控制</span></span><br><span class="line"> <span class="comment">//判断是否遇到了字符串的结束标志</span></span><br><span class="line"> <span class="keyword">if</span>( isLowerCase(<span class="built_in">array</span>[i]) ){</span><br><span class="line"> <span class="built_in">array</span>[i] = <span class="built_in">array</span>[i] - (<span class="string">'a'</span>-<span class="string">'A'</span>); <span class="comment">// 小写转大写</span></span><br><span class="line"> }</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检测字符是否为小写字母, [a, z]</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLowerCase</span><span class="params">(<span class="keyword">char</span> ch)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> (ch>=<span class="string">'a'</span>&&ch<=<span class="string">'z'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Windows-client-端"><a href="#Windows-client-端" class="headerlink" title="Windows client 端"></a>Windows client 端</h3><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><windows.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><winsock.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment( lib, <span class="meta-string">"ws2_32.lib"</span> )</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PORT 28345</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXDATASIZE 100</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Address 192.168.188.128</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> iClientSock;</span><br><span class="line"><span class="keyword">int</span> addr_len;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">ServerAddr</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> numbytes;</span><br><span class="line"><span class="keyword">char</span> buf[MAXDATASIZE+<span class="number">1</span>] = <span class="string">"hello world!"</span>;</span><br><span class="line"></span><br><span class="line">WSADATA WSAData;</span><br><span class="line"> <span class="comment">//连接准备部分以及初始化</span></span><br><span class="line"><span class="keyword">if</span> (WSAStartup(<span class="number">0x0202</span>, &WSAData)){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"initializationing error!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ((iClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == <span class="number">-1</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"创建套接字失败!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line">ServerAddr.sin_family = PF_INET;</span><br><span class="line">ServerAddr.sin_port = htons(PORT);</span><br><span class="line">ServerAddr.sin_addr.s_addr = inet_addr(<span class="string">"192.168.188.128"</span>);<span class="comment">//换localhost</span></span><br><span class="line"><span class="keyword">if</span> (connect(iClientSock, (struct sockaddr*) & ServerAddr, <span class="keyword">sizeof</span>(SOCKADDR_IN)) != <span class="number">-1</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Connection success!\n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Connection failed!\n"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">memset</span>(&(ServerAddr.sin_zero), <span class="number">0</span>, <span class="keyword">sizeof</span>(ServerAddr.sin_zero));</span><br><span class="line">addr_len = <span class="keyword">sizeof</span>(struct sockaddr);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Server ip is %s,port is %d\n"</span>, inet_ntoa(ServerAddr.sin_addr), PORT);</span><br><span class="line"> <span class="comment">//发送数据部分</span></span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"client says:"</span>);</span><br><span class="line">gets(buf);</span><br><span class="line">numbytes = send(iClientSock, buf, <span class="built_in">strlen</span>(buf), <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (numbytes == <span class="number">-1</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"send调用失败!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"successfully send %d bytes\n"</span>, numbytes);</span><br><span class="line">numbytes = <span class="number">-1</span>;</span><br><span class="line">numbytes = recv(iClientSock, buf, MAXDATASIZE, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (numbytes == <span class="number">-1</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"recv调用失败!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"> buf[numbytes] = <span class="string">'\0'</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"recv %d bytes\n"</span>, numbytes);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"server replys:%s\n"</span>, buf);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">strcmp</span>(buf, <span class="string">"See you~"</span>) == <span class="number">0</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">closesocket(iClientSock);</span><br><span class="line">WSACleanup();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/Echo_Server_Demo.png" alt="Echo Server Demo"></p><h2 id="Problem"><a href="#Problem" class="headerlink" title="Problem"></a>Problem</h2><h3 id="Socket-一直不能建立连接"><a href="#Socket-一直不能建立连接" class="headerlink" title="Socket 一直不能建立连接"></a>Socket 一直不能建立连接</h3><p>这是由于 Linux 和 Windows 的防火墙策略设置<br><strong>Solution:</strong><br>在 Linux 中安装白名单 IP 地址:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iptables -N whitelist-A whitelist -s 10.10.68.58 -j ACCEPT #将10.10.68.58换成你的主机真实网卡的IP地址</span><br></pre></td></tr></table></figure><h3 id="网卡设置有误"><a href="#网卡设置有误" class="headerlink" title="网卡设置有误"></a>网卡设置有误</h3><p>在 Task1 中,我错误地将主机中 VMnet8 的 IP 地址设置成了 VM1 的 IP 地址 192.168.188.128,导致出现了我一直都不能用 ssh 连接上我的虚拟机,但是 ping 又能 ping 通的诡异现象。<br><strong>Solution:</strong><br>在 Windows 系统中,更改 VMnet8 这张网卡的 IP 地址在虚拟机的相同网段下的其它 IP,例如说 192.168.188.1。</p><h3 id="客户端协议设置出错"><a href="#客户端协议设置出错" class="headerlink" title="客户端协议设置出错"></a>客户端协议设置出错</h3><p>由于之前在熬测的时候写过了类似的程序,于是乎我想都没想就在 Windows 系统上的 client 用了之前的的源码,然而这份源码是 UDP 协议的,但是在 Linux 上跑的是 TCP 协议的 Server,导致 Server 一直不能发包到 Client。<br><strong>Solution:</strong><br>重写 Windows 系统下的源码,改用 TCP 协议。<br>虽然这次错误实在是有点蠢,但是我因此更加明确了我这次 Task 使用的一些 socket 函数在 UDP 协议与 TCP 协议的不同情况下使用的一些区别,也算是因祸得福(确信。</p><h2 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h2><p>client</p><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Windows</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><windows.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><winsock.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment( lib, <span class="meta-string">"ws2_32.lib"</span> )</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Address <span class="meta-string">"192.168.188.128"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/types.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/socket.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><netinet/in.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><arpa/inet.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Address <span class="meta-string">"192.168.188.1"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PORT 28345</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXDATASIZE 100</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> iClientSock;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">ServerAddr</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> numbytes;</span><br><span class="line"><span class="keyword">char</span> buf[MAXDATASIZE+<span class="number">1</span>] = <span class="string">"hello world!"</span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSADATA WSAData;</span><br><span class="line"><span class="keyword">if</span> (WSAStartup(<span class="number">0x0202</span>, &WSAData))</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"initializationing error!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ((iClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == <span class="number">-1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"创建套接字失败!\n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line">ServerAddr.sin_family = PF_INET;</span><br><span class="line">ServerAddr.sin_port = htons(PORT);</span><br><span class="line">ServerAddr.sin_addr.s_addr = inet_addr(Address);<span class="comment">//换localhost</span></span><br><span class="line"><span class="keyword">if</span> (connect(iClientSock, (struct sockaddr*) & ServerAddr, <span class="keyword">sizeof</span>(ServerAddr)) != <span class="number">-1</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Connection success!\n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Connection failed!\n"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">memset</span>(&(ServerAddr.sin_zero), <span class="number">0</span>, <span class="keyword">sizeof</span>(ServerAddr.sin_zero));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Server ip is %s,port is %d\n"</span>, inet_ntoa(ServerAddr.sin_addr), PORT);</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"client says:"</span>);</span><br><span class="line">gets(buf);</span><br><span class="line">numbytes = send(iClientSock, buf, <span class="built_in">strlen</span>(buf), <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (numbytes == <span class="number">-1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"send调用失败!\n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"successfully send %d bytes\n"</span>, numbytes);</span><br><span class="line">numbytes = <span class="number">-1</span>;</span><br><span class="line">numbytes = recv(iClientSock, buf, MAXDATASIZE, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (numbytes == <span class="number">-1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"recv调用失败!\n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line">buf[numbytes] = <span class="string">'\0'</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"recv %d bytes\n"</span>, numbytes);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"server replys:%s\n"</span>, buf);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">strcmp</span>(buf, <span class="string">"See you~"</span>) == <span class="number">0</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"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">closesocket(iClientSock);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line">close(iClientSock);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Server</p><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Windows</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><netdb.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sys/socket.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><errno.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><arpa/inet.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><winsock2.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><windows.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><ws2tcpip.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment( lib, <span class="meta-string">"ws2_32.lib"</span> )</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> EHCO_PORT 28345<span class="comment">//监听端口</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_CLIENT_NUM 10<span class="comment">//最大客户端连接数量</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_BUFF 101<span class="comment">//最大缓冲区长度</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">charConversion</span><span class="params">(<span class="keyword">char</span> <span class="built_in">array</span>[])</span></span>;<span class="comment">//大小写转换</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLowerCase</span><span class="params">(<span class="keyword">char</span> ch)</span></span>;<span class="comment">//判断小写</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSADATA WSAData;</span><br><span class="line"><span class="keyword">if</span> (WSAStartup(<span class="number">0x0202</span>, &WSAData))</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"initializationing error!\n"</span>);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="keyword">int</span> socketfd;</span><br><span class="line">socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);<span class="comment">//创建套接字</span></span><br><span class="line"><span class="keyword">if</span> (socketfd == <span class="number">-1</span>) {</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"errno=%d \n"</span>, errno);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"socket create failed \n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"socket create successfully \n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">sa</span>;</span></span><br><span class="line"><span class="built_in">memset</span>(&sa,<span class="number">0</span>,<span class="keyword">sizeof</span>(sa));<span class="comment">//重置结构体</span></span><br><span class="line">sa.sin_family = AF_INET;</span><br><span class="line">sa.sin_port = htons(EHCO_PORT);<span class="comment">//设定监听端口</span></span><br><span class="line">sa.sin_addr.s_addr = htons(INADDR_ANY);<span class="comment">//设定本地IP</span></span><br><span class="line"><span class="built_in">memset</span>(&(sa.sin_zero),<span class="number">0</span>, <span class="number">8</span>);</span><br><span class="line"><span class="keyword">if</span> (bind(socketfd, (struct sockaddr*) & sa, <span class="keyword">sizeof</span>(sa)) != <span class="number">0</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"bind failed \n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"errno=%d \n"</span>, errno);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"bind successfully \n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (listen(socketfd, MAX_CLIENT_NUM) != <span class="number">0</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"listen error "</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"listen successfully \n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> clientfd;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">clientAdd</span>;</span></span><br><span class="line"><span class="keyword">char</span> buff[MAX_BUFF];</span><br><span class="line"><span class="keyword">socklen_t</span> len = <span class="keyword">sizeof</span>(clientAdd);</span><br><span class="line"><span class="keyword">int</span> closing = <span class="number">0</span>;</span><br><span class="line">clientfd = accept(socketfd, (struct sockaddr*) & clientAdd, &len);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Client ip is %s,port is %d\n"</span>, inet_ntoa(clientAdd.sin_addr), ntohs(clientAdd.sin_port));</span><br><span class="line"><span class="keyword">while</span> (closing == <span class="number">0</span>) {</span><br><span class="line"><span class="keyword">int</span> n;</span><br><span class="line"><span class="keyword">while</span> ((n = recv(clientfd, buff, <span class="number">100</span>, <span class="number">0</span>)) > <span class="number">0</span>) {<span class="comment">//循环接收数据</span></span><br><span class="line">buff[n] = <span class="string">'\0'</span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line">write(STDOUT_FILENO, buff, n);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"successfully recv %d bytes\n"</span>, n);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"client says :%s\n"</span>, buff);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">strcmp</span>(buff, <span class="string">"quit"</span>) == <span class="number">0</span>) {<span class="comment">//根据不同情形判断操作</span></span><br><span class="line">send(clientfd, <span class="string">"See you~"</span>, <span class="built_in">strlen</span>(<span class="string">"See you~"</span>), <span class="number">0</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"server says: See you~\n"</span>);</span><br><span class="line">closing = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">charConversion(buff);<span class="comment">//将小写全部变为大写</span></span><br><span class="line">send(clientfd, buff, n, <span class="number">0</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"server replys:%s\n"</span>, buff);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Windows</span></span><br><span class="line">closesocket(clientfd);</span><br><span class="line">closesocket(socketfd);</span><br><span class="line">WSACleanup();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> Linux</span></span><br><span class="line">close(clientfd);</span><br><span class="line">close(socketfd);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"socket close successfully!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">charConversion</span><span class="params">(<span class="keyword">char</span> <span class="built_in">array</span>[])</span> </span>{</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>; <span class="comment">// 定义数组循环下标</span></span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">array</span>[i]) {<span class="comment">// 数组访问循环控制</span></span><br><span class="line"> <span class="comment">//判断是否遇到了字符串的结束标志</span></span><br><span class="line"><span class="keyword">if</span> (isLowerCase(<span class="built_in">array</span>[i])) {</span><br><span class="line"><span class="built_in">array</span>[i] = <span class="built_in">array</span>[i] - (<span class="string">'a'</span> - <span class="string">'A'</span>); <span class="comment">// 小写转大写</span></span><br><span class="line">}</span><br><span class="line">i++;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检测字符是否为小写字母, [a, z]</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">isLowerCase</span><span class="params">(<span class="keyword">char</span> ch)</span> </span>{</span><br><span class="line"><span class="keyword">return</span> (ch >= <span class="string">'a'</span> && ch <= <span class="string">'z'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="File-Server"><a href="#File-Server" class="headerlink" title="File Server"></a>File Server</h1><p>话不多说,直接上代码</p><h2 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h2><h3 id="Linux-receiver"><a href="#Linux-receiver" class="headerlink" title="Linux receiver"></a>Linux receiver</h3><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"head.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BUFF_SIZE 1024</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PORT 28345</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FILE_NAME_SIZE 512</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">s_addr</span>,<span class="title">c_addr</span>;</span></span><br><span class="line"> <span class="keyword">int</span> s_sockfd,c_sockfd;</span><br><span class="line"> <span class="keyword">char</span> buf[BUFF_SIZE];</span><br><span class="line"> <span class="keyword">int</span> c_addr_len=<span class="keyword">sizeof</span>(struct sockaddr_in);</span><br><span class="line"> <span class="keyword">int</span> s_addr_len=<span class="keyword">sizeof</span>(struct sockaddr_in);</span><br><span class="line"> s_sockfd=socket(AF_INET,SOCK_STREAM,<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(s_sockfd==<span class="number">-1</span>)</span><br><span class="line"> strerr(<span class="string">"socket error"</span>);</span><br><span class="line"> <span class="built_in">memset</span>(&s_addr,<span class="number">0</span>,s_addr_len);</span><br><span class="line"> s_addr.sin_family=AF_INET;</span><br><span class="line"> s_addr.sin_port=htons(PORT);</span><br><span class="line"> s_addr.sin_addr.s_addr=htonl(INADDR_ANY);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> opt=<span class="number">1</span>;</span><br><span class="line"> setsockopt(s_sockfd,SOL_SOCKET, SO_REUSEADDR, &opt, <span class="keyword">sizeof</span>(opt));</span><br><span class="line"> <span class="keyword">if</span>(bind(s_sockfd,(struct sockaddr*)&s_addr,s_addr_len)==<span class="number">-1</span>)</span><br><span class="line"> strerr(<span class="string">"bind error"</span>);</span><br><span class="line"> <span class="keyword">if</span>(listen(s_sockfd,<span class="number">5</span>)==<span class="number">-1</span>)</span><br><span class="line"> strerr(<span class="string">"listen error"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"listening to connect........\n"</span>);</span><br><span class="line"> c_sockfd=accept(s_sockfd,(struct sockaddr*)&c_addr,&c_addr_len);</span><br><span class="line"> <span class="keyword">if</span>(c_sockfd==<span class="number">-1</span>)</span><br><span class="line"> strerr(<span class="string">"accept error"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(buf,<span class="number">0</span>,BUFF_SIZE);</span><br><span class="line"> <span class="keyword">if</span>(recv(c_sockfd,buf,BUFF_SIZE,<span class="number">0</span>)==<span class="number">-1</span>)</span><br><span class="line"> strerr(<span class="string">"recv error"</span>);</span><br><span class="line"> <span class="keyword">char</span> filename[FILE_NAME_SIZE+<span class="number">1</span>];</span><br><span class="line"> <span class="built_in">memset</span>(filename,<span class="number">0</span>,FILE_NAME_SIZE+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(filename,buf,<span class="built_in">strlen</span>(buf)>FILE_NAME_SIZE? FILE_NAME_SIZE:<span class="built_in">strlen</span>(buf));</span><br><span class="line"> filename[<span class="built_in">strlen</span>(buf)]=<span class="string">'\0'</span>;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">strcmp</span>(filename,<span class="string">"quit"</span>)==<span class="number">0</span>){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Exit.\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"file name is %s\n"</span>,filename);</span><br><span class="line"></span><br><span class="line"> FILE * fp=fopen(filename,<span class="string">"wb+"</span>);</span><br><span class="line"> <span class="keyword">if</span>(<span class="literal">NULL</span>==fp)</span><br><span class="line"> strerr(<span class="string">"fopen error"</span>);</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">int</span> length=<span class="number">0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(buf,<span class="number">0</span>,BUFF_SIZE);</span><br><span class="line"> <span class="keyword">while</span>((length=recv(c_sockfd,buf,BUFF_SIZE,<span class="number">0</span>))><span class="number">0</span>){</span><br><span class="line"> <span class="keyword">if</span>(fwrite(buf,<span class="keyword">sizeof</span>(<span class="keyword">char</span>),length,fp)<length)</span><br><span class="line"> strerr(<span class="string">"fwrite error"</span>);</span><br><span class="line"> <span class="built_in">memset</span>(buf,<span class="number">0</span>,BUFF_SIZE);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"receive file : %s from server successful\n"</span>,filename);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> }</span><br><span class="line"> close(c_sockfd);</span><br><span class="line"> }<span class="comment">//end while(1)</span></span><br><span class="line"> close(s_sockfd);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Windows-Sender"><a href="#Windows-Sender" class="headerlink" title="Windows Sender"></a>Windows Sender</h3><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _CRT_SECURE_NO_WARNINGS</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _WINSOCK_DEPRECATED_NO_WARNINGS</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><WinSock2.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PORT 28345</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SERVER_IP <span class="meta-string">"192.168.188.128"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BUFFER_SIZE 1024</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FILE_NAME_MAX_SIZE 512</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment(lib, <span class="meta-string">"WS2_32"</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (argc != <span class="number">2</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"usage : %s filename and format\n"</span>, argv[<span class="number">0</span>]);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 初始化socket dll</span></span><br><span class="line">WSADATA wsaData;</span><br><span class="line">WORD socketVersion = MAKEWORD(<span class="number">2</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (WSAStartup(socketVersion, &wsaData) != <span class="number">0</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Init socket dll error!"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建socket</span></span><br><span class="line">SOCKET c_Socket = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (SOCKET_ERROR == c_Socket){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Create Socket Error!"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}<span class="comment">//指定服务端的地址</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">server_addr</span>;</span></span><br><span class="line">server_addr.sin_family = AF_INET;</span><br><span class="line">server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);</span><br><span class="line">server_addr.sin_port = htons(PORT);</span><br><span class="line"><span class="keyword">int</span> opt = <span class="number">1</span>;</span><br><span class="line">setsockopt(c_Socket, SOL_SOCKET, SO_REUSEADDR, &opt, <span class="keyword">sizeof</span>(opt));</span><br><span class="line"><span class="keyword">if</span> (SOCKET_ERROR == connect(c_Socket, (LPSOCKADDR)&server_addr, <span class="keyword">sizeof</span>(server_addr))){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Can Not Connect To Server IP!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> buffer[BUFFER_SIZE];<span class="comment">//初始化缓冲区</span></span><br><span class="line"><span class="built_in">memset</span>(buffer, <span class="number">0</span>, BUFFER_SIZE);</span><br><span class="line"><span class="built_in">strncpy</span>(buffer, argv[<span class="number">1</span>], <span class="built_in">strlen</span>(argv[<span class="number">1</span>]) > BUFFER_SIZE ? BUFFER_SIZE : <span class="built_in">strlen</span>(argv[<span class="number">1</span>]));<span class="comment">//将文件名传入缓冲区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//向服务器发送文件名</span></span><br><span class="line"><span class="keyword">if</span> (send(c_Socket, buffer, BUFFER_SIZE, <span class="number">0</span>) < <span class="number">0</span>){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Send File Name Failed\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</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">FILE* fp = fopen(argv[<span class="number">1</span>], <span class="string">"rb"</span>);<span class="comment">//windows下是"rb",表示打开一个只读的二进制文件</span></span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == fp){</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"File: %s Not Found\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">else</span>{</span><br><span class="line"><span class="built_in">memset</span>(buffer, <span class="number">0</span>, BUFFER_SIZE);</span><br><span class="line"> <span class="keyword">int</span> length = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//获取文件长度</span></span><br><span class="line">fseek(fp, <span class="number">0</span>, SEEK_END); <span class="comment">//定位到文件末</span></span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> nFileLen = ftell(fp); <span class="comment">//文件总长度</span></span><br><span class="line">fseek(fp, <span class="number">0</span>, SEEK_SET); <span class="comment">//恢复到文件头</span></span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> sendLength = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span> ((length = fread(buffer, <span class="keyword">sizeof</span>(<span class="keyword">char</span>), BUFFER_SIZE, fp)) > <span class="number">0</span>) {</span><br><span class="line"><span class="keyword">if</span> (send(c_Socket, buffer, length, <span class="number">0</span>) < <span class="number">0</span>) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Send File: %s Failed\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">sendLength += length;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"The File have sended %lld percent\n"</span>, sendLength * <span class="number">100</span> / nFileLen);</span><br><span class="line"><span class="built_in">memset</span>(buffer, <span class="number">0</span>, BUFFER_SIZE);</span><br><span class="line">}</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"File: %s Transfer Successful!\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line">closesocket(c_Socket);</span><br><span class="line"><span class="comment">//释放winsock库</span></span><br><span class="line">WSACleanup();</span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="效果-1"><a href="#效果-1" class="headerlink" title="效果"></a>效果</h2><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/File_Server_Demo.png" alt="File Server Demo"></p><h2 id="Problem-1"><a href="#Problem-1" class="headerlink" title="Problem"></a>Problem</h2><h3 id="Problem1:"><a href="#Problem1:" class="headerlink" title="Problem1:"></a>Problem1:</h3><p>遇到了服务端创建文件成功,但是写入文件失败的情况<br><strong>Solution:</strong><br>经过检查发现,是 Linux 接收端的代码出现了问题。我写了一个死循环来实现持续接收文件。但是忘记了在每次接收完文件后使用 fclose()语句来释放文件指针,导致文件写入失败。</p><h1 id="iptables-过滤报文"><a href="#iptables-过滤报文" class="headerlink" title="iptables 过滤报文"></a>iptables 过滤报文</h1><p>通过使用 iptables 丰富的拓展模块,我们可以实现以下 Task 所要求的内容:</p><h2 id="根据字符串过滤"><a href="#根据字符串过滤" class="headerlink" title="根据字符串过滤"></a>根据字符串过滤</h2><p>这个实现简单快捷,只需要一条 shell 语句:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iptables -t filter -I INPUT -m string --algo bm --string "CyberSecurity" -j REJECT</span><br></pre></td></tr></table></figure><p>‘-I INPUT’表示对 INPUT 链进行操作<br>‘-m string’ 表示使用 string 模块。<br>‘–algo bm’表示使用 bm 算法去匹配指定的字符串。<br>‘–string “CyberSecurity”‘则表示我们想要匹配的字符串。<br>实现效果:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E8%BF%87%E6%BB%A4%E6%8C%87%E5%AE%9A%E5%AD%97%E7%AC%A6%E4%B8%B2.png" alt="过滤指定字符串"></p><h2 id="根据时间过滤"><a href="#根据时间过滤" class="headerlink" title="根据时间过滤"></a>根据时间过滤</h2><p>这个实现也只需要一条 shell 语句:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iptables -t filter -I INPUT -p tcp --dport 28345 -m time --timestart 13:00:00 --timestop 16:00:00 -j REJECT</span><br></pre></td></tr></table></figure><p><strong>注意,这里的时间默认使用的是 UTC 标准时间,我们需要将我们想要设定的时间-8(因为我们在东八区)才可以使设置正常生效。</strong><br>‘-I INPUT’表示对 INPUT 链进行操作<br>‘-m time’表示使用 time 模块。<br>‘–timestart’选项用于指定起始时间<br>‘–timestop’选项用于指定结束时间<br>实现效果:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E8%BF%87%E6%BB%A4%E6%8C%87%E5%AE%9A%E6%97%B6%E9%97%B4.png" alt="过滤指定时间"></p><h1 id="其余部分"><a href="#其余部分" class="headerlink" title="其余部分"></a>其余部分</h1><p>能力有限<br>时间不足<br>告辞,有空会回来补的<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1571031606850.gif" alt="咕咕咕"></p>]]></content>
<summary type="html">
<p>吹着前奏望着天空,我想起 Task 试着完成,为它爆肝的那一天,断电的那一天,709 的那一间,我怎么看不见,消失的 DDL,我好像再来一年~</p>
</summary>
<category term="Dian CyberSecurity Team" scheme="http://blog.zam-meow.cn/tags/Dian-CyberSecurity-Team/"/>
<category term="Network" scheme="http://blog.zam-meow.cn/tags/Network/"/>
<category term="socket" scheme="http://blog.zam-meow.cn/tags/socket/"/>
</entry>
<entry>
<title>AST-Task3</title>
<link href="http://blog.zam-meow.cn/2019/11/02/AST-Task3/"/>
<id>http://blog.zam-meow.cn/2019/11/02/AST-Task3/</id>
<published>2019-11-02T12:18:39.000Z</published>
<updated>2020-04-16T02:12:02.254Z</updated>
<content type="html"><![CDATA[<p>鲸了!数据结构居然如此复杂!</p><a id="more"></a><h1 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h1><p>栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。<br>栈可以根据是否可以动态增加存储空间,分为动态栈和静态栈两种。</p><h2 id="动态栈"><a href="#动态栈" class="headerlink" title="动态栈"></a>动态栈</h2><p>动态栈是建立在 malloc 函数的基础之上的一种栈结构</p><h3 id="动态栈的数据结构定义"><a href="#动态栈的数据结构定义" class="headerlink" title="动态栈的数据结构定义"></a>动态栈的数据结构定义</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <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="keyword">int</span> data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> * <span class="title">last</span>;</span></span><br><span class="line">}*node;</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">Stack</span> //定义链式栈结构体</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> node top; <span class="comment">//栈顶指针</span></span><br><span class="line"> <span class="keyword">int</span> count; <span class="comment">//记录栈结点数量</span></span><br><span class="line">}<span class="built_in">stack</span>;</span><br></pre></td></tr></table></figure><p>由于动态栈的数据是可以变化的。因此在动态栈中是没有 init()操作的,每一次 push 操作都会使得动态栈的大小动态扩大。</p><h3 id="动态栈的入栈操作函数"><a href="#动态栈的入栈操作函数" class="headerlink" title="动态栈的入栈操作函数"></a>动态栈的入栈操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">push</span><span class="params">(<span class="built_in">stack</span> *S,<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">node curs = (node)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(struct Node));<span class="comment">//开辟新的存储空间用来存放链式表节点</span></span><br><span class="line">curs -> data = n;<span class="comment">//存入节点</span></span><br><span class="line">curs -> last = S->top;</span><br><span class="line">S -> top = curs;<span class="comment">//更新top指针</span></span><br><span class="line">S -> count ++;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="动态栈的出栈操作函数"><a href="#动态栈的出栈操作函数" class="headerlink" title="动态栈的出栈操作函数"></a>动态栈的出栈操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pop</span><span class="params">(<span class="built_in">stack</span> *S)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> output;</span><br><span class="line"> node curs; <span class="comment">//定义一个结点指针变量</span></span><br><span class="line"> <span class="keyword">if</span>(S->count == <span class="number">0</span>){ <span class="comment">//判断栈是否为空</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error,Stack is empty!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> curs = S->top; <span class="comment">//让新定义的结点指针指向当前栈顶</span></span><br><span class="line"> output = curs->data; <span class="comment">//保存当前节点的数据</span></span><br><span class="line"> S->top = curs->last; <span class="comment">//让栈顶指针指向栈顶结点的上一个结点</span></span><br><span class="line"> <span class="built_in">free</span>(curs); <span class="comment">//释放结点s的存储空间</span></span><br><span class="line"> S->count--; <span class="comment">//结点个数减一</span></span><br><span class="line"> <span class="keyword">return</span> output; <span class="comment">//返回栈顶元素</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="动态栈的取栈顶元素函数"><a href="#动态栈的取栈顶元素函数" class="headerlink" title="动态栈的取栈顶元素函数"></a>动态栈的取栈顶元素函数</h3><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="function"><span class="keyword">int</span> <span class="title">Gettop</span><span class="params">(<span class="built_in">stack</span> *S)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span>(S->count == <span class="number">0</span>) <span class="comment">//判断栈是否为空</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error,Stack is empty!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> S->top->data;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="静态栈"><a href="#静态栈" class="headerlink" title="静态栈"></a>静态栈</h2><p>静态栈是建立在静态数组的基础之上的一种栈结构。</p><h3 id="静态栈的数据结构定义"><a href="#静态栈的数据结构定义" class="headerlink" title="静态栈的数据结构定义"></a>静态栈的数据结构定义</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXSIZE 1000</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Stack</span> //定义栈</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">int</span> data[MAXSIZE]; <span class="comment">//栈中的数据</span></span><br><span class="line"> <span class="keyword">int</span> top; <span class="comment">//栈顶</span></span><br><span class="line">}<span class="built_in">stack</span>;</span><br></pre></td></tr></table></figure><h3 id="静态栈的初始化函数"><a href="#静态栈的初始化函数" class="headerlink" title="静态栈的初始化函数"></a>静态栈的初始化函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">stack</span> <span class="title">Init</span><span class="params">()</span></span>{ <span class="comment">//初始化栈</span></span><br><span class="line"> <span class="built_in">stack</span> s;</span><br><span class="line"> s->top = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">return</span> s;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="静态栈的进栈操作函数"><a href="#静态栈的进栈操作函数" class="headerlink" title="静态栈的进栈操作函数"></a>静态栈的进栈操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Push</span><span class="params">(<span class="built_in">stack</span> &s,<span class="keyword">int</span> n)</span></span>{ <span class="comment">//进栈操作,我们是对指针进行操作,因此需要添加取地址符</span></span><br><span class="line"> <span class="keyword">if</span>(s->top == P<span class="number">-1</span>){ <span class="comment">//进栈的时候必须判断是否栈满</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"stack full\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> s->top++;</span><br><span class="line"> s->data[s->top] = n; <span class="comment">//将数据放入数组中</span></span><br><span class="line"><span class="keyword">if</span>(s->top == P<span class="number">-1</span>){ <span class="comment">//进栈结束的时候判断是否栈满</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</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><p><strong>注意</strong> 由于静态表并不会自动增加存储空间,因此我们需要在进栈结束后判断栈是否已满,避免发生溢出情况。</p><h3 id="静态栈的出栈操作函数"><a href="#静态栈的出栈操作函数" class="headerlink" title="静态栈的出栈操作函数"></a>静态栈的出栈操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Pop</span><span class="params">(<span class="built_in">stack</span> &s)</span></span>{ <span class="comment">//出栈操作</span></span><br><span class="line"> <span class="keyword">int</span> n; <span class="comment">//出栈元素</span></span><br><span class="line"> <span class="keyword">if</span>(s->top == <span class="number">-1</span>){ <span class="comment">//出栈的时候必须判断是否栈空,避免数组越界</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"stack empty\n"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"> n = s->data[s->top];</span><br><span class="line"> s->top--;</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="静态栈的取栈顶元素函数"><a href="#静态栈的取栈顶元素函数" class="headerlink" title="静态栈的取栈顶元素函数"></a>静态栈的取栈顶元素函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Gettop</span><span class="params">(<span class="built_in">stack</span> *s)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(s->top == <span class="number">-1</span>){ <span class="comment">//判断栈是否为空</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error,Stack is empty!\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> s->data[s->top];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="栈的应用"><a href="#栈的应用" class="headerlink" title="栈的应用"></a>栈的应用</h2><h3 id="LeetCode-20-有效的括号"><a href="#LeetCode-20-有效的括号" class="headerlink" title="LeetCode 20 有效的括号"></a>LeetCode 20 有效的括号</h3><h4 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h4><p>遇左括号就入栈,遇右括号就把栈顶元素拿出来比对。<br>如果栈空(用 top 指针判断),则为 True。</p><h4 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h4><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isValid</span><span class="params">(<span class="keyword">char</span> * s)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>( s == <span class="literal">NULL</span> )</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">char</span> * <span class="built_in">stack</span> = (<span class="keyword">char</span> * ) <span class="built_in">malloc</span>( <span class="keyword">sizeof</span>(<span class="keyword">char</span>) * ( <span class="built_in">strlen</span>(s)+<span class="number">1</span> ) );</span><br><span class="line"> <span class="keyword">int</span> top = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> i=<span class="number">0</span>; s[i]!=<span class="string">'\0'</span>; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(s[i]==<span class="string">'('</span> || s[i]==<span class="string">'['</span> || s[i]==<span class="string">'{'</span>) <span class="comment">//左括号入栈</span></span><br><span class="line"> <span class="built_in">stack</span>[++top] = s[i];</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>( (s[i]==<span class="string">')'</span>&&<span class="built_in">stack</span>[top]==<span class="string">'('</span>) || (s[i]==<span class="string">']'</span>&&<span class="built_in">stack</span>[top]==<span class="string">'['</span>) || (s[i]==<span class="string">'}'</span>&&<span class="built_in">stack</span>[top]==<span class="string">'{'</span>) ) <span class="comment">//右括号比对</span></span><br><span class="line"> top--;</span><br><span class="line"> <span class="keyword">else</span> <span class="comment">//比对失败</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>( <span class="built_in">stack</span>!= <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">free</span>(<span class="built_in">stack</span>);</span><br><span class="line"> <span class="built_in">stack</span> = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>( top == <span class="number">0</span> ) <span class="comment">//字符串结束且栈空</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="LeetCode-150-逆波兰表达式求值"><a href="#LeetCode-150-逆波兰表达式求值" class="headerlink" title="LeetCode 150 逆波兰表达式求值"></a>LeetCode 150 逆波兰表达式求值</h3><h4 id="思路-1"><a href="#思路-1" class="headerlink" title="思路"></a>思路</h4><p>写个循环遍历字符串<br>碰到数字就压进栈<br>遇到运算符就把栈顶的两个元素弹出来<br>然后算了之后的结果再压进去<br>直到最后把栈里的最后一个元素输出即可</p><h4 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h4><p>这题真的很迷,每次运行都报数组越界,无奈只好放弃,就把有 bug 的源代码贴上来好了<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%8A%A5%E9%94%99%E4%BF%A1%E6%81%AF.png" alt="报错信息"></p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">evalRPN</span><span class="params">(<span class="keyword">char</span> ** tokens, <span class="keyword">int</span> tokensSize)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> <span class="built_in">stack</span>[<span class="number">10000</span>], top = <span class="number">0</span>;<span class="comment">//使用数组实现栈</span></span><br><span class="line"> <span class="keyword">int</span> num1, num2;</span><br><span class="line"> <span class="keyword">int</span> i;</span><br><span class="line"> <span class="keyword">for</span>(i = <span class="number">0</span>; i < tokensSize; i++){</span><br><span class="line"> <span class="keyword">if</span>(tokens[i] == <span class="string">'+'</span> || tokens[i] == <span class="string">'-'</span> || tokens[i] == <span class="string">'*'</span> || tokens[i] == <span class="string">'/'</span>){<span class="comment">//如果碰到运算符</span></span><br><span class="line"> num2 = <span class="built_in">stack</span>[--top]; <span class="comment">//将栈中的顶部两元素pop</span></span><br><span class="line"> num1 = <span class="built_in">stack</span>[--top];<span class="comment">//先出栈的是除数/减数,后出栈的是被除数/除数</span></span><br><span class="line"> <span class="keyword">switch</span>(tokens[i][<span class="number">0</span>]){<span class="comment">//用switch_case语句确定不同运算情况</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'-'</span> :</span><br><span class="line"> <span class="built_in">stack</span>[top++] = num1 - num2;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'+'</span> :</span><br><span class="line"> <span class="built_in">stack</span>[top++] = num1 + num2;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> case '* ' :</span><br><span class="line"> <span class="built_in">stack</span>[top++] = num1 * num2;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'/'</span> :</span><br><span class="line"> <span class="built_in">stack</span>[top++] = num1 / num2;</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">else</span>{</span><br><span class="line"> <span class="built_in">stack</span>[top++] = atoi(tokens[i]);<span class="comment">//将字符串中的字符转化为数字push进栈</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">stack</span>[--top];<span class="comment">//将最终结果返回</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h1><p>队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。</p><h2 id="队列的实现"><a href="#队列的实现" class="headerlink" title="队列的实现"></a>队列的实现</h2><h3 id="队列的数据结构定义"><a href="#队列的数据结构定义" class="headerlink" title="队列的数据结构定义"></a>队列的数据结构定义</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <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="keyword">int</span> data;</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">}Node,*DNode;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Queue</span>//指针域</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> DNode front;</span><br><span class="line"> DNode rear;</span><br><span class="line">}Queue,*DQueue;</span><br></pre></td></tr></table></figure><h3 id="队列的初始化函数"><a href="#队列的初始化函数" class="headerlink" title="队列的初始化函数"></a>队列的初始化函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">initQueue</span><span class="params">(DQueue Q)</span></span>{</span><br><span class="line"> Q=(DQueue)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Queue));<span class="comment">//申请队节点的内存空间</span></span><br><span class="line"> Q->front=Q->rear=<span class="literal">NULL</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="队列的入队操作函数"><a href="#队列的入队操作函数" class="headerlink" title="队列的入队操作函数"></a>队列的入队操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inQueue</span><span class="params">(DQueue Q,<span class="keyword">int</span> n)</span></span>{</span><br><span class="line"> DNode q;</span><br><span class="line"> q=(DNode)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Node)); <span class="comment">//申请队节点的内存空间</span></span><br><span class="line"> q->next=<span class="literal">NULL</span>;</span><br><span class="line"> q->data=n;将值放入节点中</span><br><span class="line"> <span class="keyword">if</span>(Q->rear==<span class="literal">NULL</span>) <span class="comment">//判断如果队空,则插入结点为队首结点</span></span><br><span class="line"> Q->front=Q->rear=q;</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">//若队不为空,尾指针所指的结点的next连接上q,同时后移尾指针</span></span><br><span class="line"> Q->rear->next=q;</span><br><span class="line"> Q->rear=q;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>由于队的存储空间大小是动态变化的,因此我们不需要判断队列是否已满。</p><h3 id="队列的出队操作函数"><a href="#队列的出队操作函数" class="headerlink" title="队列的出队操作函数"></a>队列的出队操作函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">outQueue</span><span class="params">(DQueue Q,<span class="keyword">int</span> x)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(Q->front==<span class="literal">NULL</span> || Q->rear==<span class="literal">NULL</span>) <span class="comment">//判断队列是否为空</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> DNode q;</span><br><span class="line"> <span class="comment">//出队结点是front指向的结点,用一个结点q存储和释放出队结点</span></span><br><span class="line"> q=Q->front;</span><br><span class="line"> <span class="comment">//判断是否仅有一个结点</span></span><br><span class="line"> <span class="keyword">if</span>(Q->front==Q->rear)</span><br><span class="line"> Q->front=Q->rear=<span class="literal">NULL</span>;<span class="comment">//仅有一节点则该元素出队后队列即空</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> Q->front=Q->front->next;<span class="comment">//将front指针指向下一个节点</span></span><br><span class="line"> x=q->data;</span><br><span class="line"> <span class="built_in">free</span>(q); <span class="comment">//释放节点内存空间</span></span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="队列的取队尾元素函数"><a href="#队列的取队尾元素函数" class="headerlink" title="队列的取队尾元素函数"></a>队列的取队尾元素函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">getFront</span><span class="params">(DQueue Q)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(Q->front==<span class="literal">NULL</span> || Q->rear==<span class="literal">NULL</span>) <span class="comment">//判断队列是否为空</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">return</span> Q->front->data;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="队列的应用"><a href="#队列的应用" class="headerlink" title="队列的应用"></a>队列的应用</h2><h3 id="LeetCode-21-合并两个有序链表"><a href="#LeetCode-21-合并两个有序链表" class="headerlink" title="LeetCode 21 合并两个有序链表"></a>LeetCode 21 合并两个有序链表</h3><h4 id="思路-2"><a href="#思路-2" class="headerlink" title="思路"></a>思路</h4><p>定义一个新链表。<br>比较两个原链表中元素的大小,并把它放到新链表中。<br>然后再把放入了新元素的链表的指针向后移一个节点,没有放入新元素的链表的指针不动。<br>如果一个链表指针已经指向队尾,那么就把另外一个链表之后的所有节点原封不动全部放入新链表中。</p><h4 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * struct ListNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * struct ListNode *next;</span></span><br><span class="line"><span class="comment"> * };</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function">struct ListNode* <span class="title">mergeTwoLists</span><span class="params">(struct ListNode* l1, struct ListNode* l2)</span></span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ListNode</span> <span class="title">output</span>;</span></span><br><span class="line"> output.next = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ListNode</span> * <span class="title">ear</span> = &<span class="title">output</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ListNode</span> * <span class="title">node</span> = <span class="title">NULL</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(l1 && l2){</span><br><span class="line"> rear->next = l1->val < l2->val ? l1 : l2;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(l1->val < l2->val){</span><br><span class="line"> l1 = l1->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> l2 = l2->next;</span><br><span class="line"> }</span><br><span class="line"> rear = rear->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(l1){</span><br><span class="line"> rear->next = l1;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(l2){</span><br><span class="line"> rear->next = l2;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> output.next;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="LeetCode-2-两数相加"><a href="#LeetCode-2-两数相加" class="headerlink" title="LeetCode 2 两数相加"></a>LeetCode 2 两数相加</h3><h4 id="思路-3"><a href="#思路-3" class="headerlink" title="思路"></a>思路</h4><p><del>懒得写了</del>全部写在代码的注释当中……</p><h4 id="代码-3"><a href="#代码-3" class="headerlink" title="代码"></a>代码</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * struct ListNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * struct ListNode *next;</span></span><br><span class="line"><span class="comment"> * };</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> value = <span class="number">0</span>;<span class="comment">//使用全局变量,来实现进位</span></span><br><span class="line"></span><br><span class="line"><span class="function">struct ListNode* <span class="title">addTwoNumbers</span><span class="params">(struct ListNode* l1, struct ListNode* l2)</span></span>{<span class="comment">//使用递归将链表各个对应的元素相加</span></span><br><span class="line"> <span class="keyword">if</span>(l1==<span class="literal">NULL</span>&&l2==<span class="literal">NULL</span>&&value==<span class="number">0</span>){<span class="comment">//判断输入的链表节点是否已经超过的链表的末尾,是否还有没存入最终输出链表的元素</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(l1!=<span class="literal">NULL</span>){</span><br><span class="line"> value+=l1->val;<span class="comment">//如果当前链表节点存在,则把它的值取出来加到value上</span></span><br><span class="line"> l1=l1->next;<span class="comment">//使得指针指向下一个节点</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> value+=<span class="number">0</span>;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">if</span>(l2!=<span class="literal">NULL</span>){</span><br><span class="line"> value+=l2->val;</span><br><span class="line"> l2=l2->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> value+=<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ListNode</span> *<span class="title">outList</span>=(<span class="title">struct</span> <span class="title">ListNode</span> *)<span class="title">malloc</span>(<span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">ListNode</span>));</span><span class="comment">//开辟数组空间储存当前的链表节点</span></span><br><span class="line"> outList->val=value%<span class="number">10</span>;<span class="comment">//使用取模计算符保证val的值是value的个位数部分</span></span><br><span class="line"> value=value/<span class="number">10</span>;<span class="comment">//使用整除来保存value中需要进位的十位数的值</span></span><br><span class="line"> outList->next=addTwoNumbers(l1,l2);</span><br><span class="line"> <span class="keyword">return</span> outList;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="还有两道困难题……"><a href="#还有两道困难题……" class="headerlink" title="还有两道困难题……"></a>还有两道困难题……</h1><p>就先咕为敬了,告辞<br>有空再补<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1571031606850.gif" alt="咕咕咕"></p>]]></content>
<summary type="html">
<p>鲸了!数据结构居然如此复杂!</p>
</summary>
<category term="C" scheme="http://blog.zam-meow.cn/tags/C/"/>
<category term="Learning" scheme="http://blog.zam-meow.cn/tags/Learning/"/>
<category term="Stack" scheme="http://blog.zam-meow.cn/tags/Stack/"/>
<category term="Queue" scheme="http://blog.zam-meow.cn/tags/Queue/"/>
<category term="Data Structure" scheme="http://blog.zam-meow.cn/tags/Data-Structure/"/>
</entry>
<entry>
<title>Linux初体验</title>
<link href="http://blog.zam-meow.cn/2019/11/02/Linux%E5%88%9D%E4%BD%93%E9%AA%8C/"/>
<id>http://blog.zam-meow.cn/2019/11/02/Linux%E5%88%9D%E4%BD%93%E9%AA%8C/</id>
<published>2019-11-02T01:50:17.000Z</published>
<updated>2020-04-16T02:15:26.404Z</updated>
<content type="html"><![CDATA[<p>用 VMware 安装Linux虚拟机,还可以顺带做网安组的 Task,美滋滋。</p><a id="more"></a><h1 id="安装准备"><a href="#安装准备" class="headerlink" title="安装准备"></a>安装准备</h1><h2 id="VMware-的安装"><a href="#VMware-的安装" class="headerlink" title="VMware 的安装"></a>VMware 的安装</h2><p>下载地址:<a href="https://www.nocmd.com/740.html" target="_blank" rel="noopener">https://www.nocmd.com/740.html</a><br>安装无脑下一步即可</p><h2 id="Arch-Linux-镜像"><a href="#Arch-Linux-镜像" class="headerlink" title="Arch Linux 镜像"></a>Arch Linux 镜像</h2><p>下载地址:<a href="http://mirrors.zju.edu.cn/archlinux/iso/2019.10.01/" target="_blank" rel="noopener">http://mirrors.zju.edu.cn/archlinux/iso/2019.10.01/</a> (浙大源)<br>下载链接地址中的 archlinux-2019.10.01-x86_64.iso<br>若此地址下载缓慢,也可以进入官方的下载源集合:<a href="https://www.archlinux.org/download/" target="_blank" rel="noopener">https://www.archlinux.org/download/</a></p><h1 id="开始安装"><a href="#开始安装" class="headerlink" title="开始安装"></a>开始安装</h1><h2 id="VMware-配置"><a href="#VMware-配置" class="headerlink" title="VMware 配置"></a>VMware 配置</h2><p>打开 VMware,首先点击文件选项 -> 新建虚拟机 -> 典型 -> 稍后安装操作系统 -> 选择 Linux 选项 -> 版本选择‘Linux4.x’,内存按需分配就可以,硬盘分配 40G 左右,根据自己需求来定,毕竟 Arch 一开始还算一个比较干净简洁轻量的 Linux release。网络类型选择 NAT,其他默认即可。<br>CD/DVD 选项记得选择 ArchLinux 镜像。</p><h2 id="Arch-Linux-安装准备"><a href="#Arch-Linux-安装准备" class="headerlink" title="Arch Linux 安装准备"></a>Arch Linux 安装准备</h2><h3 id="启动安装-archlinux"><a href="#启动安装-archlinux" class="headerlink" title="启动安装 archlinux"></a>启动安装 archlinux</h3><p>不论你使用的是 EFI 或 BIOS 引导,均选择 Boot Arch Linux(x86_64)这一选项<br>启动成功后就会进入命令行模式,此时如果你不清楚你用的是 EFI 引导还是 BIOS 引导,可以在此处列出 efivars 目录以验证启动模式来判断主板是以何种方式引导系统的(这对之后对硬盘的分区 <strong>十分有用</strong>):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls /sys/firmware/efi/efivars</span><br></pre></td></tr></table></figure><p>若该目录不存在,系统就可能以 BIOS 模式启动。</p><h3 id="确认网络连接情况"><a href="#确认网络连接情况" class="headerlink" title="确认网络连接情况"></a>确认网络连接情况</h3><p>如果你使用的是有线网络连接方式,那么 Arch Linux 在启动后,守护进程 dhcpcd 已被默认启用以探测有线设备,因此你只需验证网络是否正常即可<br>如果你使用的是无线网络连接方式,<br>Arch Linux 的安装必须使用网络才能完成,使用下面命令以验证网络是否正常:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ping -c 4 www.baidu.com</span><br></pre></td></tr></table></figure><p>如果网络不正常,可能是由于 dhcp 服务没有开启,可以使用以下命令来开启此服务:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl enable dhcpcd.service</span><br></pre></td></tr></table></figure><h3 id="更新系统时间"><a href="#更新系统时间" class="headerlink" title="更新系统时间"></a>更新系统时间</h3><p>首先还是验证一下系统的时间是否正常:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">timedatectl status</span><br></pre></td></tr></table></figure><p>如果时间和当前时间对不上的话,使用下面命令来更新系统时间:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">timedatectl set-ntp true</span><br></pre></td></tr></table></figure><h3 id="建立硬盘分区"><a href="#建立硬盘分区" class="headerlink" title="建立硬盘分区"></a>建立硬盘分区</h3><p>硬盘如果被系统识别到,就会被分配为一个块设备,如/dev/sda;因此先查看一下硬盘的状态,以便于后续分区操作:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lsblk</span><br></pre></td></tr></table></figure><p>屏幕显示如下:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/lsblk%E8%BE%93%E5%87%BA.jng" alt="lsblk输出"><br>这里 sda 即是我分配给虚拟机的 8GB 硬盘,因为此硬盘下还没有分区,所以 sda 节点下无任何显示;loop0 和 sr0 可以忽略。如果硬盘已经有分区,sda 节点下应当会显示如下图:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%88%86%E5%8C%BA%E5%AE%8C%E6%AF%95%E5%90%8E%E6%98%BE%E7%A4%BA.png" alt="分区完毕后显示"><br>接下来我们要对这 8GB 的硬盘进行分区,能够创建分区的命令很多,如 fdisk,parted,cfdisk 等,这里使用 GUI 的 cfdisk 命令(在真机上分区时,请认真检查你的硬盘是否选择正确,如果你有多个硬盘,可能你要用来安装 Linux 的硬盘并不是如下所写的/dev/sda,而是/dev/sdb 也说不定。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cfdisk /dev/sda</span><br></pre></td></tr></table></figure><p>对于一个硬盘,以下三个分区是必须要有的:<br>··· 一个根分区(挂载在根目录) /<br>··· 如果 UEFI 模式被启用,你还需要一个 EFI 系统分区,或者是通过 BIOS 启动,那么你需要建立的是 BIOS boot 分区<br>··· Swap 可以在一个独立的分区上设置,也可以直接建立交换文件<br>其中,Swap 分区的大小应与你物理机/虚拟机的内存大小相同,EFI 分区通常为 300M,BIOS boot 分区通常为 1G</p><p>具体 cfdisk 的使用命令,可以参考这篇文章:<a href="https://jingyan.baidu.com/article/ce09321bb922da2bff858fdd.html" target="_blank" rel="noopener">https://jingyan.baidu.com/article/ce09321bb922da2bff858fdd.html</a></p><p>分好区后确认写入分区到硬盘,然后退出分区工具,再次使用 lsblk 查看一下,显示如下图:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E7%A1%AC%E7%9B%98%E5%88%86%E5%8C%BA.png" alt="硬盘分区"><br>那么你就成功分好区了</p><h3 id="格式化分区"><a href="#格式化分区" class="headerlink" title="格式化分区"></a>格式化分区</h3><p>分区完成后,需要对分区做格式化处理,如果你使用了 EFI 分区,因为 EFI 分区需要 FAT32 文件格式,所以需要将其格式化为 FAT32 格式;<br>EFI 引导分区推荐大小为 512M。<br>在这里我使用的是 BIOS 引导,但为了方便起见,我也将它格式化成 FAT32 文件格式。<br>根分区格式化为 ext4 格式;设置并开启 Swap 分区:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">mkfs.fat -F32 /dev/sda2</span><br><span class="line">mkfs.ext4 /dev/sda3</span><br><span class="line">mkfs.ext4 /dev/sda4</span><br><span class="line">mkswap /dev/sda1 -L Swap</span><br><span class="line">swapon /dev/sda1</span><br></pre></td></tr></table></figure><h3 id="挂载分区"><a href="#挂载分区" class="headerlink" title="挂载分区"></a>挂载分区</h3><h4 id="EFI-模式引导"><a href="#EFI-模式引导" class="headerlink" title="EFI 模式引导"></a>EFI 模式引导</h4><p>格式化完成后,需要将分区挂载到 /mnt ,先挂载根分区(这里是/dev/sda2);再挂载 EFI 分区(这里是/dev/sda1),挂载 EFI 分区时,需要在/mnt 上先创建 boot/EFI 目录,然后将 EFI 分区挂载到/mnt/boot/EFI 上;Sawp 分区不需要挂载:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mount /dev/sda3 /mnt</span><br><span class="line">mkdir -p /mnt/boot/EFI</span><br><span class="line">mount /dev/sda2 /mnt/boot/EFI</span><br></pre></td></tr></table></figure><h4 id="BIOS-模式引导"><a href="#BIOS-模式引导" class="headerlink" title="BIOS 模式引导"></a>BIOS 模式引导</h4><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">mount /dev/sda4 /mnt 注:sda4挂载为根</span><br><span class="line">mkdir /mnt/boot 注:在 / 分区中创建/boot文件夹</span><br><span class="line">mkdir /mnt/home 注:在 / 分区中创建home文件夹</span><br><span class="line">mount /dev/sda2 /mnt/boot 注:将sda2分区挂载到/mnt/boot文件夹内</span><br><span class="line">mount /dev/sda3 /mnt/home 注:将sda3分区挂载到/mnt/home文件夹内</span><br></pre></td></tr></table></figure><h2 id="安装基本系统"><a href="#安装基本系统" class="headerlink" title="安装基本系统"></a>安装基本系统</h2><h3 id="选择软件镜像源"><a href="#选择软件镜像源" class="headerlink" title="选择软件镜像源"></a>选择软件镜像源</h3><p>在安装基本系统之前,需要修改一下软件镜像源,不然安装基本系统时会安装不上。镜像源列表在 /etc/pacman.d/mirrorlist 文件中。</p><p>我们选择软件镜像源时,最好选择国内的镜像源,因为国内网络环境的关系,选择其他国家或地区的镜像源,安装时可能很慢或失败也不一定。</p><p>下面这段代码首先添加了阿里巴巴镜像源到一个新文件(此处为 mrlist),然后从 mirrolist 文件中选出所有国内镜像源追加到 mrlist 中,然后将 mirrorlist 文件的内容追加在 mrlist 的最后面,最后将 mrlist 重命名为 mirrorlsit:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">echo '## China\nServer = http://mirrors.aliyun.com/archlinux/$repo/os/$arch' > mrlist</span><br><span class="line">grep -A 1 'China' /etc/pacman.d/mirrorlist|grep -v '\-\-' >> mrlist</span><br><span class="line">cat /etc/pacman.d/mirrorlist >> mrlist</span><br><span class="line">mv mrlist /etc/pacman.d/mirrorlist</span><br></pre></td></tr></table></figure><p>执行完以上命令后,可以使用以下命令来查看 mirrorlist 文件是否修改成功:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano /etc/pacman.d/mirrorlist</span><br></pre></td></tr></table></figure><p>按下 Ctrl+X 退出查看<br>若修改成功,会看到 mirrorlist 文件中的开头的内容全是国内的镜像源</p><h3 id="开始安装系统"><a href="#开始安装系统" class="headerlink" title="开始安装系统"></a>开始安装系统</h3><p>修改完软件镜像源后,然后就可以开始安装系统了:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pacstrap -i /mnt base base-devel vim linux linux-firmware</span><br></pre></td></tr></table></figure><p><strong>注意,在安装环节就需要安装 linux 和 linux-firmware 两个包,不然会导致内核没有安装导致 grub 引导失败进不去系统</strong><br>使用-i 选项会在实际安装前进行确认;安装 base-devel 组,可以让我们通过 AUR (简体中文) 或者 ABS (简体中文) 编译安装软件包,如果不需要通过 AUR 或 ABS 安装软件包,则只需要安装 base 组就可以了 。</p><h2 id="配置系统"><a href="#配置系统" class="headerlink" title="配置系统"></a>配置系统</h2><h3 id="Fstab"><a href="#Fstab" class="headerlink" title="Fstab"></a>Fstab</h3><p>Linux 的文件结构是单个的树状结构。最顶部的为根目录,即/。在根目录下,分为多个子目录,包括/bin、/boot、/dev、/etc、/home、/lib、/media、/mnt、/opt、/proc、/root、/sbin、/tmp、/usr 和/var 等。<br>磁盘 Linux 分区都必须挂载到目录树中的某个具体的目录上才能进行读写操作,而 fstab 正是负责这一配置。</p><p>因此,在基本系统安装完成后,用以下命令生成 fstab 文件 (用 -U 或 -L 选项设置 UUID 或卷标):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">genfstab -U /mnt >> /mnt/etc/fstab</span><br></pre></td></tr></table></figure><p>然后使用以下命令检查一下生成的 fstab 文件是否正确:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano /mnt/etc/fstab</span><br></pre></td></tr></table></figure><p>如果生成的 fstab 文件正确,会看到之前分的 4 个分区的信息。</p><h3 id="Chroot"><a href="#Chroot" class="headerlink" title="Chroot"></a>Chroot</h3><p>chroot 命令用来在指定的根目录下运行指令<br>切换到新安装的系统:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch-chroot /mnt</span><br></pre></td></tr></table></figure><p>chroot 之后,当前目录就变成为 / 。此步会自动进行创建初始的 ramdisk 环境,但是如果以后更改了内核配置了的话,最好使用一下命令再重新生成 ramdisk 环境:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkinitcpio -p linux</span><br></pre></td></tr></table></figure><h3 id="配置时区"><a href="#配置时区" class="headerlink" title="配置时区"></a>配置时区</h3><p>将系统时区设为东八区:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ln -sf /usr/share/zoneinfo/Asia/Chongqing /etc/localtime</span><br></pre></td></tr></table></figure><p>设置时间标准为 UTC,并调整时间漂移:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hwclock --systohc --utc</span><br></pre></td></tr></table></figure><h3 id="配置-Locale"><a href="#配置-Locale" class="headerlink" title="配置 Locale"></a>配置 Locale</h3><p>locale 文件对系统的使用地区和语言等进行配置。在/etc/locale.gen 文件中进行配置。<br>locale.gen 是一个仅包含注释文档的文本文件。指定需要的本地化类型,只需移除对应行前面的注释符号(#)即可,使用下面命令打开 locale.gen 文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano /etc/locale.gen</span><br></pre></td></tr></table></figure><p>然后找到下面 2 项,去掉每项前面的#即可:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">en_US.UTF-8 UTF-8</span><br><span class="line">zh_CN.UTF-8 UTF-8</span><br></pre></td></tr></table></figure><p>使用 locale-gen 命令生成 Locale 信息,并列出所有启用的 Locale:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">locale-gen</span><br><span class="line">locale -a</span><br></pre></td></tr></table></figure><p>最后创建 locale.conf 文件,并提交所要使用的本地化选项,然后使用 locale 命令显示当前正在使用的 Locale 和相关的环境变量:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">echo LANG=en_US.UTF-8 > /etc/locale.conf</span><br><span class="line">locale</span><br></pre></td></tr></table></figure><p>/etc/locale.conf 用来配置整个系统所使用的 Loacle,而这也可以由用户通过用户自己的 <del>/.config/locale.conf (</del>表示当前用户的 Home 目录)来覆盖整个系统的 Locale 配置。<br>建立 /etc/skel/.config/locale.conf 文件,可以在新用户的建立(新用户的建立见下文)且同时创建用户主目录(useradd -m)时,自动应用其中的 Locale(会将此文件复制到新建用户的 ~/.config/locale.conf 中)。<br><em>不推荐此时设置任何中文 locale,因为这样做可能会导致 tty 显示乱码。</em></p><h3 id="设置主机名"><a href="#设置主机名" class="headerlink" title="设置主机名"></a>设置主机名</h3><p>要设置主机名,创建 /etc/hostname 文件并将主机名写入该文件即可。我的主机名为 Zam-laptop:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo Zam-laptop > /etc/hostname</span><br></pre></td></tr></table></figure><p>然后配置主机名对应的 IP 到 /etc/hosts 中:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano /etc/hosts</span><br></pre></td></tr></table></figure><p>将其中的主机名改为你自己的主机名(我这里是 Zam-laptop):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1 localhost.localdomain localhost</span><br><span class="line">::1 localhost.localdomain localhost</span><br><span class="line">127.0.1.1 Zam-laptop.localdomain Zam-laptop</span><br></pre></td></tr></table></figure><h3 id="网络配置"><a href="#网络配置" class="headerlink" title="网络配置"></a>网络配置</h3><p>若使用有线网络的话,由于在 Base 包里已经不包括联网所需的程序,所以需要下载 dhcp 客户端:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pacman -S dhcpcd</span><br></pre></td></tr></table></figure><p>若使用无线网络的话,则安装以下几个软件包(因为我使用的是虚拟机,并未验证过):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pacman -S iw wpa_supplicant dialog</span><br></pre></td></tr></table></figure><h3 id="设置-Root-用户密码"><a href="#设置-Root-用户密码" class="headerlink" title="设置 Root 用户密码"></a>设置 Root 用户密码</h3><p>设置 root 密码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">passwd</span><br></pre></td></tr></table></figure><p>然后输入两次密码即可。</p><h3 id="创建新用户"><a href="#创建新用户" class="headerlink" title="创建新用户"></a>创建新用户</h3><p>因为使用 root 用户登陆后,root 用户拥有系统的所有操作权限,这样对系统的操作非常不安全(如一不小心删库,你就要开始跑路),所以需要新建一个普通用户,让其对系统的操作受到一定限制,使用下面命令新建用户 zam:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">useradd -m -G wheel -s /bin/bash zam</span><br></pre></td></tr></table></figure><p>命令解释:<br>-m:创建用户主目录(/home/[用户名])<br>-G:用户要加入的附加组列表;此处将用户加到 wheel 组中,之后可以给这个组执行 sudo 命令的权限<br>-s:指定了用户默认登录 shell 的路径,此处设置为 bash 的路径<br>更多创建新用户的使用请查看官方 Arch Linux Wiki:<a href="https://wiki.archlinux.org/index.php/Users_and_groups_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)" target="_blank" rel="noopener">https://wiki.archlinux.org/index.php/Users_and_groups_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)</a></p><p>然后修改新创建用户的用户密码,和修改 Root 用户密码所使用的命令一样(只是需要指定要修改密码的用户名):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">passwd zam</span><br></pre></td></tr></table></figure><p>然后输入两次密码即可。</p><p>以后大部分时间我们都将使用此普通用户来工作,但由于此用户的操作权限有限,有时会对很多操作带来不便,因此需要给该用户在某些情况下提权,这就需要允许该用户所在的 wheel 组有执行 sudo 命令的权限,此时需要修改 /etc/sudoers 文件 ,但请不要直接修改此文件,而是用下面的命令修改:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">visudo</span><br></pre></td></tr></table></figure><p>使用上面命令打开 sudoers 文件后,删除 wheel 组前面的注释(#)即可:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">## Uncomment to allow members of group wheel to execute any command</span><br><span class="line">%wheel ALL=(ALL) ALL</span><br></pre></td></tr></table></figure><p>若执行 visudo 时,提示找不到 vim,则请先安装 vim 后在执行上面的操作,执行下面指令安装 vim:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pacman -S vim</span><br></pre></td></tr></table></figure><h3 id="安装-grub"><a href="#安装-grub" class="headerlink" title="安装 grub"></a>安装 grub</h3><p>grub 是一个启动引导器,同时支持 EFI 和 BIOS 方式的启动。若使用的 UEFI 方式引导系统,则还需要安装 efibootmgr,如果是双系统的话,还需要安装 os-prober,且如果使用 Intel CPU 的话,则需要安装 intel-ucode 并启用 <a href="https://wiki.archlinux.org/index.php/Microcode_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87" target="_blank" rel="noopener">因特尔微码更新</a></p><p><strong>嘤特尔微码更新并不是一定需要开启</strong></p><ul><li>微码<blockquote><p>微码(microcode)就是由 Intel/AMD 提供的 CPU 固件。Linux 的内核可以在引导时更新 CPU 固件,而无需 BIOS 更新。处理器的微码保存在内存中,在每次启动系统时,内核可以更新这个微码。</p></blockquote></li></ul><blockquote><p>这些来自 Intel/AMD 的微码的更新可以去修复 bug 或者使用补丁来防范 bug。例如前段时间爆出的<a href="https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV180002" target="_blank" rel="noopener">幽灵(Spectre)与熔断(Meltdown)</a>漏洞,就可以通过更新微码来解决</p></blockquote><ul><li>因此,最好还是启用因特尔微码更新,以保证自己的数据安全</li></ul><p>因为我使用的是虚拟机和 BIOS 引导方式,因此只需要安装 grub:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pacman -S grub</span><br></pre></td></tr></table></figure><p>然后,还需要将其安装到 BIOS boot 分区当中:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grub-install --recheck /dev/sda</span><br></pre></td></tr></table></figure><p><strong>注意</strong>:此处的 /dev/sda 后没有数字。<br>最后还需要生成一个 grub 的配置文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grub-mkconfig -o /boot/grub/grub.cfg</span><br></pre></td></tr></table></figure><h3 id="重启"><a href="#重启" class="headerlink" title="重启"></a>重启</h3><p>执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">exit #退出chroot环境,切换到光盘系统</span><br><span class="line">reboot #重启系统</span><br></pre></td></tr></table></figure><p>然后你就可以进入到 grub 的引导界面,选择 Arch Linux,enjoy it!</p><h1 id="进阶安装"><a href="#进阶安装" class="headerlink" title="进阶安装"></a>进阶安装</h1><p>在重启之后,我们就已经成功地安装了 ArchLinux,但是这时系统处于一个非常精简的状态,为了日常使用,我们必须安装一些需要的组件,来完善我们的系统功能。</p><h2 id="预先准备:启动-DHCP-服务"><a href="#预先准备:启动-DHCP-服务" class="headerlink" title="预先准备:启动 DHCP 服务"></a>预先准备:启动 DHCP 服务</h2><p>启动 dhcp 服务:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl enable dhcpcd.service</span><br></pre></td></tr></table></figure><p>以确保后续的下载软件包操作能够正常进行</p><h2 id="安装图形界面"><a href="#安装图形界面" class="headerlink" title="安装图形界面"></a>安装图形界面</h2><h3 id="安装-Xorg"><a href="#安装-Xorg" class="headerlink" title="安装 Xorg"></a>安装 Xorg</h3><p>Xorg 是 Linux 下的一个著名的开源图形服务,我们的桌面环境需要 Xorg 的支持。<br>执行以下命令安装 Xorg 及相关组件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pacman -S xorg</span><br></pre></td></tr></table></figure><h3 id="安装-Deepin-Desktop-Environment(DDE)"><a href="#安装-Deepin-Desktop-Environment(DDE)" class="headerlink" title="安装 Deepin Desktop Environment(DDE)"></a>安装 Deepin Desktop Environment(DDE)</h3><p>作为国产的桌面操作环境,当然要滋磁一波<br>执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pacman -S deepin deepin-extra lightdm lightdm-deepin-greeter</span><br></pre></td></tr></table></figure><h3 id="安装其余实用软件"><a href="#安装其余实用软件" class="headerlink" title="安装其余实用软件"></a>安装其余实用软件</h3><p>deepin 还提供了解压、下载工具等实用工具的下载<br>执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo pacman -S file-roller evince gedit thunderbird gpicview</span><br><span class="line">sudo pacman -S unrar unzip p7zip</span><br></pre></td></tr></table></figure><h3 id="安装桌面管理器-sddm"><a href="#安装桌面管理器-sddm" class="headerlink" title="安装桌面管理器 sddm"></a>安装桌面管理器 sddm</h3><p><em>经过某李姓学长提醒,了解了 sddm 和 lightdm 都是图形化的桌面管理器,因此若上面安装 deepin 时已经安装了 lightdm,接下来就不需要安装桌面管理器 sddm 了</em></p><p>安装好了桌面环境包以后,我们需要安装一个图形化的桌面管理器来帮助我们登录并且选择我们使用的桌面环境,sddm 就是这样的一款管理器。<br>执行以下命令来安装 sddm:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pacman -S sddm</span><br></pre></td></tr></table></figure><h3 id="设置开机启动-sddm-服务"><a href="#设置开机启动-sddm-服务" class="headerlink" title="设置开机启动 sddm 服务"></a>设置开机启动 sddm 服务</h3><p>使用 systemctl 命令:<code>sudo systemctl enable sddm</code>来启用 sddm 开机自启<br>设置 lightdm 开机自启同理</p><h3 id="配置网络"><a href="#配置网络" class="headerlink" title="配置网络"></a>配置网络</h3><p>到现在我们已经安装好了桌面环境,但是还有一件事情需要我们提前设置一下。由于我们之前使用的一直都是 netctl 这个自带的网络服务,而桌面环境使用的是 NetworkManager 这个网络服务,所以我们需要禁用 netctl 并启用 NetworkManager:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl disable netctl</span><br><span class="line">sudo systemctl enable NetworkManager</span><br></pre></td></tr></table></figure><p>同时你需要安装工具栏工具来显示网络设置图标:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pacman -S network-manager-applet</span><br></pre></td></tr></table></figure><h3 id="安装完成"><a href="#安装完成" class="headerlink" title="安装完成"></a>安装完成</h3><p>重启之后就可以尽情地 enjoy 了</p><h2 id="下载编译安装最新最鬼酷的-Linux-内核"><a href="#下载编译安装最新最鬼酷的-Linux-内核" class="headerlink" title="下载编译安装最新最鬼酷的 Linux 内核"></a>下载编译安装最新最鬼酷的 Linux 内核</h2><p>注:为了完成之后的 Patch 任务,我这里就下载 5.3 版本的内核。下载最新版的内核同理。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85%E5%86%85%E6%A0%B8%E4%B9%8B%E5%89%8D.png" alt="编译安装内核之前"></p><h3 id="下载内核"><a href="#下载内核" class="headerlink" title="下载内核"></a>下载内核</h3><p>首先,我们当然要先去把内核下载到虚拟机中</p><p>由于某些特殊原因,国内访问国外的 Kernel.org 速度偏慢<br>那么就需要从国内的开源镜像站去下载内核<br>地址:<a href="https://mirrors.tuna.tsinghua.edu.cn/kernel/" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/kernel/</a></p><p>然后根据自己的喜好找到自己需要的内核,使用 wget 命令下载,并将其解压</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.3.tar.xz</span><br><span class="line">xz -d linux-5.3.tar.xz</span><br><span class="line">tar -xvf linux-5.3.tar</span><br></pre></td></tr></table></figure><h3 id="编译内核"><a href="#编译内核" class="headerlink" title="编译内核"></a>编译内核</h3><p>进入解压好的源码的根目录下,如果需要自定义选项,就执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make menuconfig</span><br></pre></td></tr></table></figure><p>会进入一个菜单,让你定制你自己的内核。<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E5%AE%9A%E5%88%B6Kernel.png" alt="定制Kernel"><br>当然,你也可以使用缺省配置,执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zcat /proc/config.gz > .config #将当前内核的配置文件复制到此处</span><br></pre></td></tr></table></figure><p>并且不要忘记在 General Setup —> 选项中或者复制来的 config 文件的”CONFIG_LOCALVERSION”一行的值来修改内核版本,这样可以避免编译的内核覆盖当前内核文件。<br>这样内核已经配置好了,下面就可以开始编译了<br>编译命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make -j 线程数</span><br></pre></td></tr></table></figure><h3 id="安装内核模块"><a href="#安装内核模块" class="headerlink" title="安装内核模块"></a>安装内核模块</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo make modules_install ##把所有编译好的模块安装到正确的主目录/lib/modules下</span><br></pre></td></tr></table></figure><p>该命令将编译好的模块拷贝至 /lib/modules/<kernel version>-<config local version>,例如 /lib/modules/3.18.28-ARCH。这样,这些模块和那些被你电脑上其他内核使用的模块就独立开来。</p><h3 id="拷贝内核到-boot-目录"><a href="#拷贝内核到-boot-目录" class="headerlink" title="拷贝内核到 /boot 目录"></a>拷贝内核到 /boot 目录</h3><p>内核编译完成后会生成内核的 bzImage (big zImage) 文件,根据系统架构,将此文件复制到 /boot 目录,以 5.3 内核为例:</p><p>32-bit (i686) kernel:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp -v arch/x86/boot/bzImage /boot/vmlinuz-linux53</span><br></pre></td></tr></table></figure><p>64-bit (x86_64) kernel:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp -v arch/x86_64/boot/bzImage /boot/vmlinuz-linux53</span><br></pre></td></tr></table></figure><h3 id="制作初始化内存盘"><a href="#制作初始化内存盘" class="headerlink" title="制作初始化内存盘"></a>制作初始化内存盘</h3><p>自动生成<br>复制和修改 mkinitcpio preset,就能用官方内核一样的方式生成自定义内核的 initramfs 镜像。下面例子中将已有的 preset 复制到 linux53 要使用的文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linux53.preset</span><br></pre></td></tr></table></figure><p>针对定制内核编辑和修改此文件,</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/mkinitcpio.d/linux53.preset</span><br></pre></td></tr></table></figure><p>ALL_kver= 应该和定制内核匹配:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">/etc/mkinitcpio.d/linux53.preset</span><br><span class="line">...</span><br><span class="line">ALL_kver="/boot/vmlinuz-linux53"</span><br><span class="line">...</span><br><span class="line">default_image="/boot/initramfs-linux53.img"</span><br><span class="line">...</span><br><span class="line">fallback_image="/boot/initramfs-linux53-fallback.img"</span><br></pre></td></tr></table></figure><p>用官方内核一样的方式生成 initramfs 镜像:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkinitcpio -p linux53</span><br></pre></td></tr></table></figure><h3 id="拷贝-System-map"><a href="#拷贝-System-map" class="headerlink" title="拷贝 System.map"></a>拷贝 System.map</h3><p>将 System.map 复制到 /boot, 然后创建 /boot/System.map 软链接到 /boot/System.map-YourKernelName:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo cp System.map /boot/System.map-YourKernelName</span><br><span class="line">sudo ln -sf /boot/System.map-YourKernelName /boot/System.map</span><br></pre></td></tr></table></figure><h3 id="更新-grub-配置信息"><a href="#更新-grub-配置信息" class="headerlink" title="更新 grub 配置信息"></a>更新 grub 配置信息</h3><p>使用命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grub-mkconfig -o /boot/grub/grub.cfg</span><br></pre></td></tr></table></figure><p>来把我们刚刚配置好的内核添加到 grub 的启动配置中</p><h3 id="然后-reboot-就可以享受最新(更老)的-kernel-了!"><a href="#然后-reboot-就可以享受最新(更老)的-kernel-了!" class="headerlink" title="然后 reboot 就可以享受最新(更老)的 kernel 了!"></a>然后 reboot 就可以享受最新<del>(更老)</del>的 kernel 了!</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85%E5%86%85%E6%A0%B8%E4%B9%8B%E5%90%8E.PNG" alt="编译安装内核之后"></p><h2 id="Patch-自己的内核"><a href="#Patch-自己的内核" class="headerlink" title="Patch 自己的内核"></a>Patch 自己的内核</h2><p>说实话,一开始看到要 patch 自己的内核,能够给出现象证明你的 patch 有效,我的心就拔凉拔凉的<br>Kernel 里那么多东西,我咋看的过来哪些能改哪些不能改,改了会有什么效果啊我透……<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/29412e0a494585b8.jpg" alt=""><br>后来转念一想,Patch 最显著的现象不就是版本号升级了吗?!<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/90455D42F926AF850A7ED03D05D614AB109D2.jpg" alt=""><br>于是就有了之前反向升级最新内核的操作</p><h3 id="下载-Patch"><a href="#下载-Patch" class="headerlink" title="下载 Patch"></a>下载 Patch</h3><p>地址:<a href="https://mirrors.tuna.tsinghua.edu.cn/kernel/" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/kernel/</a><br>依旧是打开那个熟悉的地址,然后找到自己对应的 patch 包,用 wget 命令下载即可。<br><strong>注意:如果你想跨版本升级,例如说现在是 5.3,想要升级到 5.3.8 的内核,可以一步到位从 5.3 这个大版本用 patch-5.3.8 升级至 5.3.8</strong></p><h3 id="解压-Patch"><a href="#解压-Patch" class="headerlink" title="解压 Patch"></a>解压 Patch</h3><p>使用命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xz -d patch-5.3.1.xz</span><br></pre></td></tr></table></figure><p>即可</p><h3 id="安装-Patch"><a href="#安装-Patch" class="headerlink" title="安装 Patch"></a>安装 Patch</h3><p>将解压出的 Patch 文件放在你想要打 Patch 的源代码文件夹的 Kernel 目录下 <strong>重要</strong></p><p>为确保内核树绝对干净,执行以下命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make clean && make mrproper</span><br></pre></td></tr></table></figure><p>注意:由于 patch 文件中的文件路径包含了它所基于的内核源文件目录的名字(或者像是”a/“和”b/“之类的其它名字)。这很可能和你本地机器上的内核源代码目录的名字不匹配。你应该切换到你的内核源代码目录,并且在打补丁的时候去掉 patch 中文件名字路径的第一个分量(patch 命令的-p1 参数可以完成这个任务)。<br>因此我们在 Kernel 目录下直接执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">patch -p1 < ./patch-5.3.8.patch</span><br></pre></td></tr></table></figure><p>其余的 patch 文件依样画瓢即可</p><h3 id="编译-Patch-后的内核以及之后的步骤"><a href="#编译-Patch-后的内核以及之后的步骤" class="headerlink" title="编译 Patch 后的内核以及之后的步骤"></a>编译 Patch 后的内核以及之后的步骤</h3><p>这个就和之前的差不多,就不再叙说了~</p><h2 id="制作自己的-Patch-文件并打-Patch。"><a href="#制作自己的-Patch-文件并打-Patch。" class="headerlink" title="制作自己的 Patch 文件并打 Patch。"></a>制作自己的 Patch 文件并打 Patch。</h2><p>经过某李姓学长的提醒,我明白了这个任务其实是要我们自己动手修改 kernel 内核。<br>那么最简单的修改方法就是添加一个系统调用。</p><h3 id="修改源程序,增加系统调用实现"><a href="#修改源程序,增加系统调用实现" class="headerlink" title="修改源程序,增加系统调用实现"></a>修改源程序,增加系统调用实现</h3><p>假设你现在已经下好了 kernel 源文件并解压。在解压出的源代码文件夹内执行以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ./kernel/sys.c</span><br></pre></td></tr></table></figure><p>在 sys.c 的末尾加入以下函数</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">asmlinkage <span class="keyword">void</span> <span class="title">sys_helloworld</span><span class="params">(<span class="keyword">void</span>)</span></span>{</span><br><span class="line"> printk(“hello world”);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Zam-0703/Pictures/master/img/%E4%BF%AE%E6%94%B9sys.c-3.png" alt="修改sys.c"></p><h3 id="修改头文件,增加系统调用声明"><a href="#修改头文件,增加系统调用声明" class="headerlink" title="修改头文件,增加系统调用声明"></a>修改头文件,增加系统调用声明</h3><p><strong>注意:我在修改之后的系统调用后才发现 520 已经被占,因此之后图里所有的 520 都被改成了 436,也推荐大家以后新增调用时先去看看 syscall_64.tbl 文件的内容。</strong><br>使用命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ./include/uapi/asm-generic/unistd.h</span><br></pre></td></tr></table></figure><p>来添加系统调用的声明<br>在文件的最末尾处添加如下代码:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%B7%BB%E5%8A%A0%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E5%A3%B0%E6%98%8E.png" alt="添加系统调用声明"><br>再使用命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ./include/linux/syscalls.h</span><br></pre></td></tr></table></figure><p>在文件的最末尾处添加如下代码:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%B7%BB%E5%8A%A0%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E.png" alt="添加函数声明"></p><h3 id="注册系统调用"><a href="#注册系统调用" class="headerlink" title="注册系统调用"></a>注册系统调用</h3><p>进入<code>/arch/x86/entry/syscalls</code>目录<br>可以看到以下内容:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/syacalls%E6%96%87%E4%BB%B6%E5%A4%B9%E5%86%85%E5%AE%B9.png" alt="syacalls文件夹内容"><br>32 位系统就添加到 syscall_32.tbl,64 位就修改 syscall_64.tbl<br>我这里是 64 位,因此我应该修改 syscall_64.tbl。<br>按着顺序往下添加即可,中间使用 Tab 键分隔。如图所示:<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/436.png" alt=""></p><h3 id="生成-Patch-文件"><a href="#生成-Patch-文件" class="headerlink" title="生成 Patch 文件"></a>生成 Patch 文件</h3><p>在这里,我就假设你已经修改好了文件。<br>比如基于 kernel 内核 做了修改,修改前的内容放在文件夹 kernel 下,修改后的内容放在文件夹 kernel_new 下,并且两个目录在同级的文件夹里,那么制作 patch 文件的命令为</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">diff -Naur kernel/ kernel_new/ > new.patch</span><br></pre></td></tr></table></figure><p>然后接下来的步骤和上面的 <strong>安装 Patch</strong> 部分相同,不再赘述。</p><h3 id="最终结果"><a href="#最终结果" class="headerlink" title="最终结果"></a>最终结果</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%BA%90%E4%BB%A3%E7%A0%81%E4%BB%A5%E5%8F%8A%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E5%90%8Edmesg%E7%BB%93%E6%9E%9C.png" alt="源代码以及程序运行后dmesg结果"></p><h1 id="几个踩坑的点"><a href="#几个踩坑的点" class="headerlink" title="几个踩坑的点"></a>几个踩坑的点</h1><h2 id="一开始只安装了-Base-包"><a href="#一开始只安装了-Base-包" class="headerlink" title="一开始只安装了 Base 包"></a>一开始只安装了 Base 包</h2><p>网上的远古教程指导我说安装了 Base 包就行了,然而 Arch Linux 似乎把 Linux 内核从 Base 包当中分离了出来,所以一开始只安装 Base 包导致 grub 找不到内核一直引导失败。<br>这个惨痛的经历告诉我们,以后不管装什么,<strong>都要看官方 wiki</strong>,避免踩坑。</p><h2 id="在编译内核时-config-文件没有配置好"><a href="#在编译内核时-config-文件没有配置好" class="headerlink" title="在编译内核时 config 文件没有配置好"></a>在编译内核时 config 文件没有配置好</h2><p>一开始直接用<code>make defconfig</code>命令,编译了一个几乎啥都不带的内核。之后才明白如果想沿用当前系统设置应该用<code>zcat /proc/config.gz > .config</code>命令。<br>还好有 <em>善意的提醒</em> 不然又得从头再来。</p><h2 id="在-Patch-时报错,提示-patch-rejected"><a href="#在-Patch-时报错,提示-patch-rejected" class="headerlink" title="在 Patch 时报错,提示 patch rejected"></a>在 Patch 时报错,提示 patch rejected</h2><p>这是因为我用了 tuna 的 patch 包,这个包是用来一步到位从 5.3 升级到 5.3.8 的,而要想从 5.3.7 升级到 5.3.8,则需要从 <a href="https://www.kernel.org" target="_blank" rel="noopener">https://www.kernel.org</a> 上下载 Inc.patch 包。</p>]]></content>
<summary type="html">
<p>用 VMware 安装Linux虚拟机,还可以顺带做网安组的 Task,美滋滋。</p>
</summary>
<category term="Linux" scheme="http://blog.zam-meow.cn/tags/Linux/"/>
<category term="syscall" scheme="http://blog.zam-meow.cn/tags/syscall/"/>
<category term="Patch" scheme="http://blog.zam-meow.cn/tags/Patch/"/>
</entry>
<entry>
<title>使用VMware搭建小型局域网</title>
<link href="http://blog.zam-meow.cn/2019/10/26/%E4%BD%BF%E7%94%A8VMware%E6%90%AD%E5%BB%BA%E5%B0%8F%E5%9E%8B%E5%B1%80%E5%9F%9F%E7%BD%91/"/>
<id>http://blog.zam-meow.cn/2019/10/26/%E4%BD%BF%E7%94%A8VMware%E6%90%AD%E5%BB%BA%E5%B0%8F%E5%9E%8B%E5%B1%80%E5%9F%9F%E7%BD%91/</id>
<published>2019-10-26T09:25:11.000Z</published>
<updated>2020-04-16T02:07:27.546Z</updated>
<content type="html"><![CDATA[<p>在Dian团队的第一个Task~</p><a id="more"></a><h1 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h1><h2 id="安装-VMware"><a href="#安装-VMware" class="headerlink" title="安装 VMware"></a>安装 VMware</h2><p>下载地址:<a href="https://www.nocmd.com/740.html" target="_blank" rel="noopener">https://www.nocmd.com/740.html</a><br>安装无脑下一步即可</p><h2 id="安装-Ubuntu"><a href="#安装-Ubuntu" class="headerlink" title="安装 Ubuntu"></a>安装 Ubuntu</h2><p>镜像下载地址:<a href="https://cn.ubuntu.com/download" target="_blank" rel="noopener">https://cn.ubuntu.com/download</a><br>最新版的 VMware 支持 Ubuntu 的简易安装,所以在配置时直接填好主机名用户名密码就可以愉快的食用 Ubuntu 了</p><h1 id="配置局域网"><a href="#配置局域网" class="headerlink" title="配置局域网"></a>配置局域网</h1><h2 id="在虚拟网络编辑器中配置网关以及网段"><a href="#在虚拟网络编辑器中配置网关以及网段" class="headerlink" title="在虚拟网络编辑器中配置网关以及网段"></a>在虚拟网络编辑器中配置网关以及网段</h2><p>在 VMware 主页面 编辑–》虚拟网络编辑器<br>单击箭头所指按钮,给编辑器提升权限以更改设置</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9B%B4%E6%94%B9%E8%99%9A%E6%8B%9F%E7%BD%91%E7%BB%9C%E7%BC%96%E8%BE%91%E5%99%A8%E8%AE%BE%E7%BD%AE.png" alt="更改虚拟网络编辑器设置"></p><p>我们使用 NAT 模式,所以选择 VMnet8。(记住这个名称,稍后还会用到)<br>不勾选【使用本地 DHCP】服务这个复选框,因为我们要求固定 IP,DHCP 是动态分配 IP 的。<br>首先,我们将子网 IP 设置成我们需要的 IP 地址所在的网段,即 192.168.188.0,如图所示:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9B%B4%E6%94%B9VMnet8%E5%AD%90%E7%BD%91IP.png" alt="更改VMnet8子网IP"></p><p>点击 NAT 设置。<br>我们将网关 IP 设置成我们需要的 IP 地址 192.168.188.2,如图所示:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9B%B4%E6%94%B9VMnet8%E7%BD%91%E5%85%B3%E8%AE%BE%E7%BD%AE.png" alt="更改VMnet8网关设置"></p><p>然后再选择 Host-only 模式的网卡 VMnet1<br>同理,不勾选【使用本地 DHCP】服务这个复选框。<br>首先,我们将子网 IP 设置成我们需要的 IP 地址所在的网段,即 192.168.188.0,如图所示:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9B%B4%E6%94%B9VMnet1%E5%AD%90%E7%BD%91IP.png" alt="更改VMnet1子网IP"></p><h2 id="在控制面板中配置虚拟网卡-IP"><a href="#在控制面板中配置虚拟网卡-IP" class="headerlink" title="在控制面板中配置虚拟网卡 IP"></a>在控制面板中配置虚拟网卡 IP</h2><p>在 VMware 中更改了虚拟网络之后,我们还需要去宿主机的网络控制面板中更改网络适配器,才能让我们的虚拟主机能够正常的上网<br>在控制面版中找到 VMware Network Adapter VMnet8,也就是我们之前记住的使用 NAT 模式的那张网卡<br>双击图标后,在弹出的窗口中单击属性–>双击 Internet 协议版本 4<br>将弹出的窗口设置成如图所示的亚子:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%8E%A7%E5%88%B6%E9%9D%A2%E6%9D%BF%E8%AE%BE%E7%BD%AE.png" alt="控制面板设置"></p><h2 id="到此,配置局域网的工作暂告一段落"><a href="#到此,配置局域网的工作暂告一段落" class="headerlink" title="到此,配置局域网的工作暂告一段落"></a>到此,配置局域网的工作暂告一段落</h2><h1 id="打开三台虚拟机,进行最后的指向操作"><a href="#打开三台虚拟机,进行最后的指向操作" class="headerlink" title="打开三台虚拟机,进行最后的指向操作"></a>打开三台虚拟机,进行最后的指向操作</h1><h2 id="对于充当路由器和-DNS-服务器的虚拟机-VM1"><a href="#对于充当路由器和-DNS-服务器的虚拟机-VM1" class="headerlink" title="对于充当路由器和 DNS 服务器的虚拟机 VM1"></a>对于充当路由器和 DNS 服务器的虚拟机 VM1</h2><p>首先打开 VM1 的 IP 转发功能:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano /etc/sysctl.conf</span><br></pre></td></tr></table></figure><p>把 net.ipv4.ip_forward = 0 改成 1,或者是将注释符删去,如图所示:</p><p>![更改VM1 IP转发.png](<a href="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/更改VM1" target="_blank" rel="noopener">https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/更改VM1</a> IP 转发.png)</p><p>使用<code>sysctl -p</code>命令使改动生效。</p><p>然后再将用于与 VM2 和 VM3 通信的网卡手动分配下 IP<br>注意,此时一定要分清哪张网卡是工作在 NAT 模式下,用于与宿主机通信的;哪张是工作在 Host-Only 模式下,用于与两个虚拟机通信的。<br>所以在开始更改网卡配置之前,需要先执行<code>ifconfig</code>确定网卡的具体信息</p><p>从 ubuntu 从 17.10 开始,已经不再在/etc/network/interfaces 里配置 IP,即使配置了也不会生效,而是改成 netplan 方式 ,配置写在/etc/netplan/文件夹里或者类似名称的 yaml 文件里。因此,我们应先查看/etc/netplan 文件夹下的文件,来判断我们需要修改的是什么文件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /etc/netplan</span><br><span class="line">ls</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9F%A5%E7%9C%8B%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6.PNG" alt="输出结果"></p><p>从而确定下一步的命令为:<code>sudo nano 01-network-manager-all.yaml</code></p><p>经过我的甄别,确定网卡 ens38 是那张工作在 Host-Only 模式下的网卡、ens33 是那张工作在 NAT 模式下的网卡,于是乎按照任务要求,如图所示编辑两张网卡的配置:</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%9B%B4%E6%94%B9netplan1.PNG" alt="更改netplan"></p><p>之后运行命令<code>netplan apply</code>来应用这一设置</p><p>到这里,对 VM1 的操作就告一段落</p><h2 id="对于-VM2-和-VM3"><a href="#对于-VM2-和-VM3" class="headerlink" title="对于 VM2 和 VM3"></a>对于 VM2 和 VM3</h2><p>照葫芦画瓢,首先执行<code>ifconfig</code>确定网卡的具体信息,再更改 netplan 的配置信息。<br>具体操作过程与 VM1 大差不差,就直接放按着任务要求配置好的 netplan 了。</p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/VM2netplan.PNG" alt="VM2 netplan"></p><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/VM3netplan.PNG" alt="VM3 netplan"></p><p>然后就可以实现 VM1,VM2,VM3 三台机器的互 ping</p><h1 id="对-VM2-与-VM3-进行-NAT-转换"><a href="#对-VM2-与-VM3-进行-NAT-转换" class="headerlink" title="对 VM2 与 VM3 进行 NAT 转换"></a>对 VM2 与 VM3 进行 NAT 转换</h1><p>但是,经过上述的操作,VM2 与 VM3 却不能 ping 通 VM1 的网关和公网。有句话说得好,不能连公网的主机和咸鱼没啥区别。因此,我们需要用 iptables 实现 NAT 转换,使得 B 和 C 能够访问(ping)A 的网关和公网。</p><p>具体的 NAT 操作过程示例,可以参照这篇 blog 中 SNAT 部分,再根据具体的环境配置 iptables 的参数:<a href="https://www.zsythink.net/archives/1764" target="_blank" rel="noopener">https://www.zsythink.net/archives/1764</a></p><p>在此次任务中,命令为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iptables -t nat -A POSTROUTING -s 10.1.2.0/24 -j SNAT --to-source 192.168.188.128</span><br></pre></td></tr></table></figure><p>执行之后就可以在 VM2 与 VM3 两台“内网”机器中愉快的连上公网了~</p><h1 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h1><ul><li>1.netplan apply 时遇到格式错误<blockquote><p>错误原因:YAML 文件对格式的要求非常严格。在冒号后少打一个空格都会导致文件读取错误。</p></blockquote></li></ul><blockquote><p>解决方法:重新检查一遍 YAML 文件格式是否正确,有没有少打空格。并在之后多多注意格式问题</p></blockquote><ul><li>2.在执行命令中多次出现 Permission Denied<blockquote><p>错误原因:因为这次任务中涉及到对网络配置等系统关键文件进行修改的操作,所以需要 su 权限才能进行命令的执行。</p></blockquote></li></ul><blockquote><p>解决方法:在之后的命令行输入时,涉及到对系统文件、系统环境的修改时要记得加上 sudo。</p></blockquote><blockquote><p>不能因为 sudo 麻烦而直接使用 root 用户登录。不然没准哪天你就会不小心删库跑路</p></blockquote><h1 id="Task-Over"><a href="#Task-Over" class="headerlink" title="Task Over"></a>Task Over</h1><p>然而还差 12h 工时……绝了</p>]]></content>
<summary type="html">
<p>在Dian团队的第一个Task~</p>
</summary>
<category term="Dian CyberSecurity Team" scheme="http://blog.zam-meow.cn/tags/Dian-CyberSecurity-Team/"/>
<category term="Network" scheme="http://blog.zam-meow.cn/tags/Network/"/>
</entry>
<entry>
<title>AST-Task2</title>
<link href="http://blog.zam-meow.cn/2019/10/16/AST-Task2/"/>
<id>http://blog.zam-meow.cn/2019/10/16/AST-Task2/</id>
<published>2019-10-16T04:09:55.000Z</published>
<updated>2020-04-16T02:10:18.484Z</updated>
<content type="html"><![CDATA[<p>《C语言快速入门:从指针到Segmentation Fault》</p><a id="more"></a><p><strong>注意,本文编译环境为 Visual Studio 2019 on Windows 10 1903 X64</strong></p><h1 id="几个练手题"><a href="#几个练手题" class="headerlink" title="几个练手题"></a>几个练手题</h1><h2 id="1-编程实现:用户给定一个整数,将该整数逆置之后输出。(如:输入-123,输出-321)。"><a href="#1-编程实现:用户给定一个整数,将该整数逆置之后输出。(如:输入-123,输出-321)。" class="headerlink" title="1.编程实现:用户给定一个整数,将该整数逆置之后输出。(如:输入 123,输出 321)。"></a>1.编程实现:用户给定一个整数,将该整数逆置之后输出。(如:输入 123,输出 321)。</h2><h3 id="限制条件"><a href="#限制条件" class="headerlink" title="限制条件"></a>限制条件</h3><p>a.给定整数,不要用字符串来完成。<br>b.尽可能使时间复杂度小。<br>c.要求能够完成 214748364792 这个数字的逆置。</p><h3 id="程序源码"><a href="#程序源码" class="headerlink" title="程序源码"></a>程序源码</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">int</span> input;</span><br><span class="line">scanf_s(<span class="string">"%lld"</span>, &input);</span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">int</span> output = <span class="number">0</span>, digit;</span><br><span class="line"><span class="keyword">while</span> (input > <span class="number">0</span>) {</span><br><span class="line">digit = input % <span class="number">10</span>;<span class="comment">//取当前最末位数</span></span><br><span class="line">output = output * <span class="number">10</span> + digit;<span class="comment">//将当前的最末位数加到待输出结果的最后一位</span></span><br><span class="line">input /= <span class="number">10</span>;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld"</span>, output);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="运行结果"><a href="#运行结果" class="headerlink" title="运行结果"></a>运行结果</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%95%B4%E6%95%B0%E9%80%86%E7%BD%AE.png" alt="整数逆置"></p><h2 id="2-编程实现:给定一串任意字符串。要求,将其中的所有整数提取出来并存入整数数组"><a href="#2-编程实现:给定一串任意字符串。要求,将其中的所有整数提取出来并存入整数数组" class="headerlink" title="2.编程实现:给定一串任意字符串。要求,将其中的所有整数提取出来并存入整数数组"></a>2.编程实现:给定一串任意字符串。要求,将其中的所有整数提取出来并存入整数数组</h2><h3 id="给定样例"><a href="#给定样例" class="headerlink" title="给定样例"></a>给定样例</h3><p>1023fase415#145#</p><h3 id="程序源码-1"><a href="#程序源码-1" class="headerlink" title="程序源码"></a>程序源码</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">char</span> str[<span class="number">1000</span>];</span><br><span class="line"><span class="keyword">int</span> a[<span class="number">100</span>];</span><br><span class="line"><span class="keyword">int</span> p, q;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, r = <span class="number">0</span>,count = <span class="number">0</span>;<span class="comment">//i用于控制输出,r用于控制字符串内查找,count用于计算字符串内数字个数</span></span><br><span class="line"></span><br><span class="line">gets(str);</span><br><span class="line"></span><br><span class="line">r = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">while</span> (str[r] && (str[r]<<span class="string">'0'</span> || str[r]><span class="string">'9'</span>))</span><br><span class="line">r++; <span class="comment">//跳过字符串中非数字部分</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (str[r])</span><br><span class="line">{</span><br><span class="line">p = r; <span class="comment">//p指向数字子串开头</span></span><br><span class="line">q = r + <span class="number">1</span>; <span class="comment">//q寻找数字串结尾</span></span><br><span class="line">a[i] = str[r] - <span class="string">'0'</span>;<span class="comment">//将字符串中的0变为数字0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (str[q] >= <span class="string">'0'</span> && str[q] <= <span class="string">'9'</span>)</span><br><span class="line">{</span><br><span class="line">a[i] = <span class="number">10</span> * a[i] + (str[q] - <span class="string">'0'</span>);<span class="comment">//计算数字</span></span><br><span class="line">q++;</span><br><span class="line">}</span><br><span class="line">count++;</span><br><span class="line">r = q; <span class="comment">//设定新起点</span></span><br><span class="line">i++;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < count; i++)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d "</span>, a[i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="运行结果-1"><a href="#运行结果-1" class="headerlink" title="运行结果"></a>运行结果</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%8F%90%E5%8F%96%E6%95%B4%E6%95%B0.png" alt="提取整数"></p><h2 id="3-编程实现:给定一串任意字符串。要求,将其中的所有数字提取出来并存入-double-数组。"><a href="#3-编程实现:给定一串任意字符串。要求,将其中的所有数字提取出来并存入-double-数组。" class="headerlink" title="3.编程实现:给定一串任意字符串。要求,将其中的所有数字提取出来并存入 double 数组。"></a>3.编程实现:给定一串任意字符串。要求,将其中的所有数字提取出来并存入 double 数组。</h2><h3 id="给定样例-1"><a href="#给定样例-1" class="headerlink" title="给定样例"></a>给定样例</h3><p>10.23fase4.15#14.5#</p><h3 id="程序源码-2"><a href="#程序源码-2" class="headerlink" title="程序源码"></a>程序源码</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">char</span> str[<span class="number">1000</span>], num[<span class="number">1000</span>];</span><br><span class="line"><span class="keyword">double</span> a[<span class="number">100</span>];</span><br><span class="line"><span class="keyword">int</span> p, q;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, r = <span class="number">0</span>,count = <span class="number">0</span>;<span class="comment">//i用于控制循环,r用于控制字符串内查找,count用于计算字符串内数字个数</span></span><br><span class="line"></span><br><span class="line">gets(str);</span><br><span class="line"></span><br><span class="line">r = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">while</span> (str[r] && (str[r]<<span class="string">'0'</span> || str[r]><span class="string">'9'</span>)) {<span class="comment">//查找数字字串开头部分</span></span><br><span class="line">r++;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (str[r])</span><br><span class="line">{</span><br><span class="line">p = r; <span class="comment">//p指向数字子串开头</span></span><br><span class="line">q = r + <span class="number">1</span>; <span class="comment">//q寻找数字串结尾</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> ((str[q] >= <span class="string">'0'</span> && str[q] <= <span class="string">'9'</span>)||str[q]==<span class="string">'.'</span>){</span><br><span class="line">q++;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < q-p; i++) {<span class="comment">//将数字字串转存到新字符串中,用于后续转化输出</span></span><br><span class="line">num[i] = str[p + i];</span><br><span class="line">}</span><br><span class="line">num[i] = <span class="string">'\0'</span>;</span><br><span class="line">a[count] = atof(num);</span><br><span class="line">count++;</span><br><span class="line">r = q; <span class="comment">//设定新起点</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < count; i++)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lf "</span>, a[i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="运行结果-2"><a href="#运行结果-2" class="headerlink" title="运行结果"></a>运行结果</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E6%8F%90%E5%8F%96%E6%95%B0%E5%AD%97.png" alt="提取数字"></p><h1 id="几个烧脑题"><a href="#几个烧脑题" class="headerlink" title="几个烧脑题"></a>几个烧脑题</h1><h2 id="多级指针:观察下列代码,思考并解释程序运行结果"><a href="#多级指针:观察下列代码,思考并解释程序运行结果" class="headerlink" title="多级指针:观察下列代码,思考并解释程序运行结果"></a>多级指针:观察下列代码,思考并解释程序运行结果</h2><h3 id="1"><a href="#1" class="headerlink" title="1"></a>1</h3><h4 id="程序代码及解释"><a href="#程序代码及解释" class="headerlink" title="程序代码及解释"></a>程序代码及解释</h4><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">char</span> * c[] = { <span class="string">"ENTER"</span>, <span class="string">"NEW"</span>, <span class="string">"POINT"</span>, <span class="string">"FIRST"</span> };<span class="comment">//定义了四个指针c[0]-c[3]分别指向四个字符串</span></span><br><span class="line"><span class="keyword">char</span>** cp[] = { c + <span class="number">3</span>, c + <span class="number">2</span>, c + <span class="number">1</span>, c };<span class="comment">//定义了一个二级指针数组,其中的cp[0]-cp[3]分别依次对应c[3]-c[0]</span></span><br><span class="line"><span class="keyword">char</span>*** cpp = cp;<span class="comment">//定义了一个三级指针,指向二级指针数组的第一个元素cp[0],此时cpp就相当于c[3]</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>, ** ++cpp);<span class="comment">//** cpp经过间接引用运算后相当于是指针cp,指针cp自增后指向的是指针数组cp[]中的第二个元素,也即是c[2]</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>, * --* ++cpp + <span class="number">3</span>);<span class="comment">//首先对指针cpp作了自增操作,使得cpp指向cp[2],此时cpp中的地址为c+1,再通过自减符使得指针 * cpp即cp[2] (注意不是cpp)的地址变为c,这时候*--* ++cpp相当于指向了一个字符数组{'E','N','T','E','R'}的首地址。+3的操作等价于在这个字符数组的首地址的基础上再右移三个地址,指向了第二个E,然后通过printf将第二个E和之后的所有剩余字符全部打印。</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>, * cpp[<span class="number">-2</span>] + <span class="number">3</span>);<span class="comment">//经过了上一条语句后,cpp指向cp[2],那么cpp[-2]指向cp[0](注意,此时cpp内存储的地址并没有改变),即c[3]此时的*cpp[-2]就相当于指向了一个字符数组{'F','I','R','S','T'}的首地址。+3的操作等价于在这个字符数组的首地址的基础上再右移三个地址,指向了S,然后通过printf将S和之后的所有剩余字符全部打印。</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>, * cpp[<span class="number">-1</span>][<span class="number">-1</span>] + <span class="number">1</span>);<span class="comment">//同上理,此时cpp指向cp[2],cpp[-1]就会指向cp[1],即c[2],那么cpp[-1][-1]就相当于指向c[1],即一个字符数组{'N','E','W'}的首地址。+1的操作等价于在这个字符数组的首地址的基础上再右移1个地址,指向了E,然后通过printf将E和之后的所有剩余字符全部打印。</span></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="上机验证"><a href="#上机验证" class="headerlink" title="上机验证"></a>上机验证</h4><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/20191019223002.png" alt="上机验证"></p><h3 id="2"><a href="#2" class="headerlink" title="2"></a>2</h3><h4 id="程序代码"><a href="#程序代码" class="headerlink" title="程序代码"></a>程序代码</h4><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Test</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"><span class="keyword">int</span> Num;</span><br><span class="line"><span class="keyword">char</span>* pcName;</span><br><span class="line"><span class="keyword">short</span> sDate;</span><br><span class="line"><span class="keyword">char</span> cha[<span class="number">2</span>];</span><br><span class="line"><span class="keyword">short</span> sBa[<span class="number">4</span>];</span><br><span class="line">}*p;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">p = <span class="number">0x100000</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%p\n"</span>, p + <span class="number">0x1</span>);<span class="comment">//输出00100014</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%p\n"</span>, (<span class="keyword">unsigned</span> <span class="keyword">long</span>)p + <span class="number">0x1</span>);<span class="comment">//输出00100001</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%p\n"</span>, (<span class="keyword">unsigned</span> <span class="keyword">int</span>*)p + <span class="number">0x1</span>);<span class="comment">//输出00100004</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h4><p>首先要明确,指针与整数的加减法公式为 p = p +/- sizeof(type * p)</p><p>第一行输出 00100014 的原因是结构体 Test 的存储空间大小为 sizeof(int) + sizeof(char* ) + sizeof(short) + sizeof(char)* 2 + sizeof(short)* 4 = 20,并且指针的地址是以十六进制数存放的,因此 p + 0x1 就相当于指针 p 向后移动了 sizeof(Test)的内存大小,因此输出的内存地址比 1000000 大 20,为 00100014。</p><p>第二行输出 001000001 的原因是(unsigned long)语句将* p 转换成了整数类型,因此此时做的只是普通的整数与整数之间的加减法。</p><p>第三行输出 00100004 的原因是 (unsigned int<em>)语句将 Test</em> 类型的 p 指针转换成了 int _ 类型,根据公式此时 sizeof(int_) = 4,因此输出 00100004。</p><h4 id="上机验证-1"><a href="#上机验证-1" class="headerlink" title="上机验证"></a>上机验证</h4><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E4%B8%8A%E6%9C%BA%E9%AA%8C%E8%AF%812.png" alt="上机验证"></p><h3 id="3"><a href="#3" class="headerlink" title="3"></a>3</h3><h4 id="程序代码-1"><a href="#程序代码-1" class="headerlink" title="程序代码"></a>程序代码</h4><p>#include <stdio.h></p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">int</span> a[<span class="number">4</span>] = { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> };</span><br><span class="line"><span class="keyword">int</span> * ptr1 = (<span class="keyword">int</span> * )(&a + <span class="number">1</span>);</span><br><span class="line"><span class="keyword">int</span> * ptr2 = (<span class="keyword">int</span> * )((<span class="keyword">int</span>)a + <span class="number">1</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%x\n%x\n"</span>, ptr1[<span class="number">-1</span>], * ptr2);\\输出<span class="number">4</span> <span class="number">2000000</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="解释-1"><a href="#解释-1" class="headerlink" title="解释"></a>解释</h4><p>首先,&a 指的是取数组 a 的地址,而&a + 1 指的是加上一个 int a[4]的长度,即 sizeof(int) * 4=16 字节,所以 ptr1 指向数组 a 后面的内存单元,如果用下标表示就是 a[5]</p><p>由指针与整数的加减法公式 p = p +/- sizeof(type _ p,ptr1[-1]表示 ptr1 指向的地址再减去 sizeof(int _ ),即指向 a[4],所以第一个%x 输出对应的是 0x4.</p><p>(int)a+1 的值就是元素 a[0]的第二个字节的地址,然后把这个地址强制转化为(int<em>)类型赋给 ptr2,也就是说</em>ptr2 的值应该为元素 a[0]的第二个字节开始的连续 4 个 Byte 的内容。</p><p>不过要想理解为什么输出了 2000000,就要首先明白数字在数组中是怎么被存储的:</p><p>每个元素具体存储方式,取决于 CPU。 有两种:<br>1、小端(Little Endian):<br>将低序字节存储在起始地址(低位编址), 地址低位存储值的低位,地址高位存储值的高位 。<br>目前大多数 CPU 是按照这种方式存储的,包括 intel 和移动端最常见的 arm。<br>比如 4 字节整型值为 0x12345678 的情况,那么在内存中会存储为:<br>0x78 0x56 0x34 0x12<br>2、大端(Big Endian):<br>与小端相反, 将高序字节存储在起始地址(高位编址),地址低位存储值的高位,地址高位存储值的低位。<br>之前的例子在大端情况下存储为:<br>0x12 0x34 0x56 0x78</p><p>因此,a[0]在内存中被存储为 0x01 0x00 0x00 0x00,a[1]在内存中被存储为 0x02 0x00 0x00 0x00,此时 ptr2 所指向的内存区域的值就是 0x00 0x00 0x00 0x02</p><p>但是,在 printf 进行输出时,内存中的值是自右而左地被读出的,因此输出的值应该是 0x02000000</p><h4 id="上机验证-2"><a href="#上机验证-2" class="headerlink" title="上机验证"></a>上机验证</h4><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E4%B8%8A%E6%9C%BA%E9%AA%8C%E8%AF%813.png" alt="上机验证"></p><h1 id="其余部分"><a href="#其余部分" class="headerlink" title="其余部分"></a>其余部分</h1><p><del>就先咕为敬了,告辞</del><br><del>有空再补</del><br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/1571031606850.gif" alt="咕咕咕"><br>好了我胡汉三又回来了</p><h2 id="malloc-函数的使用"><a href="#malloc-函数的使用" class="headerlink" title="malloc 函数的使用"></a>malloc 函数的使用</h2><p>在 C 语言中,malloc 是动态内存分配函数。</p><p>它的原型声明在 stdlib.h 头文件中:</p><figure class="highlight c"><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="keyword">void</span> *<span class="title">malloc</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> num_bytes)</span></span>;</span><br></pre></td></tr></table></figure><p>num_bytes 是无符号整型,用于表示分配的字节数。<br>这个函数的返回值:如果分配成功则返回指向被分配内存的指针 void* (此存储区中的初始值不确定),否则返回空指针 NULL。<br>void* 表示未确定类型的指针,void * 可以指向任何类型的数据,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是 char 还是 int 或者…)<br>这个函数的功能很简单:就是分配长度为 num_bytes 字节的内存块。<br>注意:由于 C 语言中缺少内存回收机制,所以当内存不再使用时,应使用 free()函数将内存块释放。函数返回的指针一定要适当对齐,例如说统一为 4 的倍数,使其可以用于任何数据对象。<br>关于该函数的原型,在以前 malloc 返回的是 char 型指针,新的 ANSIC 标准规定,该函数返回为 void 型指针,因此在使用是我们应要进行类型转换。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdlib.h>//malloc()函数被包含在stdlib.h里面</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span>*a=<span class="literal">NULL</span>; <span class="comment">//声明一个指向a的char*类型的指针</span></span><br><span class="line"></span><br><span class="line">a=(<span class="keyword">char</span>*)<span class="built_in">malloc</span>(<span class="number">100</span>*<span class="keyword">sizeof</span>(<span class="keyword">char</span>));<span class="comment">//使用malloc分配内存的首地址,然后赋值给a</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(!a)<span class="comment">//如果malloc失败,可以得到一些log</span></span><br><span class="line"></span><br><span class="line">{</span><br><span class="line">perror(<span class="string">"malloc"</span>);</span><br><span class="line"><span class="keyword">return</span><span class="number">-1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">sprintf</span>(a,<span class="string">"%s"</span>,<span class="string">"HelloWorld\n"</span>);<span class="comment">//"HelloWorld\n"写入a指向的地址</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>,a);<span class="comment">//输出上边写入a的字符串</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">free</span>(a);<span class="comment">//释放掉使用的内存地址</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;<span class="comment">//例2有无内存泄露?</span></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>而且,作为一名合格的码农,我们应当对一些特殊情况进行特殊处理,如这里的 malloc 函数若调用失败,则直接让程序退出,而不是让其运行下去,否则可能会造成更大的 bug,而且也不利于我们根据返回值进行 debug。</p><h2 id="结构体指针-gt-的使用"><a href="#结构体指针-gt-的使用" class="headerlink" title="结构体指针->的使用"></a>结构体指针->的使用</h2><p>除了我们通过<code>结构体变量名.成员名</code>的方式引用结构体变量中的成员,我们还可以使用指针。<br>要想学会->这种指针的使用,首先我们就要学会一般的结构体指针使用方式:<br><code>(* 指针变量名).成员名</code><br>这个指针变量定义成什么类型呢?<br>只能定义成结构体类型,且指向什么结构体类型的结构体变量,就要定义成什么样的结构体类型。<br>比如指向 struct STUDENT 类型的结构体变量,那么指针变量就一定要定义成 struct STUDENT* 类型。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta"># <span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">AGE</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">int</span> year;</span><br><span class="line"> <span class="keyword">int</span> month;</span><br><span class="line"> <span class="keyword">int</span> day;</span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">char</span> name[<span class="number">20</span>]; <span class="comment">//姓名</span></span><br><span class="line"> <span class="keyword">int</span> num; <span class="comment">//学号</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">AGE</span> <span class="title">birthday</span>;</span> <span class="comment">//生日</span></span><br><span class="line"> <span class="keyword">float</span> score; <span class="comment">//分数</span></span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span> <span class="title">student1</span>;</span> <span class="comment">/* 用struct STUDENT结构体类型定义结构体变量student1*/</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span> * <span class="title">p</span> = <span class="title">NULL</span>;</span> <span class="comment">/* 定义一个struct STUDENT结构体类型的指针变量p*/</span></span><br><span class="line"> p = &student1; <span class="comment">/* p指向结构体变量student1的首地址, 即第一个成员的地址*/</span></span><br><span class="line"> <span class="built_in">strcpy</span>((* p).name, <span class="string">"小明"</span>); <span class="comment">//(* p).name等价于student1.name</span></span><br><span class="line"> (* p).birthday.year = <span class="number">1989</span>;</span><br><span class="line"> (* p).birthday.month = <span class="number">3</span>;</span><br><span class="line"> (* p).birthday.day = <span class="number">29</span>;</span><br><span class="line"> (* p).num = <span class="number">1207041</span>;</span><br><span class="line"> (* p).score = <span class="number">100</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"name : %s\n"</span>, (* p).name); <span class="comment">//(* p).name不能写成p,即使p指向的是student1.name的地址。</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"birthday : %d-%d-%d\n"</span>, (* p).birthday.year, (* p).birthday.month, (* p).birthday.day);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"num : %d\n"</span>, (* p).num);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"score : %.1f\n"</span>, (* p).score);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出结果:</p><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></pre></td><td class="code"><pre><span class="line">name : 小明</span><br><span class="line">birthday : <span class="number">1989</span><span class="number">-3</span><span class="number">-29</span></span><br><span class="line">num : <span class="number">1207041</span></span><br><span class="line">score : <span class="number">100.0</span></span><br></pre></td></tr></table></figure><p>注意,_ p 两边的括号不可省略,因为成员运算符“.”的优先级高于指针运算符“ _ ”,所以如果 _ p 两边的括号省略的话,那么 _ p.num 就等价于 _ (p.num) 了。<br>从该程序也可以看出:因为指针变量 p 指向的是结构体变量 student1 第一个成员的地址,即字符数组 name 的首地址,所以 p 和 (_ p).name 是等价的。<br>但是,“等价”仅仅是说它们表示的是同一个内存单元的地址,但它们的类型是不同的。指针变量 p 是 struct STUDENT* 型的,而 (* p).name 是 char* 型的。所以在 strcpy 中不能将 (* p).name 改成 p。用 %s 进行输入或输出时,输入参数或输出参数也只能写成 (_ p).name 而不能写成 p。<br>同样,虽然 &student1 和 student1.name 表示的是同一个内存单元的地址,但它们的类型是不同的。&student1 是 struct STUDENT_ 型的,而 student1.name 是 char* 型的,所以在对 p 进行初始化时,“p=&student1;”不能写成“p=student1.name”。因为 p 是 struct STUDENT* 型的,所以不能将 char* 型的 student1.name 赋给 p。C 语言是一门强数据类型的语言,就在这里体现的淋漓尽致。<br>此外,为了使用的方便和直观,我们可以直接用<code>指针变量名->成员名</code>来代替。<br>p->num 的含义是:指针变量 p 所指向的结构体变量中的 num 成员。p->num 最终代表的就是 num 这个成员中的内容。<br>下面,我们可以用<code>指针变量名->成员名</code>的形式对我们刚刚的代码进行修改:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta"># <span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">AGE</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">int</span> year;</span><br><span class="line"> <span class="keyword">int</span> month;</span><br><span class="line"> <span class="keyword">int</span> day;</span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">char</span> name[<span class="number">20</span>]; <span class="comment">//姓名</span></span><br><span class="line"> <span class="keyword">int</span> num; <span class="comment">//学号</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">AGE</span> <span class="title">birthday</span>;</span> <span class="comment">/* 用struct AGE结构体类型定义结构体变量birthday, 生日*/</span></span><br><span class="line"> <span class="keyword">float</span> score; <span class="comment">//分数</span></span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span> <span class="title">student1</span>;</span> <span class="comment">/* 用struct STUDENT结构体类型定义结构体变量student1*/</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">STUDENT</span> * <span class="title">p</span> = <span class="title">NULL</span>;</span> <span class="comment">/* 定义struct STUDENT结构体类型的指针变量p*/</span></span><br><span class="line"> p = &student1; <span class="comment">/* p指向结构体变量student1的首地址, 即第一项的地址*/</span></span><br><span class="line"> <span class="built_in">strcpy</span>(p->name, <span class="string">"小明"</span>);</span><br><span class="line"> p->birthday.year = <span class="number">1989</span>;</span><br><span class="line"> p->birthday.month = <span class="number">3</span>;</span><br><span class="line"> p->birthday.day = <span class="number">29</span>;</span><br><span class="line"> p->num = <span class="number">1207041</span>;</span><br><span class="line"> p->score = <span class="number">100</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"name : %s\n"</span>, p->name); <span class="comment">//p->name不能写成p</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"birthday : %d-%d-%d\n"</span>, p->birthday.year, p->birthday.month, p->birthday.day);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"num : %d\n"</span>, p->num);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"score : %.1f\n"</span>, p->score);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出同上。</p><h2 id="链表的基本概念及简单使用"><a href="#链表的基本概念及简单使用" class="headerlink" title="链表的基本概念及简单使用"></a>链表的基本概念及简单使用</h2><p>学了三个月,终于学到了第一种数据结构类型:链表。<br>那么,链表它到底是个啥?<br>链表,链表,首先它得是个线性表。根据《数据结构》书中介绍,一个线性表是 n 个数据元素的有限序列,它的长度可根据需要增长或缩短,还有一系列对线性表的操作。线性表可分为顺序存储结构和链式存储结构两种。<br>那么今天所学习的链表,全称就叫链式存储结构线性表。<br>线性链表可分为单链表,循环链表,双链表。</p><p>线性表特点是用一组任意的存储单元存储线性表的数据元素,同时还存储一个指向后继信息的信息,这两部分信息组成为结点。<br>结点包含两部分数据域和指针域。指针域存储信息成为指针或链。链表中只包含一个指针域,故称为单链表。<br><img src="https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike92%2C5%2C5%2C92%2C30/sign=f56b157cdeca7bcb6976cf7ddf600006/b2de9c82d158ccbfebe4998510d8bc3eb1354130.jpg" alt="单链表"></p><p>下面通过 c 语言实现单链表的基本操作:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXSIZE 20</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> OK 1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> ERROR 0</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> ElemType;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> Status;</span><br><span class="line"><span class="comment">//上述是一些重命名和宏定义</span></span><br><span class="line"><span class="comment">//单链表的存储结构</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">LNode</span>{</span></span><br><span class="line"> ElemType data; <span class="comment">//数据域</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 class="comment">//指针域</span></span><br><span class="line">}LNode,*LinkList;</span><br></pre></td></tr></table></figure><p>读取链表第 i 个元素的数据:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">GetElem</span><span class="params">(LinkList L, <span class="keyword">int</span> i, ElemType *e)</span></span>{</span><br><span class="line"> LinkList p;</span><br><span class="line"> <span class="keyword">int</span> j;</span><br><span class="line"> p = L->next;</span><br><span class="line"> j=<span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span>(p && j<i){</span><br><span class="line"> p = p->next;</span><br><span class="line"> ++j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(!p || j>i){</span><br><span class="line"> <span class="keyword">return</span> ERROR;</span><br><span class="line"> }</span><br><span class="line"> * e = p->data;</span><br><span class="line"> <span class="keyword">return</span> OK;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在带头结点的链表 L 的第 i 个位置之前插入元素 e:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">ListInsert</span><span class="params">(LinkList &L, <span class="keyword">int</span> i, ElemType e)</span></span>{</span><br><span class="line"> LNode p;</span><br><span class="line"> <span class="keyword">int</span> j;</span><br><span class="line"> p = L;</span><br><span class="line"> j=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(p && j<i<span class="number">-1</span>){</span><br><span class="line"> p = p->next;</span><br><span class="line"> ++j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(!p || j>i<span class="number">-1</span>){</span><br><span class="line"> <span class="keyword">return</span> ERROR;</span><br><span class="line"> }</span><br><span class="line"> LinkList s;</span><br><span class="line"> s = (LinkList)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(LNode));</span><br><span class="line"> s->data = e;</span><br><span class="line"> s->next = p->next;</span><br><span class="line"> p->next =s;</span><br><span class="line"> <span class="keyword">return</span> OK;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在带头结点的链表 L,删除第 i 个位置的元素,并由 e 返回其值:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">ListDelete</span><span class="params">(LinkList &L, <span class="keyword">int</span> i, ElemType &e)</span></span>{</span><br><span class="line"> LNode p;</span><br><span class="line"> <span class="keyword">int</span> j;</span><br><span class="line"> p = L;</span><br><span class="line"> j=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(p && j<i<span class="number">-1</span>){</span><br><span class="line"> p = p->next;</span><br><span class="line"> ++j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(!(p->next) || j>i<span class="number">-1</span>){</span><br><span class="line"> <span class="keyword">return</span> ERROR;</span><br><span class="line"> }</span><br><span class="line"> LinkList q;</span><br><span class="line"> q = p->next;</span><br><span class="line"> p = q->next;</span><br><span class="line"> e = q->data;</span><br><span class="line"> <span class="built_in">free</span>(q);</span><br><span class="line"> <span class="keyword">return</span> OK;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>循环链表是另外一种存储形式的链式存储结构,特点是表中最后一个结点的指针域指向头结点,与单链表比较相像,故不再赘述。<br>双向链表:是指针域指向前驱结点和后继结点。<br>存储结构为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">DuLNode</span>{</span></span><br><span class="line"> ElemType data;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">DuLNode</span> * <span class="title">prior</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">DuLNode</span> * <span class="title">next</span>;</span></span><br><span class="line">}DuLNode, * DuLinkList;</span><br></pre></td></tr></table></figure><h2 id="链表延伸"><a href="#链表延伸" class="headerlink" title="链表延伸"></a>链表延伸</h2><p>1.编程创建一个单链表。可不断读取用户输入的整数并存储进链表里。并在最后将链表里面的数据打印出来。 2.编程实现:将上述任务中已经创建完毕的单链表逆置(如将 1->2->3->4->null 逆置为 4->3->2->1->)</p><h3 id="程序代码-2"><a href="#程序代码-2" class="headerlink" title="程序代码"></a>程序代码</h3><p>简明起见,我将两个任务写在一个程序里了</p><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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SUCCESS 10000</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FAILURE 10001</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> ElemType;</span><br><span class="line"><span class="keyword">typedef</span> <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">ElemType data;</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">}Link;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">InitLink</span><span class="params">(Link** l)</span><span class="comment">//初始化链表</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == l)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">* l = (Link*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Link));</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == * l)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">(* l)->next = <span class="literal">NULL</span>;</span><br><span class="line">(* l)->data = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">return</span> SUCCESS;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">InsertLink</span><span class="params">(Link* l, <span class="keyword">int</span> place, ElemType e)</span><span class="comment">//插入链表</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">int</span> k = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == l)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (place > l->data + <span class="number">1</span> || place <= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">Link* head = l;</span><br><span class="line"><span class="keyword">while</span> (k < place)</span><br><span class="line">{</span><br><span class="line">k++;</span><br><span class="line">l = l->next;</span><br><span class="line">}</span><br><span class="line">Link* tmp = (Link*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Link));</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == tmp)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">tmp->next = l->next;</span><br><span class="line">l->next = tmp;</span><br><span class="line">tmp->data = e;</span><br><span class="line">head->data++;</span><br><span class="line"><span class="keyword">return</span> SUCCESS;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">TraverseLink</span><span class="params">(Link* l)</span><span class="comment">//遍历链表</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">int</span> length;</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == l)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">length = l->data;</span><br><span class="line"><span class="keyword">while</span> (length > <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line">length--;</span><br><span class="line">l = l->next;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d "</span>, l->data);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> SUCCESS;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ReverseList</span><span class="params">(Link* L)</span><span class="comment">//逆置链表</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">Link* curnode = L->next; <span class="comment">//当前节点,指向表头</span></span><br><span class="line">Link* temp = curnode->next; <span class="comment">//临时节点</span></span><br><span class="line"></span><br><span class="line">curnode->next = <span class="literal">NULL</span>;</span><br><span class="line">L->next = curnode;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (temp != <span class="literal">NULL</span>)</span><br><span class="line">{</span><br><span class="line">curnode = temp;</span><br><span class="line">temp = curnode->next;</span><br><span class="line">curnode->next = L->next;</span><br><span class="line">L->next = curnode;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SortInsert</span><span class="params">(Link* l, ElemType e)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">int</span> flag = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">int</span> length, place;</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == l)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> FAILURE;</span><br><span class="line">}</span><br><span class="line">Link* head = l;</span><br><span class="line">length = l->data;</span><br><span class="line">l = l->next;</span><br><span class="line">place = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="number">0</span> == length){</span><br><span class="line"> InsertLink(head, place + <span class="number">1</span>, e);</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">while</span> (place < length)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span> (e < l->data)</span><br><span class="line">{</span><br><span class="line">InsertLink(head, place + <span class="number">1</span>, e);</span><br><span class="line">flag = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">l = l->next;</span><br><span class="line">place++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (<span class="number">1</span> == flag)</span><br><span class="line">{</span><br><span class="line">InsertLink(head, length + <span class="number">1</span>, e);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> SUCCESS;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">Link* <span class="built_in">list</span>;</span><br><span class="line">ElemType e;</span><br><span class="line"></span><br><span class="line">InitLink(&<span class="built_in">list</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Please input numbers,input other character to end!\n"</span>);</span><br><span class="line"><span class="keyword">while</span>(scanf_s(<span class="string">"%d"</span>,&e) == <span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Please input numbers,input other character to end!\n"</span>);</span><br><span class="line">SortInsert(<span class="built_in">list</span>, e);</span><br><span class="line"><span class="comment">// printf("Length = %d\n", list->data);</span></span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"The output is :"</span>);</span><br><span class="line">TraverseLink(<span class="built_in">list</span>);</span><br><span class="line">ReverseList(<span class="built_in">list</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\nThe reverse List output is: "</span>);</span><br><span class="line">TraverseLink(<span class="built_in">list</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="程序运行结果"><a href="#程序运行结果" class="headerlink" title="程序运行结果"></a>程序运行结果</h3><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/%E9%93%BE%E8%A1%A8%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C.png" alt="程序运行结果"></p><h2 id="Leetcode"><a href="#Leetcode" class="headerlink" title="Leetcode"></a>Leetcode</h2><p>都不会做<br>好难啊<br>我太菜了<br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/29412e0a494585b8.jpg" alt=""><br><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/924112618.png" alt=""></p>]]></content>
<summary type="html">
<p>《C语言快速入门:从指针到Segmentation Fault》</p>
</summary>
<category term="C" scheme="http://blog.zam-meow.cn/tags/C/"/>
<category term="Learning" scheme="http://blog.zam-meow.cn/tags/Learning/"/>
<category term="Function&Pointer" scheme="http://blog.zam-meow.cn/tags/Function-Pointer/"/>
</entry>
<entry>
<title>AST Task1</title>
<link href="http://blog.zam-meow.cn/2019/10/05/AST-Task1/"/>
<id>http://blog.zam-meow.cn/2019/10/05/AST-Task1/</id>
<published>2019-10-05T08:15:00.000Z</published>
<updated>2020-04-16T02:06:57.477Z</updated>
<content type="html"><![CDATA[<p>在EIC-AST的第一个Task~</p><a id="more"></a><h1 id="学习内容"><a href="#学习内容" class="headerlink" title="学习内容"></a>学习内容</h1><h2 id="0x01-C-语言中的数据类型"><a href="#0x01-C-语言中的数据类型" class="headerlink" title="0x01 C 语言中的数据类型"></a>0x01 C 语言中的数据类型</h2><h3 id="C-语言中的常用数据类型"><a href="#C-语言中的常用数据类型" class="headerlink" title="C 语言中的常用数据类型"></a>C 语言中的常用数据类型</h3><hr><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/C%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B.png%22C%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%22" alt="C语言中的常用数据类型"></p><hr><h4 id="C-语言中整型与字符型数据"><a href="#C-语言中整型与字符型数据" class="headerlink" title="C 语言中整型与字符型数据"></a>C 语言中整型与字符型数据</h4><p>首先需要注意的是:</p><p>无符号(unsigned)整型数据与一般有符号整型数据的区别。</p><p>整型有无符号(unsigned)和有符号(signed)两种类型,在默认情况下声明的整型变量都是有符号的类型(char 有点特别,需要根据具体编译环境确定),如果需声明无符号类型的话就需要在类型前加上 unsigned。</p><p>无符号整型和有符号整型的区别就是无符号类型可以存放的正数范围比有符号整型中的范围大一倍,因为有符号类型将最高位储存符号,而无符号类型全都储存数字。</p><p>并且:</p><p>在 C/C++语言中,int 和 long int 的所占的字节数与编译环境有关。</p><p>C 语言标准是这样规定的:int 最少 16 位(2 字节),long 不能比 int 短,short 不能比 int 长,具体位长由编译器开发商根据各种情况自己决定。</p><p>在老式的 16 位编译系统上,short、int、long 普遍的长度是 2 字节、2 字节、4 字节。<br>在 32 位编译系统 x86 处理器上,short、int、long 普遍的长度是 2 字节、4 字节、4 字节。int 占四字节,与 long 相同。<br>在 64 位编译系统 x64 处理器上:short 占两字节,int 占四字节,long 占 8 字节,long 数据范围变为:-2^63~2^63-1</p><p>由此可见 int 类型的数据长度一般是机器位长。在 16 位编译系统中 int 为 16 位,两个字节;32 位编译系统中 int 为 32 位,4 个字节;但是在 64 位编译系统中为了兼容 32 位编译系统,64 位编译系统的 int 也是 4 字节。</p><p>现在常用的编译器多认为 int 和 long int 相同,均为 4 字节,short 为 2 字节,char 为 1 字节。<br>如果只输入 int,它有可能是以上三种形式中的一种。</p><p>那么如何得到某个类型在特定平台上的准确大小?</p><p>为了得到某个类型或某个变量在特定平台上的准确大小,我们可以使用 sizeof 运算符。通过表达式 sizeof(type) 得到对象或类型的存储字节大小。下面的实例演示了获取 int 类型的大小:</p><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><limits.h></span></span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"int 存储大小 : %lu \n"</span>, <span class="keyword">sizeof</span>(<span class="keyword">int</span>));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h4 id="C-语言中的-void-类型"><a href="#C-语言中的-void-类型" class="headerlink" title="C 语言中的 void 类型"></a>C 语言中的 void 类型</h4><p>void 类型指定没有可用的值。它通常用于以下三种情况下:</p><p>第一种:函数返回为空<br>C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);</p><p>第二种:函数参数为空<br>C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);</p><p>3 指针指向 void<br>类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数<br><code>void *malloc( size_t size );</code><br>返回指向 void 的指针,可以转换为任何数据类型。</p><h4 id="C-语言中的-bool(布尔)类型"><a href="#C-语言中的-bool(布尔)类型" class="headerlink" title="C 语言中的 bool(布尔)类型"></a>C 语言中的 bool(布尔)类型</h4><p>在此之前的 C 语言中,使用整型 int 来表示真假。在输入时:使用非零值表示真;零值表示假。在输出时:真的值是 1,假的值是 0。</p><p>现在,出现了布尔型变量。<code>_Bool</code>类型长度为 1,只能取值范围为 0 或 1。将任意非零值赋值给<code>_Bool</code>类型,都会先转换为 1,表示真。将零值赋值给<code>_Bool</code>类型,结果为 0,表示假。</p><p>有如下 example program:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">#include <stdio.h></span><br><span class="line">#include <stdlib.h></span><br><span class="line"></span><br><span class="line"> int main(){</span><br><span class="line"> _Bool bool1 = 1;</span><br><span class="line"> _Bool bool2 = 2; /* 非零值,bool2的值为1 */</span><br><span class="line"> _Bool bool3 = 0;</span><br><span class="line"> _Bool bool4 = -1; /* 非零值,bool4的值为1 */</span><br><span class="line"></span><br><span class="line"> printf("bool1==%d, \n", bool1);</span><br><span class="line"> printf("bool2==%d, \n", bool2);</span><br><span class="line"> printf("bool3==%d, \n", bool3);</span><br><span class="line"> printf("bool4==%d, \n", bool4);</span><br><span class="line"></span><br><span class="line"> printf("sizeof(_Bool) == %d \n", sizeof(_Bool));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>运行结果如下:<br><code>bool1==1, bool2==1, bool3==0, bool4==1, sizeof(_Bool) == 1</code></p><h4 id="C-语言中的数组类型"><a href="#C-语言中的数组类型" class="headerlink" title="C 语言中的数组类型"></a>C 语言中的数组类型</h4><p>所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。<br><img src="https://www.runoob.com/wp-content/uploads/2014/08/arrays.jpg%22%E6%95%B0%E7%BB%84%E7%BB%93%E6%9E%84%E7%A4%BA%E4%BE%8B%22" alt="数组结构示例"></p><h4 id="C-语言中的指针类型"><a href="#C-语言中的指针类型" class="headerlink" title="C 语言中的指针类型"></a>C 语言中的指针类型</h4><p>每个变量都被存放在从某个内存地址(以字节为单位)开始的若干个字节中。“指针”,也称作“指针变量”,大小为 4 个字节(或 8 个字节)的变量,其内容代表一个内存地址。<br>通过指针,我们能够对该指针指向的内存区域进行读写。<br>如果把内存的每个字节都想像成宾馆的一个房间,那么内存地址相当于就是房间号,而指针里存放的,就是房间号。</p><p>T _ p ; // T 可以是任何类型的名字,比如 int, double ,char 等等。<br>p 的类型: T _</p><ul><li>p 的类型: T<br>通过表达式 * p,可以读写从地址 p 开始的 sizeof(T)个字节</li><li><em>p</em> 等价于存放在地址 p 处的一个 T 类型的变量</li><li>意思为间接引用运算符<br>sizeof(T*) 4 字节(64 位计算机上可能 8 字节)</li></ul><p>有了指针,就有了自由访问内存空间的手段:<br>不需要通过变量,就能对内存直接进行操作。通过指针,程序能访问的内存区域就不仅限于变量所占据的数据区域。<br>在 C 中,用指针 p 指向 a 的地址,然后对 p 进行加减操作,p 就能指向 a 后面或前面的内存区域,通过 p 也就能访问这些内存区域。</p><h4 id="C-语言中的字符串类型"><a href="#C-语言中的字符串类型" class="headerlink" title="C 语言中的字符串类型"></a>C 语言中的字符串类型</h4><p>C 语言中,字符串有两种形式:</p><ol><li>用双引号括起来的字符串常量, 如”CHINA” , “C program “。</li><li>存放于字符数组中,以‘\0’字符(ASCII 码为 0)结尾</li></ol><p>存放于字符数组中的字符串常量占据内存的字节数等于字符串中字符数目加 1,多出来的是结尾字符‘\0’。<br>但是字符串的长度不包含’\0’<br>用 char 数组存放字符串,数组元素个数应该至少为字符串长度+1,以避免数组越界。<br>char 数组的内容,可以在初始化时设定,还可以用对数组元素赋值的办法任意改变其中的某个字符。</p><p>“” 也是合法的字符串常量,称为“空串”, 空串仍然会占据一个字节的存储空间,存放 ‘\0’。</p><p>如果字符串常量中包含双引号,则双引号应写为‘"’。而‘\’字符在 字符串中出现时,须连写两次,变成‘\’,以防止转译。</p><h4 id="C-语言中的结构类型"><a href="#C-语言中的结构类型" class="headerlink" title="C 语言中的结构类型"></a>C 语言中的结构类型</h4><p>两个同类型的结构变量,可以互相赋值。但是结构变量之间不能用“==”、“!=”、“<”、“>”、“<=”、“>=”进行比较运算。</p><p>一般来说,一个结构变量所占的内存空间的大小,就是结构中所有成员变量大小之和。结构变量中的各个成员变量在内存中一般是连续存放的。</p><p>并且,一个结构的成员变量可以是任何类型的,包括可以是另一个结构类型:</p><h3 id="ASCII-码和-char-类型的关系"><a href="#ASCII-码和-char-类型的关系" class="headerlink" title="ASCII 码和 char 类型的关系"></a>ASCII 码和 char 类型的关系</h3><p>char 表示一个字符型数据,其和 int 在 0-255 范围内是等价的。而字符编码采用的是 ASCII 码,所以看起来和 ASCII 有关。在用 char 进行输入、输出时其值可以被当成 ASCII 码,输入、输出函数根据这个码找到相应的字符输入或输出。</p><h2 id="0x02-使用格式化输入输出函数-printf-和-scanf"><a href="#0x02-使用格式化输入输出函数-printf-和-scanf" class="headerlink" title="0x02 使用格式化输入输出函数 printf()和 scanf()"></a>0x02 使用格式化输入输出函数 printf()和 scanf()</h2><p>在 printf 和 scanf 中可以使用以”%”开头的控制符,指明要输入或输出的数据的类型以及格式。<br>常用的格式控制符如下表所示:<br>|常用格式控制符|作 用|<br>|—|—|<br>|%d|读入或输出 int 变量|<br>|%c|读入或输出 char 变量|<br>|%f|读入或输出 float 变量,输出时保留小数点后面 6 位|<br>|%lf|读入或输出 double 变量,输出时保留小数点后面 6 位|<br>|%x|以十六进制读入或输出整型变量|<br>|%lld|读入或输出 long long 变量(64 位整数)|<br>|%nd|(如%4d,%12d)以 n 字符宽度输出整数,宽度不足时用空格填充|<br>|%0nd|( 如 %04d,%012d )以 n 字符宽度输出整数,宽度不足时用 0 填充|<br>|%.nf|(如%.4f,%.3f) 输出 double 或 float 值,精确到小数点后 n 位|</p><h3 id="scanf-的进阶使用"><a href="#scanf-的进阶使用" class="headerlink" title="scanf 的进阶使用"></a>scanf 的进阶使用</h3><p>1.用 scanf 可以一次读入多个类型不同的变量,只要输入的各项之间用空格分隔即可。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> n; <span class="keyword">char</span> c; <span class="keyword">float</span> m;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%c%f"</span>,&n,&c,&m);</span><br><span class="line"> <span class="comment">/*依次输入一个整数,一个字符,再一个整数,则它们会被分别放入n,c,m;&n代表“取n的地址",%c代表等待输入一个字符*/</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d %c %f"</span>,n,c,m);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>输入:34 k 234.45↙<br>输出:34 k 234.449997</p></blockquote><p>2.若输入的各项之间没有用空格分隔,则等待输入字符时,不会跳过空格(空格也会被当作字符读入),输入其他类型的数据时,会跳过空格。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n; <span class="keyword">char</span> c; <span class="keyword">float</span> f;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%c%f"</span>,&n,&c,&f);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d %c %f"</span>,n,c,f);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>输入:34 k 456↙<br>输出:34 0.000000</p></blockquote><pre><code>原因:c = ' ', 读入f 时,对应输入是'k',导致出错。</code></pre><p>3.如果在输入中有 scanf 中出现的非控制字符,则这些字符会被跳过。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n,m; <span class="keyword">char</span> c; <span class="keyword">float</span> f;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d %c,%f:%d"</span>,&n,&c,&f,&m);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d,%c,%f,%d"</span>,n,c,f,m);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>输入:12 k,3.75:290↙<br>输出:12,k,3.750000,290</p></blockquote><h3 id="有关-sprintf-和-sscanf-函数"><a href="#有关-sprintf-和-sscanf-函数" class="headerlink" title="有关 sprintf()和 sscanf()函数"></a>有关 sprintf()和 sscanf()函数</h3><p>参考这篇文章:<a href="https://www.cnblogs.com/jikebiancheng/p/6285525.html" target="_blank" rel="noopener">浅析 C 语言中 printf(),sprintf(),scanf(),sscanf()的用法和区别-极客编程-博客园</a></p><h2 id="0x03-分支结构与循环控制结构"><a href="#0x03-分支结构与循环控制结构" class="headerlink" title="0x03 分支结构与循环控制结构"></a>0x03 分支结构与循环控制结构</h2><h3 id="分支结构"><a href="#分支结构" class="headerlink" title="分支结构"></a>分支结构</h3><h4 id="if-语句常见错误"><a href="#if-语句常见错误" class="headerlink" title="if 语句常见错误"></a>if 语句常见错误</h4><p>1.错把赋值符当逻辑运算符来使用:<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>( a = <span class="number">0</span> ) <span class="comment">//a = 0的值是0</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"hello"</span>);</span><br><span class="line"><span class="keyword">if</span>( a = <span class="number">5</span> ) <span class="comment">// a = 5的值是5</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Hi"</span>);</span><br><span class="line"></span><br><span class="line">=> Hi</span><br></pre></td></tr></table></figure><p>2.互相矛盾的多个条件,如果确实只希望执行其中一个分支,应该用 if 和多个 else if,而不要写多个 if</p><p>wrong example:</p><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="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>( a >=<span class="number">0</span> && a < <span class="number">5</span> )</span><br><span class="line">a = <span class="number">8</span>;</span><br><span class="line"><span class="keyword">if</span>( a >= <span class="number">5</span> && a < <span class="number">10</span> )</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"hello"</span>);</span><br><span class="line"><span class="keyword">if</span>( a > <span class="number">10</span> && a < <span class="number">20</span>)</span><br><span class="line">.....</span><br><span class="line"><span class="keyword">if</span>( a >= <span class="number">20</span>)</span><br><span class="line">.....</span><br></pre></td></tr></table></figure><p>最终会输出 hello</p><p>right example:</p><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="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>( a >=<span class="number">0</span> && a < <span class="number">5</span> )</span><br><span class="line">a = <span class="number">8</span>;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>( a >= <span class="number">5</span> && a < <span class="number">10</span> )</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"hello"</span>);</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>( a > <span class="number">10</span> && a < <span class="number">20</span>)</span><br><span class="line">.....</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">.....</span><br></pre></td></tr></table></figure><p>不会输出 hello</p><h4 id="switch-语句常见错误"><a href="#switch-语句常见错误" class="headerlink" title="switch 语句常见错误"></a>switch 语句常见错误</h4><p>switch 语句在进入某个 case 分支后,会一直执行到第一个碰到的“break;”,即使这个“break;”是在后面的 case 分支里面。如果没有碰到“break;”,则会向下一直执行到 switch 语句末尾的“}”,包括“default:”部分的语句组也会被执行。</p><p>因此,在运用 switch 语句时,一定要根据题目加上 break 关键字。</p><h4 id="if-else-与-switch-case-语法的区别"><a href="#if-else-与-switch-case-语法的区别" class="headerlink" title="if-else 与 switch-case 语法的区别"></a>if-else 与 switch-case 语法的区别</h4><p>if 语句,if else if 语句和 switch case 语句都属于流程控制语句。</p><p>在只需要判断一个条件的时候,自然是使用 if 语句方便有效;但是当判断条件很多的时候,我们可以使用多个 if 语句或者 if…else if 语句或者 switch case 语句。</p><p>if…else if 语句和多个 if 语句的区别还是很大的,if…else if 在任何一个环节满足条件的时候就将会终止判断,只处理一个满足条件的情况;而对于多个 if 语句,将会对每一个判断条件进行判断,自然而然会导致程序的执行效率降低。在多个判断条件的情况下,使用 if…else if 语句相对于使用多个 if 语句而言,可以减少程序的判断次数,提高效率。</p><p>在多个判断条件的情况下,不仅可以使用 if…else if 语句,还可以使用 switch case 语句。一般情况下,当判断条件较多的情况下,使用 switch case 语句的效率会高于使用 if…else if 语句。switch…case 与 if…else if 的根本区别在于,switch…case 会生成一个跳转表来指示实际的 case 分支的地址,而这个跳转表的索引号与 switch 变量的值是相等的。从而,switch…case 不用像 if…else if 那样遍历条件分支直到命中条件,而只需访问对应索引号的表项从而到达定位分支的目的。</p><p>因此,当只有分支比较少的时候,if 效率比 switch 高(因为 switch 需要生成跳转表)。若分支比较多,那当然是 switch 更高效也更清晰。</p><h3 id="循环结构"><a href="#循环结构" class="headerlink" title="循环结构"></a>循环结构</h3><h4 id="for-循环语句结构特点"><a href="#for-循环语句结构特点" class="headerlink" title="for 循环语句结构特点"></a>for 循环语句结构特点</h4><p><img src="https://cdn.jsdelivr.net/gh/Zam-0703/Pictures/img/for.png" alt="for语句结构"><br>for 语句结构:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>( 表达式<span class="number">1</span> ;表达式<span class="number">2</span>;表达式<span class="number">3</span>) {</span><br><span class="line">语句组</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><em>for 循环结构里的“表达式 1”和“表达式 3”都可以是用逗号连接的若干个表达式。</em></p><h4 id="for-循环括号内三个语句的执行时间"><a href="#for-循环括号内三个语句的执行时间" class="headerlink" title="for 循环括号内三个语句的执行时间"></a>for 循环括号内三个语句的执行时间</h4><p>执行过程:</p><ol><li>计算“表达式 1”。</li><li>计算“表达式 2”,若其值为 true,则执行“{ }”中的语句组,然后转到 3);若为 false,则不再执行“{}”中的语句组,for 语句结束,转到 5)。</li><li>计算“表达式 3”。</li><li>转到 2)。</li><li>从 for 语句后面继续往下执行程序。</li></ol><h4 id="for-语句的循环控制变量特点"><a href="#for-语句的循环控制变量特点" class="headerlink" title="for 语句的循环控制变量特点"></a>for 语句的循环控制变量特点</h4><p>循环控制变量定义在”表达式 1”中,则其只在 for 语句内部起作用,可以不用担心循环控制变量重名。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>;i < <span class="number">26</span>; ++i ){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%c\n"</span>,<span class="keyword">char</span>(<span class="string">'a'</span>+i ));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>;i < <span class="number">26</span>; i+=<span class="number">2</span> ){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%c\n"</span>,<span class="keyword">char</span>(<span class="string">'a'</span>+i ));</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d\n"</span>,i);<span class="comment">//此处的i和for里面的i无关</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">>最后的一个输出:<span class="number">5</span></span><br><span class="line"></span><br><span class="line">#### <span class="keyword">while</span>循环语句结构特点</span><br><span class="line">```c</span><br><span class="line"><span class="keyword">while</span>(表达式) {</span><br><span class="line">语句组</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>若情境是并非到达指定次数,而是满足某条件时即停止循环,则适合用 while 语句来实现循环。</p><h4 id="while-循环的执行过程"><a href="#while-循环的执行过程" class="headerlink" title="while 循环的执行过程"></a>while 循环的执行过程</h4><ol><li>判断“表达式”是否为真,如果不为真,则转 4)</li><li>执行“语句组”</li><li>转 1)</li><li>while 语句结束,继续执行 while 语句后面的语句。</li></ol><h4 id="do……while-循环语句结构特点"><a href="#do……while-循环语句结构特点" class="headerlink" title="do……while 循环语句结构特点"></a>do……while 循环语句结构特点</h4><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span> {</span><br><span class="line">语句组</span><br><span class="line">} <span class="keyword">while</span>(表达式);</span><br></pre></td></tr></table></figure><p>如果希望循环至少要执行一次,就可以使用 do…while 语句。</p><h4 id="do……while-循环语句的执行过程"><a href="#do……while-循环语句的执行过程" class="headerlink" title="do……while 循环语句的执行过程"></a>do……while 循环语句的执行过程</h4><p>每执行一次循环后,都要判断“表达式”的值是否为真,如果真就继续循环,如果为假,就停止循环。</p><h4 id="while-和-do……while-语句的区别"><a href="#while-和-do……while-语句的区别" class="headerlink" title="while 和 do……while 语句的区别"></a>while 和 do……while 语句的区别</h4><p>while 先判断后执行,do while 先执行后判断<br>当不满足循环条件时,while 循环一次都不会执行,do while 循环至少执行一次</p><h4 id="break-与-continue-语句"><a href="#break-与-continue-语句" class="headerlink" title="break 与 continue 语句"></a>break 与 continue 语句</h4><p>break 语句可以出现在循环体中(for、while、do…while 循环均可),其作用是跳出循环。<br>并且在多重循环的情况下,break 语句只能跳出直接包含它的那一重循环。</p><p>example:<br>_找兄弟数_:如果两个不同的正整数,他们的和是他们的积的因子,就称这两个数为兄弟数,小的称为弟数,大的称为兄数。先后输入正整数 n 和 m(n < m) , 请在 n 至 m 这 m-n+1 个数中,找出一对兄弟数。如果找不到,就输出“No Solution.”。如果能找到,就找出和最小的那一对;如果有多对兄弟数和相同且都是最小,就找出弟数最小的那一对。</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n,m;</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d %d"</span>,&n,&m );</span><br><span class="line"> <span class="keyword">int</span> a = m + <span class="number">1</span>,b = m + <span class="number">1</span>; <span class="comment">//a,b记录已经找到的最佳兄弟数,a是弟数,b是兄数</span></span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> i = n; i < m ; ++i ) { <span class="comment">//取弟数,共m-n种取法</span></span><br><span class="line"> <span class="keyword">if</span>( i > (a + b)/<span class="number">2</span> + <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">break</span>; <span class="comment">// 跳出外重循环</span></span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> j = i + <span class="number">1</span>; j <= m; ++j ) { <span class="comment">//取兄数</span></span><br><span class="line"> <span class="keyword">if</span>( i + j > a + b ) <span class="keyword">break</span>; <span class="comment">// 跳出内重循环</span></span><br><span class="line"> <span class="keyword">if</span>( i * j % (i + j) == <span class="number">0</span> ) { <span class="comment">//发现兄弟数</span></span><br><span class="line"> <span class="keyword">if</span>( i + j < a + b) { <span class="comment">//发现和更小的兄弟数</span></span><br><span class="line"> a = i; b = j ; <span class="comment">//更新已找到的最佳兄弟数</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>( i + j == a + b && i < a)</span><br><span class="line"> <span class="comment">//发现和相同但弟数更小的兄弟数</span></span><br><span class="line"> a = i; b = j; <span class="comment">//更新已找到的最佳兄弟数</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>( a == m + <span class="number">1</span> ) <span class="comment">//没找到兄弟数</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"No solution."</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d,%d\n"</span>,a,b);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>continue 语句可以出现在循环体中(for、while、do…while 循环均可),其作用是立即结束本次循环,并回到循环开头判断是否要进行下一次循环。<br>在多重循环的情况下,continue 只对直接包含它的那重循环起作用。</p><p>example:<br><em>找 10 以内的偶数</em></p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">1</span>;i <= <span class="number">10</span> ;++i ) {</span><br><span class="line"> <span class="keyword">if</span>( i % <span class="number">2</span> )</span><br><span class="line"> <span class="keyword">continue</span>; <span class="comment">//导致不执行后面的语句,回到循环开头</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d,"</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">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>输出:2,4,6,8,10,</p></blockquote><h2 id="0x04-初始化数组"><a href="#0x04-初始化数组" class="headerlink" title="0x04 初始化数组"></a>0x04 初始化数组</h2><h3 id="一维数字数组的初始化"><a href="#一维数字数组的初始化" class="headerlink" title="一维数字数组的初始化"></a>一维数字数组的初始化</h3><p>在通过<code>type arrayName [ arraySize ];</code>语句声明数组之后,需要对数组进行初始化。</p><p>在 C 中,我们可以逐个初始化数组,也可以使用一个初始化语句,如下所示:<br><code>int test[5] = {1, 2, 3, 4, 5};</code></p><p>需要注意的是,声明数组时方括号内的数字代表的是数组长度。数组的元素都是从 0 开始标号的,因此数组的第一个元素是 arrayName[0],最后一个元素是 arrayName[数组的总大小减去 1]。需要牢记这一点以免发生数组越界情况。</p><p>例如,有以下程序:</p><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></pre></td><td class="code"><pre><span class="line"> <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"> <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"> <span class="meta">#<span class="meta-keyword">define</span> Lenth 5</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">int</span> test[Lenth] = { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span> };</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < Lenth; i++) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"test数组的第%d个元素为%d,\n"</span>, i+<span class="number">1</span>, test[i]);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行结果为: >test 数组的第 1 个元素为 1,<br>test 数组的第 2 个元素为 2,<br>test 数组的第 3 个元素为 3,<br>test 数组的第 4 个元素为 4,<br>test 数组的第 5 个元素为 5,</p><p><strong>大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。</strong></p><p><strong>若是大括号{ }之间的值小于我们在数组声明时在方括号中指定的元素数目,则没有值与之对应的数组元素自动赋 0</strong></p><p><strong>只能给元素逐个赋值,不能给数组整体赋值。例如给十个元素全部赋 1 值,只能写为:int a[10]={1,1,1,1,1,1,1,1,1,1};而不能写为:int a[10]=1.</strong></p><p><strong>如不给可初始化的数组赋初值,则全部元素均为 0 值。</strong></p><p>以上规则也适用于 <strong>其他类型的数组</strong><br>例如,有以下程序:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h> </span></span></span><br><span class="line"> <span class="meta">#<span class="meta-keyword">define</span> Lenth 6</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> test[Lenth] = { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span> };</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < Lenth; i++) {</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"test 数组的第%d 个元素为%d,\n"</span>, i+<span class="number">1</span>, test[i]);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行结果为: >test 数组的第 1 个元素为 1,<br>test 数组的第 2 个元素为 2,<br>test 数组的第 3 个元素为 3,<br>test 数组的第 4 个元素为 4,<br>test 数组的第 5 个元素为 5,<br>test 数组的第 6 个元素为 0,</p><p>如果在初始化时省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果我们初始化:<code>int test[] = {1, 2, 3, 4, 5};</code><br>那么创建的这个数组,它与前一个例子中所创建的数组是完全相同的。</p><h3 id="一维字符数组的初始化"><a href="#一维字符数组的初始化" class="headerlink" title="一维字符数组的初始化"></a>一维字符数组的初始化</h3><p>C 语言允许用字符串的方式对数组作初始化赋值。例如:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[<span class="number">9</span>]={<span class="string">'c'</span>,<span class="string">' '</span>,<span class="string">'p'</span>,<span class="string">'r'</span>,<span class="string">'o'</span>,<span class="string">'g'</span>,<span class="string">'r'</span>,<span class="string">'a'</span>,<span class="string">'m'</span>};</span><br></pre></td></tr></table></figure><p>可写为:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[<span class="number">10</span>]={<span class="string">"C program"</span>}; 或去掉{}写为:</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[<span class="number">10</span>]=<span class="string">"C program"</span>;</span><br></pre></td></tr></table></figure><p>需要注意的是,用字符串给字符数组赋值时由于要添加结束符 ‘<code>\0</code>‘,数组的长度要比字符串的长度(字符串长度不包括 ‘<code>\0'</code>)大 1。例如:<br>char str[] = “C program”;<br>该数组在内存中的实际存放情况为:<br><img src="http://c.biancheng.net/cpp/uploads/allimg/120129/sfdsgfsge.png" alt="数组在内存中的存放情况"><br>字符串长度为 9,数组长度为 10。<br>因此我们需要增加一个字符数组长度用来存放\0。</p><p>并且,上述这种字符数组的整体赋值只能在字符数组初始化时使用,不能用于字符数组的赋值,字符数组的赋值只能对其元素一一赋值,下面的赋值方法是错误的。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> str[];</span><br><span class="line">str=<span class="string">"I am happy"</span>;<span class="comment">//错误,字符数组的赋值只能按元素一一赋值</span></span><br></pre></td></tr></table></figure><p>当对全体元素赋初值时也可以省去长度说明。例如:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[]={<span class="string">'c'</span>,<span class="string">' '</span>,<span class="string">'p'</span>,<span class="string">'r'</span>,<span class="string">'o'</span>,<span class="string">'g'</span>,<span class="string">'r'</span>,<span class="string">'a'</span>,<span class="string">'m'</span>};</span><br></pre></td></tr></table></figure><p>这时 C 数组的长度自动定为 9。</p><h3 id="二维数字数组的初始化"><a href="#二维数字数组的初始化" class="headerlink" title="二维数字数组的初始化"></a>二维数字数组的初始化</h3><p>example:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a[<span class="number">5</span>][<span class="number">3</span>]={{<span class="number">80</span>,<span class="number">75</span>,<span class="number">92</span>},{<span class="number">61</span>,<span class="number">65</span>},{<span class="number">59</span>,<span class="number">63</span>,<span class="number">70</span>},{<span class="number">85</span>,<span class="number">90</span>},{<span class="number">76</span>,<span class="number">77</span>,<span class="number">85</span>}};</span><br></pre></td></tr></table></figure><p>每个内层的{},初始化数组中的一行。<br>同样地,二维数组初始化时,如果对每行都进行了初始化,则也可以不给出行数:<br>example:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a[][<span class="number">3</span>]={ {<span class="number">80</span>,<span class="number">75</span>,<span class="number">92</span>},{<span class="number">61</span>,<span class="number">65</span>} };</span><br></pre></td></tr></table></figure><p>a 是一个 2 行 3 列的数组,a[1][2]被初始化成 0。</p><h3 id="二维字符数组的初始化"><a href="#二维字符数组的初始化" class="headerlink" title="二维字符数组的初始化"></a>二维字符数组的初始化</h3><p>通常情况下,二维数组的每一行分别使用一个字符串进行初始化。 例如:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[<span class="number">3</span>][<span class="number">8</span>]={{<span class="string">"apple"</span>},{<span class="string">"orange"</span>},{<span class="string">"banana"</span>}};</span><br></pre></td></tr></table></figure><p>等价于:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> c[<span class="number">3</span>][<span class="number">8</span>]={<span class="string">"apple"</span>,<span class="string">"orange"</span>,<span class="string">"banana"</span>};</span><br></pre></td></tr></table></figure><p>以上两条初始化语句中,二维数组的第一维大小均可省略。数组 c 的逻辑结构如下所示:<br>| |0|1|2|3|4|5|6|7|<br>|–|–|–|–|–|–|–|–|–|<br>|c[0]|a|p|p|l|e|\0|\0|\0|<br>|c[1]|o|r|a|n|g|e|\0|\0|<br>|c[2]|b|a|n|a|n|a|\0|\0|</p><h3 id="二维数字数组的存放方式"><a href="#二维数字数组的存放方式" class="headerlink" title="二维数字数组的存放方式"></a>二维数字数组的存放方式</h3><p>数组 T a[N][m] 每一行都有 M 个元素<br>第 i 行的元素就是 a[i][0]、a[i][1]……a[i][m-1]。<br>同一行的元素,在内存中是连续存放的。<br>第 j 列的元素的元素,就是 a[0][j]、a[1][j]……a[N-1][j]。<br>a[0][0]是数组中地址最小的元素。如果 a[0][0]存放在地址 n,则 a[i][j]存放的地址就是:n + i × M× sizeof(T) + j × sizeof(T)</p><h3 id="数组越界"><a href="#数组越界" class="headerlink" title="数组越界"></a>数组越界</h3><p>数组元素的下标,可以是任何整数,可以是负数,也可以大于数组的元素个数。不会导致编译错误:<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a[<span class="number">10</span>];</span><br><span class="line">a[<span class="number">-2</span>] = <span class="number">5</span>;</span><br><span class="line">a[<span class="number">200</span>] = <span class="number">10</span>;</span><br><span class="line">a[<span class="number">10</span>] = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">int</span> m = a[<span class="number">30</span>];</span><br></pre></td></tr></table></figure><p>但运行时很可能会出错!<br>a[-2] = 5; a[200] = 10; a[10] = 20;int m = a[30];均可能导致程序运行出错!!!<br>因为可能引起意外修改其他变量的值,导致程序运行结果不正确<br>可能试图访问不该访问的内存区域,导致程序崩溃<br>数组越界的程序,用某些编译器编译后可能可以正确运行,换一个编译器编译后就运行错误<br>最可怕的是,编译器不会报错。</p><p>因此,我们在使用数组时,最好能初始化/声明地大一些</p><h2 id="0x05-标识符的作用域,结构体"><a href="#0x05-标识符的作用域,结构体" class="headerlink" title="0x05 标识符的作用域,结构体"></a>0x05 标识符的作用域,结构体</h2><h3 id="标识符的作用域"><a href="#标识符的作用域" class="headerlink" title="标识符的作用域"></a>标识符的作用域</h3><h4 id="C-语言中的全局变量、局部变量、静态变量"><a href="#C-语言中的全局变量、局部变量、静态变量" class="headerlink" title="C 语言中的全局变量、局部变量、静态变量"></a>C 语言中的全局变量、局部变量、静态变量</h4><p>要想学习变量的作用域,首先要搞懂 C 中的全局变量、局部变量、静态变量</p><p>局部变量:定义在函数内部的变量叫局部变量(函数的形参也是局部变量)</p><p>全局变量:定义在所有函数的外面的变量叫全局变量<br>全局变量在所有函数中均可以使用,局部变量只能在定义它的函数内部使用</p><p>静态变量:全局变量都是静态变量。局部变量定义时如果前面加了“static”关键字,则该变量也成为静态变量<br>静态变量的存放地址,在整个程序运行期间,都是固定不变的<br>非静态变量(一定是局部变量)地址每次函数调用时都可能不同,在函数的一次执行期间不变<br>如果未明确初始化,则静态变量会被自动初始化成全 0(每个 bit 都是 0),局部非静态变量的值则随机</p><h4 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h4><p>变量名、函数名、类型名统称为“标识符”。一个标识符能够起作用的范围,叫做该标识符的作用域</p><p>在一个标识符的作用域之外使用该标识符,会导致“标识符没有定义”的编译错误。使用标识符的语句,必须出现在它们的声明或定义之后<br>在单文件的程序中,结构、函数和全局变量的作用域是其定义所在的整个文件</p><p>函数形参的作用域是整个函数<br>局部变量的作用域,是从定义它的语句开始,到包含它的最内层的那一对大括号“{}”的右大括号 “}”为止。<br>for 循环里定义的循环控制变量,其作用域就是整个 for 循环<br>同名标示符的作用域,可能一个被另一个包含。则在小的作用域里,作用域大的那个标识符被屏蔽,不起作用。</p><h4 id="生存期"><a href="#生存期" class="headerlink" title="生存期"></a>生存期</h4><p>所谓变量的“生存期”,指的是在此期间,变量占有内存空间,其占有的内存空间只能归它使用,不会被用来存放别的东西。<br>而变量的生存期终止,就意味着该变量不再占有内存空间,它原来占有的内存空间,随时可能被派做他用。</p><p>全局变量的生存期,从程序被装入内存开始,到整个程序结束。<br>静态局部变量的生存期,从定义它语句第一次被执行开始,到整个程序结束为止。<br>函数形参的生存期从函数执行开始,到函数返回时结束。非静态局部变量的生存期,从执行到定义它的语句开始,一旦程序执行到了它的作用域之外,其生存期就终止。</p><h3 id="使用-struct-定义结构体"><a href="#使用-struct-定义结构体" class="headerlink" title="使用 struct 定义结构体"></a>使用 struct 定义结构体</h3><p>我们可以使用“struct”关键字来定义一个“结构”,也就是说定义了一个新的结构数据类型:<br>定义方式:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> 结构名</span></span><br><span class="line"><span class="class">{</span></span><br><span class="line">类型名 成员变量名;</span><br><span class="line">类型名 成员变量名;</span><br><span class="line">类型名 成员变量名;</span><br><span class="line">……</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Student</span> {</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> ID;</span><br><span class="line"><span class="keyword">char</span> Name[<span class="number">20</span>];</span><br><span class="line"><span class="keyword">float</span> GPA;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>在经过这条语句之后 Student 即成为自定义类型的名字,可以用来定义变量<br><code>Stuent s1,s2;</code></p><h3 id="使用结构体获得、写入结构体内部的成员变量"><a href="#使用结构体获得、写入结构体内部的成员变量" class="headerlink" title="使用结构体获得、写入结构体内部的成员变量"></a>使用结构体获得、写入结构体内部的成员变量</h3><p>一个结构变量的成员变量,可以完全和一个普通变量一样来使用,也可以取得其地址。使用形式:<br>结构变量名.成员变量名<br>example:对于以下定义的 StudentEx 与 Date 结构体数据类型</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Date</span> {</span></span><br><span class="line"><span class="keyword">int</span> year;</span><br><span class="line"><span class="keyword">int</span> month;</span><br><span class="line"><span class="keyword">int</span> day;</span><br><span class="line">};</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">StudentEx</span> {</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> ID;</span><br><span class="line"><span class="keyword">char</span> Name[<span class="number">20</span>];</span><br><span class="line"><span class="keyword">float</span> GPA;</span><br><span class="line">Date birthday;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>我们可以运行以下的 main()代码段:</p><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></pre></td><td class="code"><pre><span class="line">StudentEx stu;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%f"</span>, &stu.GPA);</span><br><span class="line">stu.ID = <span class="number">12345</span>;</span><br><span class="line">stu.Name[]={<span class="string">'Z'</span>,<span class="string">'a'</span>,<span class="string">'m'</span>};</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%f"</span>,stu.GPA);</span><br><span class="line">stu.birthday.year = <span class="number">1984</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> * p = & stu.ID; <span class="comment">//p指向stu中的ID成员变量</span></span><br></pre></td></tr></table></figure><h3 id="结构变量的初始化"><a href="#结构变量的初始化" class="headerlink" title="结构变量的初始化"></a>结构变量的初始化</h3><p>结构变量可以在定义时进行初始化:<br>例如对上面的例子,我们可以通过以下语句对结构变量进行初始化</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">StudentEx stu = { <span class="number">1234</span>,<span class="string">"Tom"</span>,<span class="number">3.78</span>,{ <span class="number">1984</span>,<span class="number">12</span>,<span class="number">28</span> }};</span><br></pre></td></tr></table></figure><h3 id="指向结构变量的指针"><a href="#指向结构变量的指针" class="headerlink" title="指向结构变量的指针"></a>指向结构变量的指针</h3><h4 id="定义指向结构变量的指针"><a href="#定义指向结构变量的指针" class="headerlink" title="定义指向结构变量的指针"></a>定义指向结构变量的指针</h4><p>方式:<br><code>结构名 * 指针变量名;</code><br>example:</p><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></pre></td><td class="code"><pre><span class="line">StudentEx * pStudent;<span class="comment">//定义了* pStudent为StudentEx类型的指针</span></span><br><span class="line">StudentEx Stu1;</span><br><span class="line">pStudent = & Stu1;<span class="comment">//使得pStudent指针指向stu1变量的起始位置</span></span><br><span class="line">StudentEx Stu2 = * pStudent; <span class="comment">//将pStudent指针指向的stu1变量赋值给stu2变量</span></span><br></pre></td></tr></table></figure><h4 id="通过指针,访问其指向的结构变量的成员变量"><a href="#通过指针,访问其指向的结构变量的成员变量" class="headerlink" title="通过指针,访问其指向的结构变量的成员变量"></a>通过指针,访问其指向的结构变量的成员变量</h4><p>方式:</p><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></pre></td><td class="code"><pre><span class="line">指针->成员变量名</span><br><span class="line"><span class="keyword">or</span></span><br><span class="line">(* 指针).成员变量名</span><br></pre></td></tr></table></figure><p>example:<br>对于‘使用结构体获得、写入结构体内部的成员变量’中定义的结构体<br>可以使用以下语句:</p><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></pre></td><td class="code"><pre><span class="line">StudentEx Stu;</span><br><span class="line">StudentEx * pStu;</span><br><span class="line">pStu = & Stu;</span><br><span class="line">pStu->ID = <span class="number">12345</span>;</span><br><span class="line">( * pStu).GPA = <span class="number">3.48</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>,Stu.ID) <span class="comment">//输出 12345</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%f\n"</span>,Stu.GPA); <span class="comment">//输出 3.48</span></span><br></pre></td></tr></table></figure><h2 id="0x06-C-语言函数"><a href="#0x06-C-语言函数" class="headerlink" title="0x06 C 语言函数"></a>0x06 C 语言函数</h2><h3 id="函数的定义"><a href="#函数的定义" class="headerlink" title="函数的定义"></a>函数的定义</h3><p>一般来说函数的定义必须出现在函数调用语句之前,否则调用语句编译出错<br>函数的定义方式:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">返回值类型 函数名(参数1类型 参数1名称, 参数2类型 参数2名称……)</span><br><span class="line">{</span><br><span class="line">语句组(即“函数体”)</span><br><span class="line">return 返回值;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果函数不需要返回值,则“返回值类型”可以写“void”</p><h3 id="函数的调用"><a href="#函数的调用" class="headerlink" title="函数的调用"></a>函数的调用</h3><p>调用函数语句:<br><code>函数名(参数1,参数2,……)</code></p><h3 id="return-语句"><a href="#return-语句" class="headerlink" title="return 语句"></a>return 语句</h3><p>对函数的调用,也是一个表达式。函数调用表达式的值,由函数内部的 return 语句决定。<br>return 语句语法如下:<br><code>return 返回值;</code><br>return 语句的功能是结束函数的执行,并将“返回值”作为结果返回。“返回值”是常量、变量或复杂的表达式均可。<br>如果函数返回值类型为“void”,return 语句就直接写:<br><code>return ;</code><br>需要注意的是,return 语句作为函数的出口,可以在函数中多次出现。多个 return 语句的“返回值”可以不同。在哪个 return 语句结束函数,函数的返回值就和哪个 return 语句里面的“返回值”相等。</p><h2 id="0x07-C-语言的指针"><a href="#0x07-C-语言的指针" class="headerlink" title="0x07 C 语言的指针"></a>0x07 C 语言的指针</h2><h3 id="C-语言指针的定义"><a href="#C-语言指针的定义" class="headerlink" title="C 语言指针的定义"></a>C 语言指针的定义</h3><p>T _ p ; // T 可以是任何类型的名字,比如 int, double ,char 等等。<br>p 的类型: T _ _p 的类型: T<br>通过表达式 _ p,可以读写从地址 p 开始的 sizeof(T)个字节<br>*p 等价于存放在地址 p 处的一个 T 类型的变量</p><ul><li>间接引用运算符 sizeof(T*) 4 字节(64 位计算机上可能 8 字节)</li></ul><h3 id="指针的用法"><a href="#指针的用法" class="headerlink" title="指针的用法"></a>指针的用法</h3><p>& : 取地址运算符<br>&x : 变量 x 的地址(即指向 x 的指针)<br>对于类型为 T 的变量 x,&x 表示变量 x 的地址(即指向 x 的指针) &x 的类型是 T * 。<br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> ch1 = <span class="string">'A'</span>;</span><br><span class="line"><span class="keyword">char</span> * pc = &ch1; <span class="comment">// 使得pc 指向变量ch1</span></span><br><span class="line"> * pc = <span class="string">'B'</span>; <span class="comment">// 使得ch1 = 'B'</span></span><br><span class="line"> <span class="keyword">char</span> ch2 = * pc; <span class="comment">// 使得ch2 = ch1</span></span><br><span class="line"> pc = & ch2; <span class="comment">// 使得pc 指向变量ch2</span></span><br><span class="line"> * pc = <span class="string">'D'</span>; <span class="comment">// 使得ch2 = 'D'</span></span><br></pre></td></tr></table></figure><h3 id="指针的赋值"><a href="#指针的赋值" class="headerlink" title="指针的赋值"></a>指针的赋值</h3><p><strong>不同类型的指针,如果不经过强制类型转换,不能直接互相赋值</strong><br>example:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> * pn, <span class="keyword">char</span> * pc, <span class="keyword">char</span> c = <span class="number">0x65</span>;</span><br><span class="line">pn = pc; <span class="comment">//类型不匹配,编译出错</span></span><br><span class="line">pn = & c; <span class="comment">//类型不匹配,编译出错</span></span><br><span class="line">pn = (<span class="keyword">int</span> * ) & c;</span><br><span class="line"><span class="keyword">int</span> n = * pn; <span class="comment">//n值不确定</span></span><br><span class="line">* pn = <span class="number">0x12345678</span>; <span class="comment">//编译能过但运行可能出错</span></span><br></pre></td></tr></table></figure><h3 id="指针的运算"><a href="#指针的运算" class="headerlink" title="指针的运算"></a>指针的运算</h3><p>1) 两个 <strong>同类型</strong> 的指针变量,可以比较大小</p><p>若地址 p1<地址 p2,则 p1< p2 值为真。<br>若地址 p1=地址 p2,则 p1== p2 值为真。<br>若地址 p1>地址 p2,则 p1 > p2 值为真。</p><ol start="2"><li>两个 <strong>同类型</strong> 的指针变量,可以相减</li></ol><p>运算规则:<br>两个 T * 类型的指针 p1 和 p2<br>p1 – p2 = ( 地址 p1 – 地址 p2 ) / sizeof(T)</p><p>例:int _ p1, _ p2;<br>若 p1 指向地址 2000,p2 指向地址 600, 则<br>p1 – p2 = (1000 – 600)/sizeof(int) = (2000 – 600)/4 = 350</p><p>3)指针变量加减一个整数的结果是指针</p><p>例如:<br>p : T * 类型的指针<br>n : 整数类型的变量或常量</p><p>则 p+n : T _ 类型的指针,指向地址: 地址 p + n × sizeof(T)<br>n+p, p-n , _ (p+n), * (p-n)等同理</p><ol start="4"><li>指针变量可以自增、自减</li></ol><p>若 T* 类型的指针 p 指向地址 n<br>则 p++, ++p : p 指向 n + sizeof(T) p–, –p : p 指向 n - sizeof(T)</p><p>5)指针可以用下标运算符“[ ]”进行运算<br>若 p 是一个 T _ 类型的指针, n 是整数类型的变量或常量<br>则 p[n] 等价于 _ (p+n)</p><h2 id="Task-End"><a href="#Task-End" class="headerlink" title="Task End"></a>Task End</h2>]]></content>
<summary type="html">
<p>在EIC-AST的第一个Task~</p>
</summary>
<category term="Learning" scheme="http://blog.zam-meow.cn/categories/Learning/"/>
<category term="C" scheme="http://blog.zam-meow.cn/tags/C/"/>
<category term="AST of EIC,HUST" scheme="http://blog.zam-meow.cn/tags/AST-of-EIC-HUST/"/>
<category term="Task" scheme="http://blog.zam-meow.cn/tags/Task/"/>
</entry>
<entry>
<title>Hello World</title>
<link href="http://blog.zam-meow.cn/2019/10/04/hello-world/"/>
<id>http://blog.zam-meow.cn/2019/10/04/hello-world/</id>
<published>2019-10-04T03:33:56.908Z</published>
<updated>2020-04-16T02:14:27.700Z</updated>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p><a id="more"></a><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>]]></content>
<summary type="html">
<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
</summary>
</entry>
</feed>