-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
561 lines (344 loc) · 761 KB
/
atom.xml
File metadata and controls
561 lines (344 loc) · 761 KB
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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Keep It Simple And Stupid</title>
<subtitle>Caoxl-Hexo-caoxl.com</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://blog.caoxl.com/"/>
<updated>2023-05-05T08:15:29.259Z</updated>
<id>http://blog.caoxl.com/</id>
<author>
<name>CAO XIAN LIANG</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Golang 每日一题 202304篇</title>
<link href="http://blog.caoxl.com/2023/05/05/Golang-Daily-Questions-5/"/>
<id>http://blog.caoxl.com/2023/05/05/Golang-Daily-Questions-5/</id>
<published>2023-05-05T01:33:02.000Z</published>
<updated>2023-05-05T08:15:29.259Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>每月一次,重在积累</p></blockquote><span id="more"></span><h2 id="Go每日一题-slice"><a href="#Go每日一题-slice" class="headerlink" title="Go每日一题(slice)"></a>Go每日一题(slice)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> s2 := s1[<span class="number">1</span>:]</span><br><span class="line"> s2[<span class="number">1</span>] = <span class="number">4</span></span><br><span class="line"> fmt.Println(s1)</span><br><span class="line"> s2 = <span class="built_in">append</span>(s2, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br><span class="line"> fmt.Println(s1)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析"><a href="#答案解析" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>[1 2 4]<br>[1 2 4]</p></blockquote><p>当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组,这会导致 s2[1] = 4 语句影响 s1。</p><p>而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1。</p><h2 id="Go每日一题-if"><a href="#Go每日一题-if" class="headerlink" title="Go每日一题(if)"></a>Go每日一题(if)</h2><p>下面选项正确的是?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> a := <span class="number">1</span>; <span class="literal">false</span> {</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> b := <span class="number">2</span>; <span class="literal">false</span> {</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">println</span>(a, b)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 1 2</li><li>B. compilation error</li></ul><h3 id="答案解析-1"><a href="#答案解析-1" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br><a href="https://studygolang.com/articles/35587">https://studygolang.com/articles/35587</a></p></blockquote><h2 id="Go每日一题-map-遍历"><a href="#Go每日一题-map-遍历" class="headerlink" title="Go每日一题(map 遍历)"></a>Go每日一题(map 遍历)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="keyword">map</span>[<span class="type">int</span>]<span class="type">string</span>{<span class="number">0</span>:<span class="string">"zero"</span>,<span class="number">1</span>:<span class="string">"one"</span>}</span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k,v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-2"><a href="#答案解析-2" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>0 zero<br>1 one<br>// 或者<br>1 one<br>0 zero</p></blockquote><p>map 的输出是无序的。</p><h2 id="Go每日一题-defer"><a href="#Go每日一题-defer" class="headerlink" title="Go每日一题(defer)"></a>Go每日一题(defer)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := <span class="number">1</span></span><br><span class="line"> b := <span class="number">2</span></span><br><span class="line"> <span class="keyword">defer</span> calc(<span class="string">"1"</span>, a, calc(<span class="string">"10"</span>, a, b))</span><br><span class="line"> a = <span class="number">0</span></span><br><span class="line"> <span class="keyword">defer</span> calc(<span class="string">"2"</span>, a, calc(<span class="string">"20"</span>, a, b))</span><br><span class="line"> b = <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">calc</span><span class="params">(index <span class="type">string</span>, a, b <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> ret := a + b</span><br><span class="line"> fmt.Println(index, a, b, ret)</span><br><span class="line"> <span class="keyword">return</span> ret</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-3"><a href="#答案解析-3" class="headerlink" title="答案解析"></a>答案解析</h3><figure class="highlight markdown"><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">10 1 2 3</span><br><span class="line">20 0 2 2</span><br><span class="line">2 0 2 2</span><br><span class="line">1 1 3 4</span><br></pre></td></tr></table></figure><blockquote><p>程序执行到 main() 函数三行代码的时候,会先执行 calc() 函数的 b 参数<br>程序执行到末尾的时候,按照栈先进后出的方式依次执行defer</p></blockquote><h2 id="Go每日一题-结构体指针"><a href="#Go每日一题-结构体指针" class="headerlink" title="Go每日一题(结构体指针)"></a>Go每日一题(结构体指针)</h2><p>以下代码输出什么:</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x *<span class="keyword">struct</span> {</span><br><span class="line"> s [][<span class="number">32</span>]<span class="type">byte</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println</span>(<span class="built_in">len</span>(x.s[<span class="number">99</span>]))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:运行时 panic;</li><li>B:32;</li><li>C:编译错误;</li><li>D:0</li></ul><h3 id="答案解析-4"><a href="#答案解析-4" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>正确答案:B。</p></blockquote><p>注意<strong>这里不是定义一个结构体类型,而是定义一个结构体类型指针变量</strong>,即 x 是一个指针,指针类型是一个匿名结构体。<br>很显然,x 的值是 nil,因为没有初始化,可以打印证实这一点。</p><blockquote><p>内建函数 len 返回 v 的长度,这取决于具体类型:</p><ul><li>数组:v 中元素的数量</li><li>数组指针:*v 中元素的数量(v 为 nil 时 panic)</li><li>切片、map:v 中元素的数量;若 v 为 nil,len(v) 即为零</li><li>字符串:v 中字节的数量</li><li>通道:通道缓存中队列(未读取)元素的数量;若 v 为 nil,len(v) 即为零</li></ul></blockquote><h2 id="Go每日一题-类型方法定义"><a href="#Go每日一题-类型方法定义" class="headerlink" title="Go每日一题(类型方法定义)"></a>Go每日一题(类型方法定义)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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">func</span> <span class="params">(i <span class="type">int</span>)</span></span> PrintInt () {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"> i.PrintInt()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 1</li><li>B. compilation error</li></ul><h3 id="答案解析-5"><a href="#答案解析-5" class="headerlink" title="答案解析"></a>答案解析</h3><p>基于类型创建的方法必须定义在同一个包内,上面的代码基于 int 类型创建了 PrintInt() 方法,<br>由于 int 类型和方法 PrintInt() 定义在不同的包内,所以编译出错。解决的办法可以定义一种新的类型:</p><figure class="highlight golang"><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">type</span> Myint <span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(i Myint)</span></span> PrintInt () {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i Myint = <span class="number">1</span></span><br><span class="line"> i.PrintInt()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-接口实现"><a href="#Go每日一题-接口实现" class="headerlink" title="Go每日一题(接口实现)"></a>Go每日一题(接口实现)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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="keyword">type</span> People <span class="keyword">interface</span> {</span><br><span class="line"> Speak(<span class="type">string</span>) <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(stu *Student)</span></span> Speak(think <span class="type">string</span>) (talk <span class="type">string</span>) {</span><br><span class="line"> <span class="keyword">if</span> think == <span class="string">"speak"</span> {</span><br><span class="line"> talk = <span class="string">"speak"</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> talk = <span class="string">"hi"</span></span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> peo People = Student{}</span><br><span class="line"> think := <span class="string">"speak"</span></span><br><span class="line"> fmt.Println(peo.Speak(think))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. speak</li><li>B. compilation error</li></ul><h3 id="答案解析-6"><a href="#答案解析-6" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B。<br>值类型 Student 没有实现接口的 Speak() 方法,而是指针类型 *Student 实现改方法。</p></blockquote><h2 id="Go每日一题-const-iota"><a href="#Go每日一题-const-iota" class="headerlink" title="Go每日一题(const iota)"></a>Go每日一题(const iota)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">const</span> (</span><br><span class="line"> a = <span class="literal">iota</span></span><br><span class="line"> b = <span class="literal">iota</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> name = <span class="string">"name"</span></span><br><span class="line"> c = <span class="literal">iota</span></span><br><span class="line"> d = <span class="literal">iota</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(a)</span><br><span class="line"> fmt.Println(b)</span><br><span class="line"> fmt.Println(c)</span><br><span class="line"> fmt.Println(d)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-7"><a href="#答案解析-7" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:0 1 1 2。<br>iota 是 golang 语言的常量计数器,只能在常量的表达式中使用<br>iota 在 const 关键字出现时将被重置为 0,<strong>const 中每新增一行常量声明将使 iota 计数一次</strong>。</p></blockquote><h2 id="Go每日一题-iota-String"><a href="#Go每日一题-iota-String" class="headerlink" title="Go每日一题(iota String())"></a>Go每日一题(iota String())</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> Direction <span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> North Direction = <span class="literal">iota</span></span><br><span class="line"> East</span><br><span class="line"> South</span><br><span class="line"> West</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d Direction)</span></span> String() <span class="type">string</span> {</span><br><span class="line"> <span class="keyword">return</span> [...]<span class="type">string</span>{<span class="string">"North"</span>, <span class="string">"East"</span>, <span class="string">"South"</span>, <span class="string">"West"</span>}[d]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(South)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-8"><a href="#答案解析-8" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>South 根据 iota 的用法推断 South 的值是 2;另外,<br>如果定义了 String()方法,当使用 fmt.Printf()、fmt.Print() 和 fmt.Println()会自动使用 String()方法, 实现字符串打印<br>所以,最终输出 <code>South</code></p></blockquote><h2 id="Go每日一题-map"><a href="#Go每日一题-map" class="headerlink" title="Go每日一题(map)"></a>Go每日一题(map)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> Math <span class="keyword">struct</span> {</span><br><span class="line"> x, y <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]Math{</span><br><span class="line"> <span class="string">"foo"</span>: Math{<span class="number">2</span>, <span class="number">3</span>},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m[<span class="string">"foo"</span>].x = <span class="number">4</span></span><br><span class="line"> fmt.Println(m[<span class="string">"foo"</span>].x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 4</li><li>B. compilation error</li></ul><h3 id="答案解析-9"><a href="#答案解析-9" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B<br>类似于 X=Y 的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,但 go 中的 map 的 value 本身是不可寻地址。</p></blockquote><h2 id="Go每日一题-chan-deadlock"><a href="#Go每日一题-chan-deadlock" class="headerlink" title="Go每日一题(chan deadlock)"></a>Go每日一题(chan deadlock)</h2><p>以下代码的输出结果:</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"sync"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> foo := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> bar := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> foo <- <-bar:</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"default"</span>)</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:default</li><li>B:panic</li></ul><h3 id="答案解析-10"><a href="#答案解析-10" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B<br>deadlock:无数据取,满数据写</p></blockquote><h2 id="Go每日一题-类型比较"><a href="#Go每日一题-类型比较" class="headerlink" title="Go每日一题(类型比较)"></a>Go每日一题(类型比较)</h2><p>下面的代码有什么问题?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println([...]<span class="type">int</span>{<span class="number">1</span>} == [<span class="number">2</span>]<span class="type">int</span>{<span class="number">1</span>})</span><br><span class="line"> fmt.Println([]<span class="type">int</span>{<span class="number">1</span>} == []<span class="type">int</span>{<span class="number">1</span>})</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-11"><a href="#答案解析-11" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:有两处错误</p><ul><li>go 中不同类型是不能比较的,而数组长度是数组类型的一部分,所以 […]int{1} 和 [2]int{1} 是两种不同的类型,不能比较;</li><li>切片是不能比较的;</li></ul></blockquote><h2 id="Go每日一题-变量作用域"><a href="#Go每日一题-变量作用域" class="headerlink" title="Go每日一题(变量作用域)"></a>Go每日一题(变量作用域)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">var</span> p *<span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">foo</span><span class="params">()</span></span> (*<span class="type">int</span>, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">var</span> i <span class="type">int</span> = <span class="number">5</span></span><br><span class="line"> <span class="keyword">return</span> &i, <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">bar</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">//use p</span></span><br><span class="line"> fmt.Println(*p)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> p, err := foo()</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(err)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> bar()</span><br><span class="line"> fmt.Println(*p)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 5 5</li><li>B. runtime error</li></ul><h3 id="答案解析-12"><a href="#答案解析-12" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B。<br>变量作用域。问题出在操作符 :=,对于使用 := 定义的变量,<br>如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。</p></blockquote><p>对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到 bar()时程序,全局变量 p 依然还是 nil,程序随即 Crash。</p><h2 id="Go每日一题-range-循环"><a href="#Go每日一题-range-循环" class="headerlink" title="Go每日一题(range 循环)"></a>Go每日一题(range 循环)</h2><p>下面这段代码能否正常结束?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> v {</span><br><span class="line"> v = <span class="built_in">append</span>(v, i)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-13"><a href="#答案解析-13" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:不会出现死循环,能正常结束。<br>循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。</p></blockquote><h2 id="Go每日一题-可变参数-append"><a href="#Go每日一题-可变参数-append" class="headerlink" title="Go每日一题(可变参数 append)"></a>Go每日一题(可变参数 append)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">change</span><span class="params">(s ...<span class="type">int</span>)</span></span> {</span><br><span class="line"> s = <span class="built_in">append</span>(s,<span class="number">3</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> slice := <span class="built_in">make</span>([]<span class="type">int</span>,<span class="number">5</span>,<span class="number">5</span>)</span><br><span class="line"> slice[<span class="number">0</span>] = <span class="number">1</span></span><br><span class="line"> slice[<span class="number">1</span>] = <span class="number">2</span></span><br><span class="line"> change(slice...)</span><br><span class="line"> fmt.Println(slice)</span><br><span class="line"> change(slice[<span class="number">0</span>:<span class="number">2</span>]...)</span><br><span class="line"> fmt.Println(slice)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-14"><a href="#答案解析-14" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:<br>[1 2 0 0 0]<br>[1 2 3 0 0]</p></blockquote><p>Go 提供的语法糖…,可以将 slice 传进可变函数,不会创建新的切片。<br>第一次调用 change() 时,append() 操作使切片底层数组发生了扩容,原 slice 的底层数组不会改变;<br>第二次调用 change() 函数时,使用了操作符[i,j]获得一个新的切片,假定为 slice1, 它的底层数组和原切片底层数组是重合的,不过 slice1 的长度、容量分别是 2、5,所以在 change() 函数中对 slice1 底层数组的修改会影响到原切片。</p><h2 id="Go每日一题-array"><a href="#Go每日一题-array" class="headerlink" title="Go每日一题(array)"></a>Go每日一题(array)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a = []<span class="type">int</span>{<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">var</span> r [<span class="number">5</span>]<span class="type">int</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i, v := <span class="keyword">range</span> a {</span><br><span class="line"> <span class="keyword">if</span> i == <span class="number">0</span> {</span><br><span class="line"> a[<span class="number">1</span>] = <span class="number">12</span></span><br><span class="line"> a[<span class="number">2</span>] = <span class="number">13</span></span><br><span class="line"> }</span><br><span class="line"> r[i] = v</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"r = "</span>, r)</span><br><span class="line"> fmt.Println(<span class="string">"a = "</span>, a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-15"><a href="#答案解析-15" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>r = [1 12 13 4 5]<br>a = [1 12 13 4 5]</p></blockquote><p>切片在 go 的内部结构有一个指向底层数组的指针,当 range 表达式发生复制时,副本的指针依旧指向原底层数组,<br>所以对切片的修改都会反应到底层数组上,所以通过 v 可以获得修改后的数组元素。</p><h2 id="Go每日一题-range"><a href="#Go每日一题-range" class="headerlink" title="Go每日一题(range)"></a>Go每日一题(range)</h2><p>下面这段代码输出结果正确吗?</p><figure class="highlight golang"><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="keyword">type</span> Foo <span class="keyword">struct</span> {</span><br><span class="line"> bar <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []Foo{</span><br><span class="line"> {<span class="string">"A"</span>},</span><br><span class="line"> {<span class="string">"B"</span>},</span><br><span class="line"> {<span class="string">"C"</span>},</span><br><span class="line"> }</span><br><span class="line"> s2 := <span class="built_in">make</span>([]*Foo, <span class="built_in">len</span>(s1))</span><br><span class="line"> <span class="keyword">for</span> i, value := <span class="keyword">range</span> s1 {</span><br><span class="line"> s2[i] = &value</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(s1[<span class="number">0</span>], s1[<span class="number">1</span>], s1[<span class="number">2</span>])</span><br><span class="line"> fmt.Println(s2[<span class="number">0</span>], s2[<span class="number">1</span>], s2[<span class="number">2</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出:<br>{A} {B} {C}<br>&{A} &{B} &{C}</p><h3 id="答案解析-16"><a href="#答案解析-16" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:s2 的输出结果错误。</p></blockquote><p>s2 的输出是 &{C} &{C} &{C},在前面题目我们提到过,for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。<br>所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2 输出的时候显示出了三个 &{c}。</p><h2 id="Go每日一题-map-1"><a href="#Go每日一题-map-1" class="headerlink" title="Go每日一题(map)"></a>Go每日一题(map)</h2><p>下面代码里的 counter 的输出值?</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>{</span><br><span class="line"> <span class="string">"A"</span>: <span class="number">21</span>,</span><br><span class="line"> <span class="string">"B"</span>: <span class="number">22</span>,</span><br><span class="line"> <span class="string">"C"</span>: <span class="number">23</span>,</span><br><span class="line"> }</span><br><span class="line"> counter := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"> <span class="keyword">if</span> counter == <span class="number">0</span> {</span><br><span class="line"> <span class="built_in">delete</span>(m, <span class="string">"A"</span>)</span><br><span class="line"> }</span><br><span class="line"> counter++</span><br><span class="line"> fmt.Println(k, v)</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"counter is "</span>, counter)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 2</li><li>B. 3</li><li>C. 2 或 3</li></ul><h3 id="答案解析-17"><a href="#答案解析-17" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:C。<br>for range map 是无序的,如果第一次循环到 A,则输出 3;否则输出 2。</p></blockquote><h2 id="Go每日一题-goroutine"><a href="#Go每日一题-goroutine" class="headerlink" title="Go每日一题(goroutine)"></a>Go每日一题(goroutine)</h2><p>关于协程,下面说法正确是()</p><ul><li>A. 协程和线程都可以实现程序的并发执行;</li><li>B. 线程比协程更轻量级;</li><li>C. 协程不存在死锁问题;</li><li>D. 通过 channel 来进行协程间的通信;</li></ul><h3 id="答案解析-18"><a href="#答案解析-18" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:AD。</p></blockquote><h2 id="Go每日一题-循环"><a href="#Go每日一题-循环" class="headerlink" title="Go每日一题(循环)"></a>Go每日一题(循环)</h2><p>关于循环语句,下面说法正确的有()</p><ul><li>A. 循环语句既支持 for 关键字,也支持 while 和 do-while;</li><li>B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;</li><li>C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;</li><li>D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;</li></ul><h3 id="答案解析-19"><a href="#答案解析-19" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:CD。<br>平行赋值: a[i], a[j] = a[j], a[i]</p></blockquote><h2 id="Go每日一题-多重赋值"><a href="#Go每日一题-多重赋值" class="headerlink" title="Go每日一题(多重赋值)"></a>Go每日一题(多重赋值)</h2><p>下面代码输出正确的是?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i := <span class="number">1</span></span><br><span class="line"> s := []<span class="type">string</span>{<span class="string">"A"</span>, <span class="string">"B"</span>, <span class="string">"C"</span>}</span><br><span class="line"> i, s[i<span class="number">-1</span>] = <span class="number">2</span>, <span class="string">"Z"</span></span><br><span class="line"> fmt.Printf(<span class="string">"s: %v \n"</span>, s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. s: [Z,B,C]</li><li>B. s: [A,Z,C]</li></ul><h3 id="答案解析-20"><a href="#答案解析-20" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br>多重赋值。</p></blockquote><p>多重赋值分为两个步骤,有先后顺序: 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式; 赋值;<br>所以本例,会先计算 s[i-1],等号右边是两个表达式是常量,所以赋值运算等同于 i, s[0] = 2, “Z”</p><h2 id="Go每日一题-强制类型转化"><a href="#Go每日一题-强制类型转化" class="headerlink" title="Go每日一题(强制类型转化)"></a>Go每日一题(强制类型转化)</h2><p>关于类型转化,下面选项正确的是?</p><figure class="highlight golang"><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">A.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = i</span><br><span class="line"></span><br><span class="line">B.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = (MyInt)i</span><br><span class="line"></span><br><span class="line">C.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = MyInt(i)</span><br><span class="line"></span><br><span class="line">D.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = i.(MyInt)</span><br></pre></td></tr></table></figure><h3 id="答案解析-21"><a href="#答案解析-21" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:C<br>知识点:强制类型转化。<br>D 是类型断言</p></blockquote><h2 id="Go每日一题-switch"><a href="#Go每日一题-switch" class="headerlink" title="Go每日一题(switch)"></a>Go每日一题(switch)</h2><p>关于 switch 语句,下面说法正确的有?</p><ul><li>A. 条件表达式必须为常量或者整数;</li><li>B. 单个 case 中,可以出现多个结果选项;</li><li>C. 需要用 break 来明确退出一个 case;</li><li>D. 只有在 case 中明确添加 fallthrough 关键字,才会继续执行紧跟的下一个 case;</li></ul><h3 id="答案解析-22"><a href="#答案解析-22" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:BD。</p></blockquote><h2 id="Go每日一题-类型断言-方法集"><a href="#Go每日一题-类型断言-方法集" class="headerlink" title="Go每日一题(类型断言 方法集)"></a>Go每日一题(类型断言 方法集)</h2><p>如果 Add() 函数的调用代码为:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a Integer = <span class="number">1</span></span><br><span class="line"> <span class="keyword">var</span> b Integer = <span class="number">2</span></span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">interface</span>{} = &a</span><br><span class="line"> sum := i.(*Integer).Add(b)</span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>则 Add 函数定义正确的是:</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line">A.</span><br><span class="line"><span class="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a Integer)</span></span> Add(b Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">B.</span><br><span class="line"><span class="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a Integer)</span></span> Add(b *Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> a + *b</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">C.</span><br><span class="line"><span class="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Integer)</span></span> Add(b Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> *a + b</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">D.</span><br><span class="line"><span class="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Integer)</span></span> Add(b *Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> *a + *b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-23"><a href="#答案解析-23" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:AC。<br>知识点:类型断言、方法集。</p></blockquote><p>go 中有些的变量不可以寻址,指的是不能通过&获得其地址。</p><p>所以 func( *A ) 只能接收 *A, func( A ) 可以接收 A 或者 *A ,通过指针一定能得到变量的值 *A -> A</p><h2 id="Go每日一题-range-1"><a href="#Go每日一题-range-1" class="headerlink" title="Go每日一题(range)"></a>Go每日一题(range)</h2><p>下面这段代码输出什么,说明原因。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> slice := []<span class="type">int</span>{<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">int</span>]*<span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> key,val := <span class="keyword">range</span> slice {</span><br><span class="line"> m[key] = &val</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k,<span class="string">"->"</span>,*v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-24"><a href="#答案解析-24" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>0 -> 3<br>1 -> 3<br>2 -> 3<br>3 -> 3</p></blockquote><p>for range 循环的时候会创建每个元素的副本,而不是元素的引用,所以 m[key] = &val 取的都是变量 val 的地址,所以最后 map 中的所有元素的值都是变量 val 的地址,因为最后 val 被赋值为 3,所有输出都是 3</p>]]></content>
<summary type="html">
<blockquote>
<p>每月一次,重在积累</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="每日一题" scheme="http://blog.caoxl.com/tags/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
</entry>
<entry>
<title>Golang 每日一题 202303篇</title>
<link href="http://blog.caoxl.com/2023/04/04/Golang-Daily-Questions-4/"/>
<id>http://blog.caoxl.com/2023/04/04/Golang-Daily-Questions-4/</id>
<published>2023-04-04T10:06:26.000Z</published>
<updated>2023-04-07T03:54:43.466Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>每日一题,从问题出发,透过问题查漏补缺</p></blockquote><span id="more"></span><h2 id="Go每日一题-变量定义"><a href="#Go每日一题-变量定义" class="headerlink" title="Go每日一题 (变量定义)"></a>Go每日一题 (变量定义)</h2><p>定义一个包内全局字符串变量,下面语法正确的是(多选):</p><ul><li>A. var str string</li><li>B. str := “”</li><li>C. str = “”</li><li>D. var str = “”</li></ul><h3 id="答案解析"><a href="#答案解析" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:AD。<br>B 只支持局部变量声明;C 是赋值,str 必须在这之前已经声明。</p></blockquote><h2 id="Go每日一题-struct-encoding-x2F-json-导出"><a href="#Go每日一题-struct-encoding-x2F-json-导出" class="headerlink" title="Go每日一题 (struct encoding/json 导出)"></a>Go每日一题 (struct encoding/json 导出)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"encoding/json"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> t := <span class="keyword">struct</span> {</span><br><span class="line"> time.Time</span><br><span class="line"> N <span class="type">int</span></span><br><span class="line"> }{</span><br><span class="line"> time.Date(<span class="number">2020</span>, <span class="number">12</span>, <span class="number">20</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, time.UTC),</span><br><span class="line"> <span class="number">5</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m, _ := json.Marshal(t)</span><br><span class="line"> fmt.Printf(<span class="string">"%s"</span>, m)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-1"><a href="#答案解析-1" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>正确答案 B</p></blockquote><blockquote><p>如果值实现了 <code>json.Marshaler</code> 接口并且不是 nil 指针,则 Marshal 函数会调用其 MarshalJSON 方法以生成 JSON。如果不存在 MarshalJSON 方法,但该值实现 encoding.TextMarshaler 接口,则 Marshal 函数调用其 MarshalText 方法并将结果编码为 JSON 字符串。</p></blockquote><h2 id="Go每日一题-defer"><a href="#Go每日一题-defer" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">hello</span><span class="params">(i <span class="type">int</span>)</span></span> { </span><br><span class="line"> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> i := <span class="number">5</span></span><br><span class="line"> <span class="keyword">defer</span> hello(i)</span><br><span class="line"> i = i + <span class="number">10</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-2"><a href="#答案解析-2" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:5。<br>hello() 函数的参数在执行 defer 语句的时候会保存一份副本,在实际调用 hello() 函数时用,所以是 5.</p></blockquote><h2 id="Go每日一题-结构体嵌套"><a href="#Go每日一题-结构体嵌套" class="headerlink" title="Go每日一题 (结构体嵌套)"></a>Go每日一题 (结构体嵌套)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> People <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *People)</span></span> ShowA() {</span><br><span class="line"> fmt.Println(<span class="string">"showA"</span>)</span><br><span class="line"> p.ShowB()</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *People)</span></span> ShowB() {</span><br><span class="line"> fmt.Println(<span class="string">"showB"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Teacher <span class="keyword">struct</span> {</span><br><span class="line"> People</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Teacher)</span></span> ShowB() {</span><br><span class="line"> fmt.Println(<span class="string">"teacher showB"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> t := Teacher{}</span><br><span class="line"> t.ShowA()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-3"><a href="#答案解析-3" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>showA<br>showB</p></blockquote><p>Teacher 没有自己 ShowA(),所以调用内部类型 People 的同名方法,<br>需要注意的是第 5 行代码调用的是 People 自己的 ShowB 方法。</p><h2 id="Go每日一题-字符串"><a href="#Go每日一题-字符串" class="headerlink" title="Go每日一题 (字符串)"></a>Go每日一题 (字符串)</h2><p>下列选项正确的是?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> str := <span class="string">"hello"</span></span><br><span class="line"> str[<span class="number">0</span>] = <span class="string">'x'</span></span><br><span class="line"> fmt.Println(str)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. hello</li><li>B. xello</li><li>C. compilation error</li></ul><h3 id="答案解析-4"><a href="#答案解析-4" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>C<br>Go 语言中的字符串是只读的。</p></blockquote><h2 id="Go每日一题-指针"><a href="#Go每日一题-指针" class="headerlink" title="Go每日一题 (指针)"></a>Go每日一题 (指针)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">incr</span><span class="params">(p *<span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> *p++</span><br><span class="line"> <span class="keyword">return</span> *p</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> p := <span class="number">1</span></span><br><span class="line"> incr(&p)</span><br><span class="line"> fmt.Println(p)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 1</li><li>B. 2</li><li>C. 3</li></ul><h3 id="答案解析-5"><a href="#答案解析-5" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>B<br>指针,incr() 函数里的 p 是 *int 类型的指针,指向的是 main() 函数的变量 p 的地址。<br>第 2 行代码是将该地址的值执行一个自增操作,incr() 返回自增后的结果。</p></blockquote><h2 id="Go每日一题-可变函数"><a href="#Go每日一题-可变函数" class="headerlink" title="Go每日一题 (可变函数)"></a>Go每日一题 (可变函数)</h2><p>对 add() 函数调用正确的是?</p><figure class="highlight golang"><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">func</span> <span class="title">add</span><span class="params">(args ...<span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> sum := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> _, arg := <span class="keyword">range</span> args {</span><br><span class="line"> sum += arg</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. add(1, 2)</li><li>B. add(1, 3, 7)</li><li>C. add([]int{1, 2})</li><li>D. add([]int{1, 3, 7}…)</li></ul><h3 id="答案解析-6"><a href="#答案解析-6" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:ABD。<br>可变函数</p></blockquote><h2 id="Go每日一题-channel"><a href="#Go每日一题-channel" class="headerlink" title="Go每日一题 (channel)"></a>Go每日一题 (channel)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch1 := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> <span class="keyword">go</span> fmt.Println(<-ch1)</span><br><span class="line"> ch1 <- <span class="number">5</span></span><br><span class="line"> time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:5</li><li>B:不能编译</li><li>C:运行时死锁</li></ul><h3 id="答案解析-7"><a href="#答案解析-7" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>C<br><-ch1 先执行再 调起; 运行时死锁</p></blockquote><h2 id="Go每日一题-array-slice"><a href="#Go每日一题-array-slice" class="headerlink" title="Go每日一题 (array slice)"></a>Go每日一题 (array slice)</h2><ol><li>以下代码输出什么?</li></ol><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := []<span class="type">int</span>{<span class="number">2</span>: <span class="number">1</span>}</span><br><span class="line"> fmt.Println(a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:编译错误;</li><li>B:[2 1];</li><li>C:[0 0 1];</li><li>D:[0 1]</li></ul><blockquote><p>答案解析: [0 0 1]</p></blockquote><ol start="2"><li>以下代码输出什么?</li></ol><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x = []<span class="type">int</span>{<span class="number">4</span>: <span class="number">44</span>, <span class="number">55</span>, <span class="number">66</span>, <span class="number">1</span>: <span class="number">77</span>, <span class="number">88</span>}</span><br><span class="line"> <span class="built_in">println</span>(<span class="built_in">len</span>(x), x[<span class="number">2</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:5 66;</li><li>B:5 88;</li><li>C:7 88;</li><li>D:以上都不对</li></ul><blockquote><p>答案解析: 7 88</p></blockquote><h3 id="答案解析-8"><a href="#答案解析-8" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>var x = []int{4: 44, 55, 66, 1: 77, 88}</p></blockquote><p>即</p><blockquote><p>var x = []int{4:44, 5:55, 6:66, 1:77, 2:88}</p></blockquote><h2 id="Go每日一题-nil切片-空切片"><a href="#Go每日一题-nil切片-空切片" class="headerlink" title="Go每日一题 (nil切片 空切片)"></a>Go每日一题 (nil切片 空切片)</h2><p>下面代码下划线处可以填入哪个选项?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> s1 []<span class="type">int</span></span><br><span class="line"> <span class="keyword">var</span> s2 = []<span class="type">int</span>{}</span><br><span class="line"> <span class="keyword">if</span> __ == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"yes nil"</span>)</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> fmt.Println(<span class="string">"no nil"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. s1</li><li>B. s2</li><li>C. s1、s2 都可以</li></ul><h3 id="答案解析-9"><a href="#答案解析-9" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br>nil 切片和空切片。nil 切片和 nil 相等,一般用来表示一个不存在的切片;<br>空切片和 nil 不相等,表示一个空的集合。</p></blockquote><blockquote><p>答案及解析:C。<br>题目仅仅是可以填入,并没有说要走第一个分支<br>都可以填, s2 == nil 返回的是 false, s1 == nil 返回的是 true</p></blockquote><h2 id="Go每日一题-强制类型转换"><a href="#Go每日一题-强制类型转换" class="headerlink" title="Go每日一题 (强制类型转换)"></a>Go每日一题 (强制类型转换)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> i := <span class="number">65</span></span><br><span class="line"> fmt.Println(<span class="type">string</span>(i))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. A</li><li>B. 65</li><li>C. compilation error</li></ul><h3 id="答案解析-10"><a href="#答案解析-10" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br>UTF-8 编码中,十进制数字 65 对应的符号是 A。</p></blockquote><h2 id="Go每日一题-接口"><a href="#Go每日一题-接口" class="headerlink" title="Go每日一题 (接口)"></a>Go每日一题 (接口)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> A <span class="keyword">interface</span> {</span><br><span class="line"> ShowA() <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> B <span class="keyword">interface</span> {</span><br><span class="line"> ShowB() <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Work <span class="keyword">struct</span> {</span><br><span class="line"> i <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(w Work)</span></span> ShowA() <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">return</span> w.i + <span class="number">10</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(w Work)</span></span> ShowB() <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">return</span> w.i + <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := Work{<span class="number">3</span>}</span><br><span class="line"> <span class="keyword">var</span> a A = c</span><br><span class="line"> <span class="keyword">var</span> b B = c</span><br><span class="line"> fmt.Println(a.ShowA())</span><br><span class="line"> fmt.Println(b.ShowB())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-11"><a href="#答案解析-11" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:13 23。<br>种类型实现多个接口,结构体 Work 分别实现了接口 A、B,<br>所以接口变量 a、b 调用各自的方法 ShowA() 和 ShowB(),输出 13、23。</p></blockquote><h2 id="Go每日一题-slice"><a href="#Go每日一题-slice" class="headerlink" title="Go每日一题 (slice)"></a>Go每日一题 (slice)</h2><p>切片 a、b、c 的长度和容量分别是多少?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := [<span class="number">3</span>]<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> a := s[:<span class="number">0</span>]</span><br><span class="line"> b := s[:<span class="number">2</span>]</span><br><span class="line"> c := s[<span class="number">1</span>:<span class="number">2</span>:<span class="built_in">cap</span>(s)]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-12"><a href="#答案解析-12" class="headerlink" title="答案解析"></a>答案解析</h3><p>a、b、c 的长度和容量分别是 0 3、2 3、1 2</p><p>数组或切片的截取操作。<br>截取操作有带 2 个或者 3 个参数,形如:[i:j] 和 [i:j:k],<br>假设截取对象的底层数组长度为 l。在操作符 [i:j] 中,如果 i 省略,默认 0,如果 j 省略,默认底层数组的长度,<br>**截取得到的切片长度和容量计算方法是 <code>j-i</code>、<code>l-i</code>**。<br>操作符 [i:j:k],k 主要是用来限制切片的容量,但是不能大于数组的长度 l,截取得到的切片长度和容量计算方法是 j-i、k-i。</p><h2 id="Go每日一题-map"><a href="#Go每日一题-map" class="headerlink" title="Go每日一题 (map)"></a>Go每日一题 (map)</h2><p>下面代码中 A B 两处应该怎么修改才能顺利编译?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span> <span class="comment">//A</span></span><br><span class="line"> m[<span class="string">"a"</span>] = <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> v := m[<span class="string">"b"</span>]; v != <span class="literal">nil</span> { <span class="comment">//B</span></span><br><span class="line"> fmt.Println(v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-13"><a href="#答案解析-13" class="headerlink" title="答案解析"></a>答案解析</h3><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br><span class="line"> <span class="comment">// 或 m := map[string]int{}</span></span><br><span class="line"> m[<span class="string">"a"</span>] = <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> v,ok := m[<span class="string">"b"</span>]; ok {</span><br><span class="line"> fmt.Println(v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-接口的静态类型"><a href="#Go每日一题-接口的静态类型" class="headerlink" title="Go每日一题 (接口的静态类型)"></a>Go每日一题 (接口的静态类型)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> A <span class="keyword">interface</span> {</span><br><span class="line"> ShowA() <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> B <span class="keyword">interface</span> {</span><br><span class="line"> ShowB() <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Work <span class="keyword">struct</span> {</span><br><span class="line"> i <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(w Work)</span></span> ShowA() <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">return</span> w.i + <span class="number">10</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(w Work)</span></span> ShowB() <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">return</span> w.i + <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := Work{<span class="number">3</span>}</span><br><span class="line"> <span class="keyword">var</span> a A = c</span><br><span class="line"> <span class="keyword">var</span> b B = c</span><br><span class="line"> fmt.Println(a.ShowB())</span><br><span class="line"> fmt.Println(b.ShowA())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 23 13</li><li>B. compilation error</li></ul><h3 id="答案解析-14"><a href="#答案解析-14" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B<br>接口的静态类型。a、b 具有相同的动态类型和动态值,分别是结构体 work 和 {3};a 的静态类型是 A,b 的静态类型是 B,<br>接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),编译报错。</p></blockquote><h2 id="Go每日一题-Go101"><a href="#Go每日一题-Go101" class="headerlink" title="Go每日一题 (Go101)"></a>Go每日一题 (Go101)</h2><p>Go101 发布的一条 twitter。以下代码是否能正常运行,结果是?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := <span class="built_in">new</span>(<span class="type">int</span>)</span><br><span class="line"> *v = <span class="number">2</span></span><br><span class="line"> <span class="built_in">println</span>(<span class="number">5</span>/+-*v)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-15"><a href="#答案解析-15" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>-2<br><a href="https://gitee.com/ilmself010cn/studygolang_daily/blob/master/daily/2023/03/16/README.md">https://gitee.com/ilmself010cn/studygolang_daily/blob/master/daily/2023/03/16/README.md</a></p></blockquote><h2 id="Go每日一题-类型-类型别名"><a href="#Go每日一题-类型-类型别名" class="headerlink" title="Go每日一题 (类型 类型别名)"></a>Go每日一题 (类型 类型别名)</h2><p>有下面 3 行代码:</p><figure class="highlight golang"><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="comment">// 32 位机器</span></span><br><span class="line"><span class="keyword">var</span> x <span class="type">int32</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> y <span class="type">int</span> = x</span><br><span class="line"><span class="keyword">var</span> z <span class="type">rune</span> = x</span><br></pre></td></tr></table></figure><p>它们是否能编译通过?为什么?</p><p>如果面试时问这道题,你需要想想面试官想考察你什么。</p><h3 id="答案解析-16"><a href="#答案解析-16" class="headerlink" title="答案解析"></a>答案解析</h3><ul><li>01 数字字面量</li></ul><p>在 Go 语言中,字面量是无类型(untyped)的。无类型是什么意思?无类型意味着可以赋值给类似类型的变量或常量。</p><figure class="highlight golang"><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">var</span> a <span class="type">int64</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> b <span class="type">int</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> c <span class="type">float32</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> d <span class="type">complex64</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> e <span class="type">byte</span> = <span class="number">32.0</span></span><br><span class="line"><span class="keyword">var</span> f <span class="type">rune</span> = <span class="number">32.0</span></span><br></pre></td></tr></table></figure><ul><li>02 不同类型</li></ul><p>int 类型在 32 位机器占 4 字节,64 位机器占 8 字节。所以,在 32 位机器上,int32 和 int 的内存占用和内存布局是完全一样的。<br>但 Go 语言不会做隐式类型转换,int 和 int32 是不同的类型,因此上题中 2)编译不通过。</p><ul><li>03 类型别名</li></ul><p>很显然,rune 是 int32 的别名,因此题目中 3)也能编译通过。</p><figure class="highlight golang"><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">// 32 位机器</span></span><br><span class="line"><span class="keyword">var</span> x <span class="type">int32</span> = <span class="number">32.0</span> </span><br><span class="line"><span class="comment">// 【正确】字面量是无类型(untyped)的,32.0 是无类型的浮点数字面量,因此它可以赋值给任意数字相关类型变量(或常量)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> y <span class="type">int</span> = x</span><br><span class="line"><span class="comment">// 【错误】Go 语言不会做隐式类型转换,int 和 int32 是不同的类型,因此上题中 2)编译不通过。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> z <span class="type">rune</span> = x</span><br><span class="line"><span class="comment">// 【正确】rune 是 int32 的别名,因此题目中 3)也能编译通过</span></span><br></pre></td></tr></table></figure><h2 id="Go每日一题-多赋值"><a href="#Go每日一题-多赋值" class="headerlink" title="Go每日一题 (多赋值)"></a>Go每日一题 (多赋值)</h2><p>下面代码中,x 已声明,y 没有声明,判断每条语句的对错。</p><ul><li>x, _ := f()</li><li>x, _ = f()</li><li>x, y := f()</li><li>x, y = f()</li></ul><h3 id="答案解析-17"><a href="#答案解析-17" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:错、对、对、错。<br>变量的声明。<br>1.错,x 已经声明,不能使用 :=;<br>2.对;<br>3.对,当多值赋值时,:= 左边的变量无论声明与否都可以;<br>4.错,y 没有声明。</p></blockquote><h2 id="Go每日一题-defer-1"><a href="#Go每日一题-defer-1" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">increaseA</span><span class="params">()</span></span> <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="type">int</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> i++</span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">return</span> i</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">increaseB</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> r++</span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">return</span> r</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(increaseA())</span><br><span class="line"> fmt.Println(increaseB())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 1 1</li><li>B. 0 1</li><li>C. 1 0</li><li>D. 0 0</li></ul><h3 id="答案解析-18"><a href="#答案解析-18" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B。<br>increaseA() 的返回参数是匿名,increaseB() 是具名</p></blockquote><h2 id="Go每日一题-defer-返回值"><a href="#Go每日一题-defer-返回值" class="headerlink" title="Go每日一题 (defer 返回值)"></a>Go每日一题 (defer 返回值)</h2><p>f1()、f2()、f3() 函数分别返回什么?</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f1</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> r++</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><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f2</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> t := <span class="number">5</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> t = t + <span class="number">5</span></span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">return</span> t</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">func</span> <span class="title">f3</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">(r <span class="type">int</span>)</span></span> {</span><br><span class="line"> r = r + <span class="number">5</span></span><br><span class="line"> }(r)</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><h3 id="答案解析-19"><a href="#答案解析-19" class="headerlink" title="答案解析"></a>答案解析</h3><p>答案及解析:1 5 1。</p><h2 id="Go每日一题-闭包"><a href="#Go每日一题-闭包" class="headerlink" title="Go每日一题 (闭包)"></a>Go每日一题 (闭包)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">app</span><span class="params">()</span></span> <span class="function"><span class="keyword">func</span><span class="params">(<span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> t := <span class="string">"Hi"</span></span><br><span class="line"> c := <span class="function"><span class="keyword">func</span><span class="params">(b <span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> t = t + <span class="string">" "</span> + b</span><br><span class="line"> <span class="keyword">return</span> t</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> c</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := app()</span><br><span class="line"> b := app()</span><br><span class="line"> a(<span class="string">"go"</span>)</span><br><span class="line"> fmt.Println(b(<span class="string">"All"</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:Hi All;</li><li>B:Hi go All;</li><li>C:Hi;</li><li>D:go All</li></ul><h3 id="答案解析-20"><a href="#答案解析-20" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>A<br>关于(函数)闭包,有几个关键点:</p><ul><li>函数是一等公民;</li><li>闭包所处环境,可以引用环境里的值;</li></ul></blockquote><h2 id="Go每日一题-defer-2"><a href="#Go每日一题-defer-2" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>下面代码段输出什么?</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Person <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> person := &Person{<span class="number">28</span>}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 1. </span></span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(person.age)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2.</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">(p *Person)</span></span> {</span><br><span class="line"> fmt.Println(p.age)</span><br><span class="line"> }(person) </span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(person.age)</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> person.age = <span class="number">29</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-21"><a href="#答案解析-21" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:29 29 28。变量 person 是一个指针变量 。</p></blockquote><ol><li>person.age 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;</li><li>defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29;</li><li>很简单,闭包引用,输出 29;</li></ol><blockquote><p>又由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 29 28。</p></blockquote><figure class="highlight markdown"><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">闭包引用输出 29</span><br><span class="line">地址引用 29</span><br><span class="line">输入固定值 28</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-defer-3"><a href="#Go每日一题-defer-3" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>下面这段代码正确的输出是什么?</p><figure class="highlight golang"><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">func</span> <span class="title">f</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(<span class="string">"D"</span>)</span><br><span class="line"> fmt.Println(<span class="string">"F"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> f()</span><br><span class="line"> fmt.Println(<span class="string">"M"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. F M D</li><li>B. D F M</li><li>C. F D M</li></ul><h3 id="答案解析-22"><a href="#答案解析-22" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>参考答案及解析:C。<br>被调用函数里的 defer 语句在返回之前就会被执行</p></blockquote><h2 id="Go每日一题-slice-1"><a href="#Go每日一题-slice-1" class="headerlink" title="Go每日一题 (slice)"></a>Go每日一题 (slice)</h2><p>下面的两个切片声明中有什么区别?哪个更可取?</p><ul><li>A. var a []int</li><li>B. a := []int{}</li></ul><h3 id="答案解析-23"><a href="#答案解析-23" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:第一种切片声明不会分配内存,优先选择。</p></blockquote><h2 id="Go每日一题-interface-指针"><a href="#Go每日一题-interface-指针" class="headerlink" title="Go每日一题 (interface 指针)"></a>Go每日一题 (interface 指针)</h2><p>A、B、C、D 哪些选项有语法错误?</p><figure class="highlight golang"><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="keyword">type</span> S <span class="keyword">struct</span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">(x <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">g</span><span class="params">(x *<span class="keyword">interface</span>{})</span></span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := S{}</span><br><span class="line"> p := &s</span><br><span class="line"> f(s) <span class="comment">//A</span></span><br><span class="line"> g(s) <span class="comment">//B</span></span><br><span class="line"> f(p) <span class="comment">//C</span></span><br><span class="line"> g(p) <span class="comment">//D</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-24"><a href="#答案解析-24" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:BD<br>函数参数为 interface{} 时可以接收任何类型的参数,包括用户自定义类型等,<br>即使是接收指针类型也用 interface{},而不是使用 *interface{}。</p></blockquote><h2 id="Go每日一题-struct-指针"><a href="#Go每日一题-struct-指针" class="headerlink" title="Go每日一题 (struct 指针)"></a>Go每日一题 (struct 指针)</h2><p>下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果?</p><figure class="highlight golang"><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="keyword">type</span> S <span class="keyword">struct</span> {</span><br><span class="line"> m <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">()</span></span> *S {</span><br><span class="line"> <span class="keyword">return</span> __ <span class="comment">//A</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> p := __ <span class="comment">//B</span></span><br><span class="line"> fmt.Println(p.m) <span class="comment">//print "foo"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-25"><a href="#答案解析-25" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>A. &S{“foo”}<br>B. *f() 或者 f()<br>B 处,如果填 *f(),则 p 是 S 类型;如果填 f(),则 p 是 *S 类型;不过都可以使用 p.m 取得结构体的成员。</p></blockquote><h2 id="Go每日一题-常量表达式"><a href="#Go每日一题-常量表达式" class="headerlink" title="Go每日一题 (常量表达式)"></a>Go每日一题 (常量表达式)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> ans <span class="type">float64</span> = <span class="number">15</span> + <span class="number">25</span> + <span class="number">5.2</span></span><br><span class="line"> fmt.Println(ans)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:不能编译;</li><li>B:45;</li><li>C:45.2;</li><li>D:45.0</li></ul><h3 id="答案解析-26"><a href="#答案解析-26" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>正确答案:C。<br>常量表达式是指仅包含常量操作数,且是在编译的时候进行计算的。</p></blockquote><h2 id="Go每日一题-string"><a href="#Go每日一题-string" class="headerlink" title="Go每日一题 (string)"></a>Go每日一题 (string)</h2><p>下面的代码有几处语法问题,各是什么?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x <span class="type">string</span> = <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">if</span> x == <span class="literal">nil</span> {</span><br><span class="line"> x = <span class="string">"default"</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-27"><a href="#答案解析-27" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>golang 的字符串类型是不能赋值 nil 的,也不能跟 nil 比较。</p></blockquote><h2 id="Go每日一题-defer-4"><a href="#Go每日一题-defer-4" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>return 之后的 defer 语句会执行吗,下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">var</span> a <span class="type">bool</span> = <span class="literal">true</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> fmt.Println(<span class="string">"1"</span>)</span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">if</span> a == <span class="literal">true</span> {</span><br><span class="line"> fmt.Println(<span class="string">"2"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> fmt.Println(<span class="string">"3"</span>)</span><br><span class="line"> }()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-28"><a href="#答案解析-28" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:2 1<br>defer 关键字后面的函数或者方法想要执行必须先注册,return 之后的 defer 是不能注册的, 也就不能执行后面的函数或方法。</p></blockquote>]]></content>
<summary type="html">
<blockquote>
<p>每日一题,从问题出发,透过问题查漏补缺</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="每日一题" scheme="http://blog.caoxl.com/tags/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
</entry>
<entry>
<title>Golang 常用标准库</title>
<link href="http://blog.caoxl.com/2023/03/13/Golang-Common-Use-Package-1/"/>
<id>http://blog.caoxl.com/2023/03/13/Golang-Common-Use-Package-1/</id>
<published>2023-03-13T09:14:37.000Z</published>
<updated>2023-03-13T08:58:31.603Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>以下内容为ChatGPT得出的Golang常用30个包</p></blockquote><span id="more"></span><h2 id="1-fmt"><a href="#1-fmt" class="headerlink" title="1. fmt"></a>1. fmt</h2><blockquote><p>提供了格式化输入输出的函数,是Golang中最常用的包之一。</p></blockquote><ul><li><p><a href="https://studygolang.com/pkgdoc">package fmt</a></p></li><li><p>通用:</p></li></ul><figure class="highlight golang"><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">%v值的默认格式表示</span><br><span class="line">%+v类似%v,但输出结构体时会添加字段名</span><br><span class="line">%#v值的Go语法表示</span><br><span class="line">%T值的类型的Go语法表示</span><br><span class="line">%%百分号 </span><br></pre></td></tr></table></figure><h2 id="2-net-x2F-http"><a href="#2-net-x2F-http" class="headerlink" title="2. net/http"></a>2. net/http</h2><blockquote><p>提供了HTTP客户端和服务器的实现,是Golang中网络编程的核心包。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package http</a></li></ul><figure class="highlight golang"><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">resp, err := http.Get(<span class="string">"http://example.com/"</span>)</span><br><span class="line">...</span><br><span class="line">resp, err := http.Post(<span class="string">"http://example.com/upload"</span>, <span class="string">"image/jpeg"</span>, &buf)</span><br><span class="line">...</span><br><span class="line">resp, err := http.PostForm(<span class="string">"http://example.com/form"</span>,</span><br><span class="line"> url.Values{<span class="string">"key"</span>: {<span class="string">"Value"</span>}, <span class="string">"id"</span>: {<span class="string">"123"</span>}})</span><br></pre></td></tr></table></figure><h2 id="3-os"><a href="#3-os" class="headerlink" title="3. os"></a>3. os</h2><blockquote><p>提供了与操作系统交互的函数,如文件操作、环境变量等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package os</a></li></ul><figure class="highlight golang"><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">file, err := os.Open(<span class="string">"file.go"</span>) <span class="comment">// For read access.</span></span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> log.Fatal(err)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="4-encoding-x2F-json"><a href="#4-encoding-x2F-json" class="headerlink" title="4. encoding/json"></a>4. encoding/json</h2><blockquote><p>提供了JSON编码和解码的函数,是Golang中处理JSON数据的标准包。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package json</a></li><li><a href="https://go.dev/blog/json">JSON and Go</a></li></ul><h2 id="5-sync"><a href="#5-sync" class="headerlink" title="5. sync"></a>5. sync</h2><blockquote><p>提供了并发编程中常用的同步原语,如锁、条件变量等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package sync</a></li></ul><h2 id="6-time"><a href="#6-time" class="headerlink" title="6. time"></a>6. time</h2><blockquote><p>提供了时间相关的函数和类型,如时间戳、时区等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package time</a></li></ul><h2 id="7-strconv"><a href="#7-strconv" class="headerlink" title="7. strconv"></a>7. strconv</h2><blockquote><p>提供了字符串和基本数据类型之间转换的函数,如字符串转整数、浮点数等。</p></blockquote><h2 id="8-database-x2F-sql"><a href="#8-database-x2F-sql" class="headerlink" title="8. database/sql"></a>8. database/sql</h2><blockquote><p>提供了数据库访问接口和驱动程序,是Golang中处理数据库操作的标准包之一。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package sql</a></li><li><a href="https://github.com/golang/go/wiki/SQLDrivers">SQL database drivers</a></li></ul><h2 id="9-io"><a href="#9-io" class="headerlink" title="9. io"></a>9. io</h2><blockquote><p>提供了I/O操作相关的接口和类型,如读写、缓冲等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package io</a></li></ul><h2 id="10-flag"><a href="#10-flag" class="headerlink" title="10. flag"></a>10. flag</h2><blockquote><p>提供了命令行参数解析的函数,是Golang中处理命令行参数的标准包。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package flag</a></li></ul><p>在所有flag都注册之后,调用:</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag.Parse()</span><br></pre></td></tr></table></figure><h2 id="11-log"><a href="#11-log" class="headerlink" title="11. log"></a>11. log</h2><blockquote><p>提供了日志记录相关的函数和类型,如输出到文件、格式化等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package log</a></li></ul><h2 id="12-testing"><a href="#12-testing" class="headerlink" title="12. testing"></a>12. testing</h2><blockquote><p>提供了单元测试相关的函数和类型,是Golang中进行单元测试的标准包之一。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package testing</a></li></ul><p>如果有需要,可以调用 *T 和 *B 的 Skip 方法,跳过该测试或基准测试:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">TestTimeConsuming</span><span class="params">(t *testing.T)</span></span> {</span><br><span class="line"> <span class="keyword">if</span> testing.Short() {</span><br><span class="line"> t.Skip(<span class="string">"skipping test in short mode."</span>)</span><br><span class="line"> }</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="13-math"><a href="#13-math" class="headerlink" title="13. math"></a>13. math</h2><blockquote><p>提供了数学计算相关的函数和常量,如三角函数、指数函数等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package math</a></li></ul><h2 id="14-reflect"><a href="#14-reflect" class="headerlink" title="14. reflect"></a>14. reflect</h2><blockquote><p>提供了反射相关的函数和类型,可以在运行时动态获取变量信息和调用方法。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package reflect</a></li></ul><h2 id="15-bufio"><a href="#15-bufio" class="headerlink" title="15. bufio"></a>15. bufio</h2><blockquote><p>提供了带缓冲区的I/O操作相关的接口和类型,可以提高读写效率。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package bufio</a></li></ul><h2 id="16-context"><a href="#16-context" class="headerlink" title="16. context"></a>16. context</h2><blockquote><p>提供了上下文相关的函数和类型,可以在多个goroutine之间传递请求作用域、取消信号等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package context</a></li></ul><h2 id="17-sort"><a href="#17-sort" class="headerlink" title="17. sort"></a>17. sort</h2><blockquote><p>提供了排序相关的函数和类型,如快速排序、堆排序等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package sort</a></li></ul><h2 id="18-crypto"><a href="#18-crypto" class="headerlink" title="18. crypto"></a>18. crypto</h2><blockquote><p>提供了加密和解密相关的函数和类型,如哈希函数、对称加密算法等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package crypto</a></li></ul><h2 id="19-image"><a href="#19-image" class="headerlink" title="19. image"></a>19. image</h2><blockquote><p>提供了图像处理相关的函数和类型,如图片解码、缩放等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package image</a></li></ul><h2 id="20-path-x2F-filepath"><a href="#20-path-x2F-filepath" class="headerlink" title="20. path/filepath"></a>20. path/filepath</h2><blockquote><p>提供了文件路径操作相关的函数和类型,如获取绝对路径、拼接路径等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package filepath</a></li></ul><h2 id="21-encoding-x2F-base64"><a href="#21-encoding-x2F-base64" class="headerlink" title="21. encoding/base64"></a>21. encoding/base64</h2><blockquote><p>提供了Base64编码和解码相关的函数和类型。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package base64</a></li></ul><h2 id="22-html-x2F-template"><a href="#22-html-x2F-template" class="headerlink" title="22. html/template"></a>22. html/template</h2><blockquote><p>提供了HTML模板渲染相关的函数和类型,可以方便地生成HTML页面。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package template</a></li></ul><h2 id="23-regexp"><a href="#23-regexp" class="headerlink" title="23. regexp"></a>23. regexp</h2><blockquote><p>提供了正则表达式相关的函数和类型,可以进行字符串匹配、替换等操作。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package regexp</a></li></ul><h2 id="26-sync-x2F-atomic"><a href="#26-sync-x2F-atomic" class="headerlink" title="26. sync/atomic"></a>26. sync/atomic</h2><blockquote><p>提供了原子操作相关的函数和类型,可以在并发编程中保证数据同步性。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package atomic</a></li></ul><h2 id="27-runtime"><a href="#27-runtime" class="headerlink" title="27. runtime"></a>27. runtime</h2><blockquote><p>提供了与Go运行时系统交互的函数和类型,如goroutine管理、垃圾回收等。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package runtime</a></li></ul><h2 id="30-net-x2F-url"><a href="#30-net-x2F-url" class="headerlink" title="30. net/url"></a>30. net/url</h2><blockquote><p>提供了URL解析相关的函数和类型,可以方便地处理URL地址。</p></blockquote><ul><li><a href="https://studygolang.com/pkgdoc">package url</a></li></ul><blockquote><p>以上内容,由ChatGPT生成的30个标准库,整理后发现,有4个是重复的.AI的路还有很远.</p></blockquote>]]></content>
<summary type="html">
<blockquote>
<p>以下内容为ChatGPT得出的Golang常用30个包</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="Golang" scheme="http://blog.caoxl.com/tags/Golang/"/>
</entry>
<entry>
<title>Golang 大厂面试题 1</title>
<link href="http://blog.caoxl.com/2023/03/13/Golang-Company-Interview-1/"/>
<id>http://blog.caoxl.com/2023/03/13/Golang-Company-Interview-1/</id>
<published>2023-03-13T08:55:01.000Z</published>
<updated>2023-03-13T07:46:19.875Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>以下内容是根据ChatGPT得到的一些大厂面试题</p></blockquote><span id="more"></span><h1 id="Golang-字节跳动"><a href="#Golang-字节跳动" class="headerlink" title="Golang 字节跳动"></a>Golang 字节跳动</h1><h2 id="1-请用Golang实现一个函数,判断一个字符串是否为回文字符串。"><a href="#1-请用Golang实现一个函数,判断一个字符串是否为回文字符串。" class="headerlink" title="1. 请用Golang实现一个函数,判断一个字符串是否为回文字符串。"></a>1. 请用Golang实现一个函数,判断一个字符串是否为回文字符串。</h2><figure class="highlight golang"><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">func</span> <span class="title">isPalindrome</span><span class="params">(s <span class="type">string</span>)</span></span> <span class="type">bool</span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">len</span>(s)/<span class="number">2</span>; i++ {</span><br><span class="line"> <span class="keyword">if</span> s[i] != s[<span class="built_in">len</span>(s)-i<span class="number">-1</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><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="2-请用Golang实现一个函数,计算两个整数的最大公约数。"><a href="#2-请用Golang实现一个函数,计算两个整数的最大公约数。" class="headerlink" title="2. 请用Golang实现一个函数,计算两个整数的最大公约数。"></a>2. 请用Golang实现一个函数,计算两个整数的最大公约数。</h2><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">gcd</span><span class="params">(a, b <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">if</span> b == <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span> a</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> gcd(b, a%b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="3-请用Golang实现一个函数,将一个字符串中的空格替换成”-20”。"><a href="#3-请用Golang实现一个函数,将一个字符串中的空格替换成”-20”。" class="headerlink" title="3. 请用Golang实现一个函数,将一个字符串中的空格替换成”%20”。"></a>3. 请用Golang实现一个函数,将一个字符串中的空格替换成”%20”。</h2><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">replaceSpaces</span><span class="params">(s <span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> <span class="keyword">var</span> res strings.Builder</span><br><span class="line"> <span class="keyword">for</span> _, c := <span class="keyword">range</span> s {</span><br><span class="line"> <span class="keyword">if</span> c == <span class="string">' '</span> {</span><br><span class="line"> res.WriteString(<span class="string">"%20"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> res.WriteRune(c)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res.String()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="4-请用Golang实现一个函数,找出数组中出现次数超过一半的元素。"><a href="#4-请用Golang实现一个函数,找出数组中出现次数超过一半的元素。" class="headerlink" title="4. 请用Golang实现一个函数,找出数组中出现次数超过一半的元素。"></a>4. 请用Golang实现一个函数,找出数组中出现次数超过一半的元素。</h2><figure class="highlight golang"><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">func</span> <span class="title">majorityElement</span><span class="params">(nums []<span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">var</span> count, candidate <span class="type">int</span></span><br><span class="line"> <span class="keyword">for</span> _, num := <span class="keyword">range</span> nums {</span><br><span class="line"> <span class="keyword">if</span> count == <span class="number">0</span> {</span><br><span class="line"> candidate = num</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> num == candidate {</span><br><span class="line"> count++</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> count--</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> candidate</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="5-请用Golang实现一个函数,将一个二叉树展开为链表。"><a href="#5-请用Golang实现一个函数,将一个二叉树展开为链表。" class="headerlink" title="5. 请用Golang实现一个函数,将一个二叉树展开为链表。"></a>5. 请用Golang实现一个函数,将一个二叉树展开为链表。</h2><figure class="highlight golang"><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="keyword">type</span> TreeNode <span class="keyword">struct</span> {</span><br><span class="line"> Val <span class="type">int</span></span><br><span class="line"> Left *TreeNode</span><br><span class="line"> Right *TreeNode</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">flatten</span><span class="params">(root *TreeNode)</span></span> {</span><br><span class="line"> <span class="keyword">if</span> root == <span class="literal">nil</span> || (root.Left == <span class="literal">nil</span> && root.Right == <span class="literal">nil</span>) { <span class="comment">// 如果节点为空或者是叶子节点,则直接返回。</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"> flatten(root.Left) <span class="comment">// 将左子树展开为链表。</span></span><br><span class="line"> flatten(root.Right) <span class="comment">// 将右子树展开为链表。</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> root.Left != <span class="literal">nil</span> { <span class="comment">// 如果左子树不为空,则将左子树插入到右子树之前。</span></span><br><span class="line"> tmp := root.Right</span><br><span class="line"> root.Right = root.Left</span><br><span class="line"> root.Left = <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">for</span> root.Right != <span class="literal">nil</span> { <span class="comment">// 找到右子树的最后一个节点。</span></span><br><span class="line"> root = root.Right</span><br><span class="line"> }</span><br><span class="line"> root.Right = tmp <span class="comment">// 将原来的右子树接到新的右子树之后。</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="6-请编写一个函数,将一个字符串中的每个单词的首字母转换为大写字母。"><a href="#6-请编写一个函数,将一个字符串中的每个单词的首字母转换为大写字母。" class="headerlink" title="6. 请编写一个函数,将一个字符串中的每个单词的首字母转换为大写字母。"></a>6. 请编写一个函数,将一个字符串中的每个单词的首字母转换为大写字母。</h2><figure class="highlight golang"><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">func</span> <span class="title">capitalizeWords</span><span class="params">(s <span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> words := strings.Fields(s)</span><br><span class="line"> <span class="keyword">for</span> i, word := <span class="keyword">range</span> words {</span><br><span class="line"> words[i] = strings.ToUpper(word[:<span class="number">1</span>]) + word[<span class="number">1</span>:]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> strings.Join(words, <span class="string">" "</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="7-请编写一个函数,将一个整数数组中的元素按照从小到大的顺序排序。"><a href="#7-请编写一个函数,将一个整数数组中的元素按照从小到大的顺序排序。" class="headerlink" title="7. 请编写一个函数,将一个整数数组中的元素按照从小到大的顺序排序。"></a>7. 请编写一个函数,将一个整数数组中的元素按照从小到大的顺序排序。</h2><figure class="highlight golang"><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">func</span> <span class="title">sortInts</span><span class="params">(a []<span class="type">int</span>)</span></span> []<span class="type">int</span> {</span><br><span class="line"> sort.Ints(a)</span><br><span class="line"> <span class="keyword">return</span> a</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="8-请编写一个函数,统计一个字符串中每个字符出现的次数,并返回一个-map,其中键为字符,值为出现次数。"><a href="#8-请编写一个函数,统计一个字符串中每个字符出现的次数,并返回一个-map,其中键为字符,值为出现次数。" class="headerlink" title="8. 请编写一个函数,统计一个字符串中每个字符出现的次数,并返回一个 map,其中键为字符,值为出现次数。"></a>8. 请编写一个函数,统计一个字符串中每个字符出现的次数,并返回一个 map,其中键为字符,值为出现次数。</h2><figure class="highlight golang"><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">func</span> <span class="title">countChars</span><span class="params">(s <span class="type">string</span>)</span></span> <span class="keyword">map</span>[<span class="type">rune</span>]<span class="type">int</span> {</span><br><span class="line"> counts := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">rune</span>]<span class="type">int</span>)</span><br><span class="line"> <span class="keyword">for</span> _, r := <span class="keyword">range</span> s {</span><br><span class="line"> counts[r]++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> counts</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="9-请编写一个函数,将一个字符串中的所有大写字母转换为小写字母。"><a href="#9-请编写一个函数,将一个字符串中的所有大写字母转换为小写字母。" class="headerlink" title="9. 请编写一个函数,将一个字符串中的所有大写字母转换为小写字母。"></a>9. 请编写一个函数,将一个字符串中的所有大写字母转换为小写字母。</h2><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">toLowerCase</span><span class="params">(s <span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> <span class="keyword">var</span> result strings.Builder</span><br><span class="line"> <span class="keyword">for</span> , r := <span class="keyword">range</span> s {</span><br><span class="line"> <span class="keyword">if</span> r >= <span class="string">'A'</span> && r <= <span class="string">'Z'</span> { <span class="comment">// 如果是大写字母,则转换为小写字母。</span></span><br><span class="line"> result.WriteRune(r + (<span class="string">'a'</span> - <span class="string">'A'</span>))</span><br><span class="line"> } <span class="keyword">else</span> { </span><br><span class="line"> result.WriteRune(r)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result.String()</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>以下内容是根据ChatGPT得到的一些大厂面试题</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试题" scheme="http://blog.caoxl.com/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 高频面试题</title>
<link href="http://blog.caoxl.com/2023/03/08/Golang-Interview-6/"/>
<id>http://blog.caoxl.com/2023/03/08/Golang-Interview-6/</id>
<published>2023-03-08T08:45:55.000Z</published>
<updated>2023-03-13T07:45:45.555Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>以下内容是根据ChatGPT得到的一些面试题</p></blockquote><span id="more"></span><h1 id="Golang-高频面试题"><a href="#Golang-高频面试题" class="headerlink" title="Golang 高频面试题"></a>Golang 高频面试题</h1><h2 id="1-什么是-Goroutine?如何创建-Goroutine?"><a href="#1-什么是-Goroutine?如何创建-Goroutine?" class="headerlink" title="1. 什么是 Goroutine?如何创建 Goroutine?"></a>1. 什么是 Goroutine?如何创建 Goroutine?</h2><p>goroutine 是 Go 语言 中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。</p><ul><li>普通函数创建goroutine</li></ul><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">go</span> f(params)</span><br></pre></td></tr></table></figure><ul><li>匿名函数创建goroutine</li></ul><figure class="highlight golang"><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">go</span> <span class="function"><span class="keyword">func</span><span class="params">(params)</span></span> {</span><br><span class="line"><span class="comment">// do something</span></span><br><span class="line">}(params)</span><br></pre></td></tr></table></figure><h2 id="2-什么是-Channel?如何使用-Channel-进行通信?"><a href="#2-什么是-Channel?如何使用-Channel-进行通信?" class="headerlink" title="2. 什么是 Channel?如何使用 Channel 进行通信?"></a>2. 什么是 Channel?如何使用 Channel 进行通信?</h2><blockquote><p>channel 就是 goroutine 之间通过通信来共享内存的手段。</p></blockquote><p>Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。</p><p>通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。</p><p>阻塞场景梳理</p><table><thead><tr><th align="left">操作</th><th align="left">为空</th><th align="left">非空已关闭</th><th align="left">非空未关闭</th></tr></thead><tbody><tr><td align="left">close</td><td align="left"><code>panic</code></td><td align="left"><code>panic</code></td><td align="left">成功close</td></tr><tr><td align="left">写入</td><td align="left">永久阻塞</td><td align="left"><code>panic</code></td><td align="left">成功写入或阻塞</td></tr><tr><td align="left">读取</td><td align="left">永久阻塞</td><td align="left">永不阻塞</td><td align="left">成功读取或阻塞</td></tr></tbody></table><h2 id="3-什么是-defer-关键字?它有什么作用?"><a href="#3-什么是-defer-关键字?它有什么作用?" class="headerlink" title="3. 什么是 defer 关键字?它有什么作用?"></a>3. 什么是 defer 关键字?它有什么作用?</h2><p>golang中的<code>defer</code>关键字用来声明一个延迟函数,该函数会放在一个列表中,在<code>defer</code>语句的外层函数返回之前系统会执行该延迟函数。 </p><p><code>defer</code>特点有: 函数返回之前执行 可以放在函数中任意位置</p><h2 id="4-如何实现并发安全的-Map?"><a href="#4-如何实现并发安全的-Map?" class="headerlink" title="4. 如何实现并发安全的 Map?"></a>4. 如何实现并发安全的 Map?</h2><ul><li>加读写锁: <code>sync.RWMutex</code></li><li>分片加锁</li><li><code>sync.map</code></li></ul><h2 id="5-如何实现一个-HTTP-Server?"><a href="#5-如何实现一个-HTTP-Server?" class="headerlink" title="5. 如何实现一个 HTTP Server?"></a>5. 如何实现一个 HTTP Server?</h2><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">indexHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> {</span><br><span class="line"> fmt.Fprintf(w, <span class="string">"Hello World"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> http.HandleFunc(<span class="string">"/"</span>, indexHandler)</span><br><span class="line"> http.ListenAndServe(<span class="string">":8080"</span>, <span class="literal">nil</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="6-如何进行内存管理和垃圾回收?"><a href="#6-如何进行内存管理和垃圾回收?" class="headerlink" title="6. 如何进行内存管理和垃圾回收?"></a>6. 如何进行内存管理和垃圾回收?</h2><blockquote><p>GC垃圾回收机制</p></blockquote><ul><li><p>Go1.3 使用的是标记清除法</p></li><li><p>Go1.5 三色标记法</p></li><li><p>Go1.8 三色标记 + 混合写屏障</p></li><li><p>这个 GC 什么时候会被触发呢?</p><blockquote><p>一是堆内存的分配达到控制器计算的触发堆大小,初始大小环境变量 <code>GOGC</code>,之后堆内存达到上一次垃圾收集的 2 倍时才会触发 GC<br>二是如果一定时间内没有触发,就会触发新的循环,该触发条件由 <code>runtime.forcegcperiod</code> 变量控制,默认为 2 分钟。</p></blockquote></li></ul><h2 id="7-什么是接口?如何实现接口?"><a href="#7-什么是接口?如何实现接口?" class="headerlink" title="7. 什么是接口?如何实现接口?"></a>7. 什么是接口?如何实现接口?</h2><blockquote><p>在Golang 中,接口是一组方法签名。 当类型为接口中的所有方法提供定义时,它被称为实现接口。</p></blockquote><p>如果一个任意类型 T 的方法集为一个接口类型的方法集的超集,则我们说类型 T 实现了此接口类型。T 可以是一个非接口类型,也可以是一个接口类型。</p><h2 id="8-如何进行单元测试和性能测试?"><a href="#8-如何进行单元测试和性能测试?" class="headerlink" title="8. 如何进行单元测试和性能测试?"></a>8. 如何进行单元测试和性能测试?</h2><ul><li>单元测试</li></ul><figure class="highlight golang"><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">func</span> <span class="title">TestHello</span><span class="params">(t *testing.T)</span></span> {}</span><br></pre></td></tr></table></figure><ul><li>基准测试</li></ul><figure class="highlight golang"><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">func</span> <span class="title">BenchmarkHello</span><span class="params">(b *testing.B)</span></span> {}</span><br></pre></td></tr></table></figure><ul><li>模糊测试</li></ul><figure class="highlight golang"><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">func</span> <span class="title">FuzzkHello</span><span class="params">(f *testing.F)</span></span> {}</span><br></pre></td></tr></table></figure><h2 id="9-如何进行并发编程调试和排错?"><a href="#9-如何进行并发编程调试和排错?" class="headerlink" title="9. 如何进行并发编程调试和排错?"></a>9. 如何进行并发编程调试和排错?</h2><ul><li>使用Go标准库中pprof来对代码运行过程进行分析:</li></ul><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> pprof.StartCPUProfile(os.Stdout)</span><br><span class="line"> <span class="keyword">defer</span> pprof.StopCPUProfile()</span><br><span class="line"></span><br><span class="line"> f, err := os.Create(output)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 使用WriteHeapProfile可以统计内存使用量</span></span><br><span class="line"> <span class="comment">//pprof.WriteHeapProfile(os.Stdout)</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><a href="https://ifun.dev/post/golang-concurrency/">Go 并发与调试</a></li></ul><h2 id="10-如何优化-Golang-程序的性能和资源利用率?"><a href="#10-如何优化-Golang-程序的性能和资源利用率?" class="headerlink" title="10. 如何优化 Golang 程序的性能和资源利用率?"></a>10. 如何优化 Golang 程序的性能和资源利用率?</h2><ul><li>问题分析: 在服务上运行一些性能分析,并检查是什么导致 CPU 的高消耗,看看是否可以做些优化工作。</li><li>升级版本: 将 GO 升级到最新的稳定版本(软件生命周期中的关键一步)</li><li>使用工具分析程序: pprof,trace等定位出问题区域</li></ul><h1 id="Golang-Hertz-面试题"><a href="#Golang-Hertz-面试题" class="headerlink" title="Golang Hertz 面试题"></a>Golang Hertz 面试题</h1><h2 id="1-什么是-Hertz-框架?它有哪些特点和优势?"><a href="#1-什么是-Hertz-框架?它有哪些特点和优势?" class="headerlink" title="1. 什么是 Hertz 框架?它有哪些特点和优势?"></a>1. 什么是 Hertz 框架?它有哪些特点和优势?</h2><p>Hertz 是一个超大规模的企业级微服务 HTTP 框架,具有高易用性、易扩展、低时延等特点。</p><h2 id="2-如何使用-Hertz-框架进行-Web-开发?"><a href="#2-如何使用-Hertz-框架进行-Web-开发?" class="headerlink" title="2. 如何使用 Hertz 框架进行 Web 开发?"></a>2. 如何使用 Hertz 框架进行 Web 开发?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/overview/">https://www.cloudwego.io/zh/docs/hertz/overview/</a></li></ul><h2 id="3-如何进行路由配置和参数解析?"><a href="#3-如何进行路由配置和参数解析?" class="headerlink" title="3. 如何进行路由配置和参数解析?"></a>3. 如何进行路由配置和参数解析?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/route/">Hertz路由注册</a></li></ul><h2 id="4-如何进行中间件配置和使用?"><a href="#4-如何进行中间件配置和使用?" class="headerlink" title="4. 如何进行中间件配置和使用?"></a>4. 如何进行中间件配置和使用?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/middleware/">Hertz中间件概览</a></li></ul><h2 id="5-如何进行模板渲染和静态文件服务?"><a href="#5-如何进行模板渲染和静态文件服务?" class="headerlink" title="5. 如何进行模板渲染和静态文件服务?"></a>5. 如何进行模板渲染和静态文件服务?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/example/#%E6%B8%B2%E6%9F%93">模板渲染</a></li></ul><h2 id="6-如何进行数据库操作和-ORM-映射?"><a href="#6-如何进行数据库操作和-ORM-映射?" class="headerlink" title="6. 如何进行数据库操作和 ORM 映射?"></a>6. 如何进行数据库操作和 ORM 映射?</h2><ul><li><a href="https://gorm.io/zh_CN/docs/">GORM</a></li></ul><h2 id="7-如何进行-Session-和-Cookie-管理?"><a href="#7-如何进行-Session-和-Cookie-管理?" class="headerlink" title="7. 如何进行 Session 和 Cookie 管理?"></a>7. 如何进行 Session 和 Cookie 管理?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/middleware/session/">Session扩展</a></li></ul><h2 id="8-如何进行日志记录和错误处理?"><a href="#8-如何进行日志记录和错误处理?" class="headerlink" title="8. 如何进行日志记录和错误处理?"></a>8. 如何进行日志记录和错误处理?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/log/">日志</a></li></ul><blockquote><p>目前 <code>hlog</code> 支持 <code>zap</code> 和 <code>logrus</code> 的拓展使用; 推荐使用<code>zap</code></p></blockquote><h2 id="9-如何进行性能优化和安全防护?"><a href="#9-如何进行性能优化和安全防护?" class="headerlink" title="9. 如何进行性能优化和安全防护?"></a>9. 如何进行性能优化和安全防护?</h2><ul><li><p>监控, 链路追踪, 服务注册与发现</p></li><li><p><a href="https://www.cloudwego.io/zh/docs/hertz/tutorials/service-governance/tracing/">链路追踪</a></p></li></ul><h2 id="10-Hertz-框架与其他-Web-框架的比较,有哪些优缺点?"><a href="#10-Hertz-框架与其他-Web-框架的比较,有哪些优缺点?" class="headerlink" title="10. Hertz 框架与其他 Web 框架的比较,有哪些优缺点?"></a>10. Hertz 框架与其他 Web 框架的比较,有哪些优缺点?</h2><ul><li>优点: 稳定性, 高易用性, 易扩展, 低延时</li></ul><h1 id="Golang-Kitex-面试题"><a href="#Golang-Kitex-面试题" class="headerlink" title="Golang Kitex 面试题"></a>Golang Kitex 面试题</h1><h2 id="1-什么是-Kitex-框架?它有哪些特点和优势?"><a href="#1-什么是-Kitex-框架?它有哪些特点和优势?" class="headerlink" title="1. 什么是 Kitex 框架?它有哪些特点和优势?"></a>1. 什么是 Kitex 框架?它有哪些特点和优势?</h2><p>Kitex 字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,针对字节内部做了定制扩展。</p><h2 id="2-如何使用-Kitex-框架进行微服务开发?"><a href="#2-如何使用-Kitex-框架进行微服务开发?" class="headerlink" title="2. 如何使用 Kitex 框架进行微服务开发?"></a>2. 如何使用 Kitex 框架进行微服务开发?</h2><blockquote><p>基于服务注册和发现</p></blockquote><h2 id="3-如何进行服务注册和发现?"><a href="#3-如何进行服务注册和发现?" class="headerlink" title="3. 如何进行服务注册和发现?"></a>3. 如何进行服务注册和发现?</h2><ol><li>基于 Etcd 实现服务注册与发现</li><li>基于 Nacos 实现服务注册与发现</li></ol><h2 id="4-如何进行负载均衡和容错处理?"><a href="#4-如何进行负载均衡和容错处理?" class="headerlink" title="4. 如何进行负载均衡和容错处理?"></a>4. 如何进行负载均衡和容错处理?</h2><p>kitex 的负载均衡扩展方式和 gRPC 基本一样, 是通过实现 <code>Loadbalancer</code> 接口. 它的职责也是在每个 rpc 调用之前选择一个连接返回. 首先查看接口定义:</p><figure class="highlight golang"><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="comment">// Loadbalancer generates pickers for the given service discovery result.</span></span><br><span class="line"><span class="keyword">type</span> Loadbalancer <span class="keyword">interface</span> {</span><br><span class="line"> GetPicker(discovery.Result) Picker</span><br><span class="line"> Name() <span class="type">string</span> <span class="comment">// unique key</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Picker picks an instance for next RPC call.</span></span><br><span class="line"><span class="keyword">type</span> Picker <span class="keyword">interface</span> {</span><br><span class="line"> Next(ctx context.Context, request <span class="keyword">interface</span>{}) discovery.Instance</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><a href="https://blog.cong.moe/post/2022-06-14-kitex-resolver-balancer/">Kitex 服务发现与负载均衡</a></li></ul><h2 id="5-如何进行协议编解码和数据传输?"><a href="#5-如何进行协议编解码和数据传输?" class="headerlink" title="5. 如何进行协议编解码和数据传输?"></a>5. 如何进行协议编解码和数据传输?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/kitex/tutorials/framework-exten/codec/">编解码 (协议) 扩展</a></li><li><a href="https://www.cloudwego.io/zh/docs/kitex/tutorials/basic-feature/transport_protocol/">传输协议</a></li></ul><h2 id="6-如何进行中间件配置和使用?"><a href="#6-如何进行中间件配置和使用?" class="headerlink" title="6. 如何进行中间件配置和使用?"></a>6. 如何进行中间件配置和使用?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/kitex/tutorials/framework-exten/middleware/">Middleware 扩展</a></li></ul><h2 id="7-如何进行服务治理和监控管理?"><a href="#7-如何进行服务治理和监控管理?" class="headerlink" title="7. 如何进行服务治理和监控管理?"></a>7. 如何进行服务治理和监控管理?</h2><ul><li><a href="https://www.cloudwego.io/zh/docs/kitex/tutorials/service-governance/discovery/">服务发现</a></li><li><a href="https://www.cloudwego.io/zh/docs/kitex/tutorials/service-governance/monitoring/">监控</a></li></ul><h2 id="8-如何进行性能优化和安全防护?"><a href="#8-如何进行性能优化和安全防护?" class="headerlink" title="8. 如何进行性能优化和安全防护?"></a>8. 如何进行性能优化和安全防护?</h2><ul><li>性能优化:</li></ul><ol><li>减少内存操作</li></ol><ul><li>buffer 管理</li><li>string / binary 零拷贝</li></ul><ol start="2"><li>预计算</li><li>使用 SIMD 优化 Thrift 编码</li><li>减少函数调用</li></ol><ul><li><a href="https://www.cloudwego.io/zh/blog/2021/09/23/%E5%AD%97%E8%8A%82%E8%B7%B3%E5%8A%A8-go-rpc-%E6%A1%86%E6%9E%B6-kitex-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5/">字节跳动 Go RPC 框架 Kitex 性能优化实践</a></li></ul><h2 id="9-Kitex-框架与其他微服务框架的比较,有哪些优缺点?"><a href="#9-Kitex-框架与其他微服务框架的比较,有哪些优缺点?" class="headerlink" title="9. Kitex 框架与其他微服务框架的比较,有哪些优缺点?"></a>9. Kitex 框架与其他微服务框架的比较,有哪些优缺点?</h2><ul><li><p><a href="https://www.cloudwego.io/zh/docs/kitex/overview/">概览</a></p></li><li><p>高性能</p></li><li><p>扩展性</p></li><li><p>多消息协议</p></li><li><p>多传输协议</p></li><li><p>多种消息类型</p></li><li><p>服务治理</p></li><li><p>代码生成</p></li></ul><h2 id="10-Kitex-框架在实际项目中的应用场景和案例有哪些?"><a href="#10-Kitex-框架在实际项目中的应用场景和案例有哪些?" class="headerlink" title="10. Kitex 框架在实际项目中的应用场景和案例有哪些?"></a>10. Kitex 框架在实际项目中的应用场景和案例有哪些?</h2><ul><li>支付,订单,通知,账号等可以单独抽离的微服务</li></ul><h1 id="Golang-Kratos-面试题"><a href="#Golang-Kratos-面试题" class="headerlink" title="Golang Kratos 面试题"></a>Golang Kratos 面试题</h1><h2 id="1-什么是Kratos框架?它的主要特点是什么?"><a href="#1-什么是Kratos框架?它的主要特点是什么?" class="headerlink" title="1. 什么是Kratos框架?它的主要特点是什么?"></a>1. 什么是Kratos框架?它的主要特点是什么?</h2><p>Kratos是一款基于Go语言的微服务框架,由Bilibili开源。它的主要特点包括高性能、易扩展、易用性强、支持多种协议等。</p><h2 id="2-Kratos框架中的中间件有哪些?请简述它们的作用。"><a href="#2-Kratos框架中的中间件有哪些?请简述它们的作用。" class="headerlink" title="2. Kratos框架中的中间件有哪些?请简述它们的作用。"></a>2. Kratos框架中的中间件有哪些?请简述它们的作用。</h2><p>Kratos框架中的中间件包括日志中间件、链路追踪中间件、限流中间件等。</p><ul><li>日志中间件用于记录请求和响应信息,</li><li>链路追踪中间件用于跟踪请求在系统内部的传递情况,</li><li>限流中间件则可以控制请求流量,防止系统过载。</li></ul><h2 id="3-Kratos框架支持哪些协议?"><a href="#3-Kratos框架支持哪些协议?" class="headerlink" title="3. Kratos框架支持哪些协议?"></a>3. Kratos框架支持哪些协议?</h2><p>Kratos框架支持HTTP/1.1、HTTP/2和gRPC协议。</p><h2 id="4-Kratos框架如何实现服务注册与发现?"><a href="#4-Kratos框架如何实现服务注册与发现?" class="headerlink" title="4. Kratos框架如何实现服务注册与发现?"></a>4. Kratos框架如何实现服务注册与发现?</h2><p>Kratos框架使用etcd作为服务注册与发现组件。当一个服务启动时,它会将自己注册到etcd上,并定期向etcd发送心跳以保持自己在etcd上的存活状态。其他服务可以通过查询etcd来获取可用服务列表,并根据需要进行调用。</p><h2 id="5-Kratos框架如何实现负载均衡?"><a href="#5-Kratos框架如何实现负载均衡?" class="headerlink" title="5. Kratos框架如何实现负载均衡?"></a>5. Kratos框架如何实现负载均衡?</h2><p>Kratos框架使用go-grpc-middleware库来实现负载均衡。该库提供了多种负载均衡策略,包括轮询、随机等。当一个客户端需要调用一个服务时,它会从可用服务列表中选择一个地址,并将请求发送到该地址上。如果该地址不可用,则会尝试选择另一个地址进行调用。</p><h1 id="Golang-Gin-面试题"><a href="#Golang-Gin-面试题" class="headerlink" title="Golang Gin 面试题"></a>Golang Gin 面试题</h1><h2 id="1-什么是Gin框架?它的主要特点是什么?"><a href="#1-什么是Gin框架?它的主要特点是什么?" class="headerlink" title="1. 什么是Gin框架?它的主要特点是什么?"></a>1. 什么是Gin框架?它的主要特点是什么?</h2><p>Gin是一个基于Go语言的Web框架,由Gin Gonic开源。它的主要特点包括高性能、易扩展、易用性强、支持中间件等。</p><h2 id="2-Gin框架中的中间件有哪些?请简述它们的作用。"><a href="#2-Gin框架中的中间件有哪些?请简述它们的作用。" class="headerlink" title="2. Gin框架中的中间件有哪些?请简述它们的作用。"></a>2. Gin框架中的中间件有哪些?请简述它们的作用。</h2><p>Gin框架中的中间件包括Logger、Recovery、CORS等。Logger用于记录请求和响应信息,Recovery用于恢复程序崩溃时的状态,CORS用于处理跨域请求。</p><h2 id="3-Gin框架支持哪些HTTP方法?"><a href="#3-Gin框架支持哪些HTTP方法?" class="headerlink" title="3. Gin框架支持哪些HTTP方法?"></a>3. Gin框架支持哪些HTTP方法?</h2><p>Gin框架支持GET、POST、PUT、PATCH、DELETE等HTTP方法。</p><h2 id="4-Gin框架如何实现路由?"><a href="#4-Gin框架如何实现路由?" class="headerlink" title="4. Gin框架如何实现路由?"></a>4. Gin框架如何实现路由?</h2><p>Gin框架使用Router来实现路由。当一个请求到达时,Router会根据请求路径和HTTP方法选择对应的处理函数进行处理。</p><h2 id="5-Gin框架如何实现参数绑定?"><a href="#5-Gin框架如何实现参数绑定?" class="headerlink" title="5. Gin框架如何实现参数绑定?"></a>5. Gin框架如何实现参数绑定?</h2><p>Gin框架使用Context来实现参数绑定。当一个请求到达时,Context会将请求参数解析并绑定到对应的结构体上,然后将结构体传递给处理函数进行处理。</p><h1 id="Golang-Beego-面试题"><a href="#Golang-Beego-面试题" class="headerlink" title="Golang Beego 面试题"></a>Golang Beego 面试题</h1><h2 id="1-什么是-Beego?它有哪些特点?"><a href="#1-什么是-Beego?它有哪些特点?" class="headerlink" title="1. 什么是 Beego?它有哪些特点?"></a>1. 什么是 Beego?它有哪些特点?</h2><p>Beego 是一个基于 Go 语言的 Web 开发框架,它具有高性能、简单易用、灵活可扩展等特点。Beego 提供了路由、模板、会话管理、日志记录等常用功能,并且支持 ORM 框架(如 GORM)和缓存框架(如 Redis)等。</p><h2 id="2-如何创建一个-Beego-应用程序?"><a href="#2-如何创建一个-Beego-应用程序?" class="headerlink" title="2. 如何创建一个 Beego 应用程序?"></a>2. 如何创建一个 Beego 应用程序?</h2><p>可以使用命令行工具 bee 来创建一个 Beego 应用程序。首先需要安装 bee 工具,然后在命令行中执行以下命令:</p><p>bee new myapp</p><p>其中 myapp 是应用程序的名称。执行完上述命令后,bee 工具会自动创建一个名为 myapp 的应用程序,并生成一些默认的文件和目录结构。</p><h2 id="3-如何定义路由?"><a href="#3-如何定义路由?" class="headerlink" title="3. 如何定义路由?"></a>3. 如何定义路由?</h2><p>可以使用 beego.Router 函数来定义路由。例如:</p><p>beego.Router(“/hello”, &controllers.MainController{})</p><p>其中 “/hello” 是路由路径,&controllers.MainController{} 是处理该路由的控制器。</p><h2 id="4-如何使用模板引擎?"><a href="#4-如何使用模板引擎?" class="headerlink" title="4. 如何使用模板引擎?"></a>4. 如何使用模板引擎?</h2><p>Beego 默认使用的是 Go 的模板引擎(html/template),可以通过 beego.SetViewsPath 函数设置模板文件所在的目录,然后在控制器中使用 this.TplName 和 this.Data 函数来渲染模板。例如:</p><p>func (this *MainController) Get() {<br>this.Data[“name”] = “world”<br>this.TplName = “index.tpl”<br>}</p><p>其中 index.tpl 是模板文件名,name 是传递给模板的数据。</p><h2 id="5-如何进行会话管理?"><a href="#5-如何进行会话管理?" class="headerlink" title="5. 如何进行会话管理?"></a>5. 如何进行会话管理?</h2><p>Beego 提供了 Session 模块来进行会话管理。可以通过 beego.BConfig.WebConfig.Session.SessionOn 设置是否启用会话管理,在控制器中使用 this.SetSession 和 this.GetSession 函数来设置和获取会话数据。例如:</p><p>func (this *MainController) Get() {<br>this.SetSession(“username”, “admin”)<br>username := this.GetSession(“username”)<br>}</p><h2 id="6-如何进行-ORM-操作?"><a href="#6-如何进行-ORM-操作?" class="headerlink" title="6. 如何进行 ORM 操作?"></a>6. 如何进行 ORM 操作?</h2><p>Beego 支持多种 ORM 框架,如 GORM、XORM 等。可以通过 beego.AppConfig.String 函数获取数据库连接字符串,在控制器中使用相应的 ORM 方法进行增删改查操作。例如:</p><p>func (this *MainController) Get() {<br>var users []models.User<br>orm.NewOrm().QueryTable(“user”).All(&users)<br>}</p><p>其中 models.User 是定义在 models 包中的用户数据结构。</p><h2 id="7-如何进行缓存操作?"><a href="#7-如何进行缓存操作?" class="headerlink" title="7. 如何进行缓存操作?"></a>7. 如何进行缓存操作?</h2><p>Beego 支持多种缓存框架,如 Redis、Memcache 等。可以通过 beego.AppConfig.String 函数获取缓存连接字符串,在控制器中使用相应的缓存方法进行读写操作。例如:</p><figure class="highlight golang"><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">func</span> <span class="params">(this *MainController)</span></span> Get() {</span><br><span class="line"> cache := cache.NewCache(<span class="string">"redis"</span>, <span class="string">`{"conn":"127.0.0.1:6379"}`</span>)</span><br><span class="line"> cache.Put(<span class="string">"key"</span>, <span class="string">"value"</span>, <span class="number">3600</span>*time.Second)</span><br><span class="line"> value := cache.Get(<span class="string">"key"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>以下内容是根据ChatGPT得到的一些面试题</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试题" scheme="http://blog.caoxl.com/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 每日一题 202302篇</title>
<link href="http://blog.caoxl.com/2023/03/07/Golang-Daily-Questions-3/"/>
<id>http://blog.caoxl.com/2023/03/07/Golang-Daily-Questions-3/</id>
<published>2023-03-07T01:00:36.000Z</published>
<updated>2023-03-07T07:24:04.801Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>好似又过了几个秋</p></blockquote><span id="more"></span><h2 id="Go每日一题-str"><a href="#Go每日一题-str" class="headerlink" title="Go每日一题 (str)"></a>Go每日一题 (str)</h2><p>关于字符串连接,下面语法正确的是?</p><ul><li>A. str := ‘abc’ + ‘123’</li><li>B. str := “abc” + “123”</li><li>C. str := ‘123’ + “abc”</li><li>D. fmt.Sprintf(“abc%d”, 123)</li></ul><h3 id="答案解析"><a href="#答案解析" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>BD<br>除了以上两种连接方式,还有 <code>strings.Join()</code>、<code>buffer.WriteString()</code> 等。</p></blockquote><h2 id="Go每日一题-结构体-引用"><a href="#Go每日一题-结构体-引用" class="headerlink" title="Go每日一题 (结构体 引用)"></a>Go每日一题 (结构体 引用)</h2><p>以下代码能否编译?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> list <span class="keyword">map</span>[<span class="type">string</span>]Student</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> list = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]Student)</span><br><span class="line"></span><br><span class="line"> student := Student{<span class="string">"Aceld"</span>}</span><br><span class="line"></span><br><span class="line"> list[<span class="string">"student"</span>] = student</span><br><span class="line"> list[<span class="string">"student"</span>].Name = <span class="string">"LDB"</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(list[<span class="string">"student"</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-1"><a href="#答案解析-1" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>编译失败<br>./main.go:18:2: cannot assign to struct field list[“student”].Name in map</p></blockquote><ul><li>修改成以下可编译运行</li></ul><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> list <span class="keyword">map</span>[<span class="type">string</span>]*Student</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> list = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]*Student)</span><br><span class="line"></span><br><span class="line"> student := Student{<span class="string">"Aceld"</span>}</span><br><span class="line"></span><br><span class="line"> list[<span class="string">"student"</span>] = &student</span><br><span class="line"> list[<span class="string">"student"</span>].Name = <span class="string">"LDB"</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(list[<span class="string">"student"</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-结构体-range-遍历"><a href="#Go每日一题-结构体-range-遍历" class="headerlink" title="Go每日一题 (结构体 range 遍历)"></a>Go每日一题 (结构体 range 遍历)</h2><p>以下代码有什么问题,说明原因</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> student <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line"> Age <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// 定义map</span></span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]*student)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义student数组</span></span><br><span class="line"> stus := []student{</span><br><span class="line"> {Name: <span class="string">"zhou"</span>, Age: <span class="number">24</span>},</span><br><span class="line"> {Name: <span class="string">"li"</span>, Age: <span class="number">23</span>},</span><br><span class="line"> {Name: <span class="string">"wang"</span>, Age: <span class="number">22</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将数组依次添加到map中</span></span><br><span class="line"> <span class="keyword">for</span> _, stu := <span class="keyword">range</span> stus {</span><br><span class="line"> m[stu.Name] = &stu</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 打印map</span></span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k ,<span class="string">"=>"</span>, v.Name)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-2"><a href="#答案解析-2" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>遍历结果出现错误,输出结果为<br>zhou => wang<br>li => wang<br>wang => wang</p></blockquote><ul><li>正确写法</li></ul><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> student <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line"> Age <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// 定义map</span></span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]*student)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义student数组</span></span><br><span class="line"> stus := []student{</span><br><span class="line"> {Name: <span class="string">"zhou"</span>, Age: <span class="number">24</span>},</span><br><span class="line"> {Name: <span class="string">"li"</span>, Age: <span class="number">23</span>},</span><br><span class="line"> {Name: <span class="string">"wang"</span>, Age: <span class="number">22</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 遍历结构体数组,依次赋值给map</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">len</span>(stus); i++ {</span><br><span class="line"> m[stus[i].Name] = &stus[i]</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 打印map</span></span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k ,<span class="string">"=>"</span>, v.Name)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>或者修改</li></ul><figure class="highlight golang"><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">// 将数组依次添加到map中</span></span><br><span class="line"><span class="keyword">for</span> _, stu := <span class="keyword">range</span> stus {</span><br><span class="line">val := stu</span><br><span class="line">m[stu.Name] = &val</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-nil"><a href="#Go每日一题-nil" class="headerlink" title="Go每日一题 (nil)"></a>Go每日一题 (nil)</h2><p>下面赋值正确的是:</p><ul><li>A. var x = nil</li><li>B. var x interface{} = nil</li><li>C. var x string = nil</li><li>D. var x error = nil</li></ul><h3 id="答案解析-3"><a href="#答案解析-3" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:BD。<br>nil 只能赋值给指针、chan、func、interface、map 或 slice 类型的变量。<br>强调下 D 选项的 error 类型,它是一种内置接口类型,看它的源码就知道,所以 D 是对的。</p></blockquote><h2 id="Go每日一题-继承与多态"><a href="#Go每日一题-继承与多态" class="headerlink" title="Go每日一题 (继承与多态)"></a>Go每日一题 (继承与多态)</h2><p>以下代码能通过编译吗?为什么?</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> People <span class="keyword">interface</span> {</span><br><span class="line"> Speak(<span class="type">string</span>) <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(stu *Student)</span></span> Speak(think <span class="type">string</span>) (talk <span class="type">string</span>) {</span><br><span class="line"> <span class="keyword">if</span> think == <span class="string">"love"</span> {</span><br><span class="line"> talk = <span class="string">"You are a good boy"</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> talk = <span class="string">"hi"</span></span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> peo People = Student{}</span><br><span class="line"> think := <span class="string">"love"</span></span><br><span class="line"> fmt.Println(peo.Speak(think))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-4"><a href="#答案解析-4" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>编译失败</p></blockquote><ul><li>需要修改成以下</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> peo People = &Student{}</span><br><span class="line"> think := <span class="string">"love"</span></span><br><span class="line"> fmt.Println(peo.Speak(think))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-interface-接口底层类型问题"><a href="#Go每日一题-interface-接口底层类型问题" class="headerlink" title="Go每日一题 (interface 接口底层类型问题)"></a>Go每日一题 (interface 接口底层类型问题)</h2><p>以下代码打印出来什么内容,说出为什么。</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> People <span class="keyword">interface</span> {</span><br><span class="line"> Show()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(stu *Student)</span></span> Show() {</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">func</span> <span class="title">live</span><span class="params">()</span></span> People {</span><br><span class="line"> <span class="keyword">var</span> stu *Student</span><br><span class="line"> <span class="keyword">return</span> stu</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> live() == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"AAAAAAA"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"BBBBBBB"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-5"><a href="#答案解析-5" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>BBBBBBB</p></blockquote><ul><li>空接口eface</li></ul><figure class="highlight golang"><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="keyword">type</span> eface <span class="keyword">struct</span> { <span class="comment">// 空接口</span></span><br><span class="line"> _type *_type <span class="comment">// 类型信息</span></span><br><span class="line"> data unsafe.Pointer <span class="comment">// 指向数据的指针(go 语言中特殊的指针类型 unsafe.Pointer 类似于 c 语言中的void*)</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>非空接口iface</li></ul><figure class="highlight golang"><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="keyword">type</span> iface <span class="keyword">struct</span> {</span><br><span class="line"> tab *itab</span><br><span class="line"> data unsafe.Pointer</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-encoding-x2F-json-导出"><a href="#Go每日一题-encoding-x2F-json-导出" class="headerlink" title="Go每日一题 (encoding/json 导出)"></a>Go每日一题 (encoding/json 导出)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"encoding/json"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> t := <span class="keyword">struct</span> {</span><br><span class="line"> time.Time</span><br><span class="line"> N <span class="type">int</span></span><br><span class="line"> }{</span><br><span class="line"> time.Date(<span class="number">2020</span>, <span class="number">12</span>, <span class="number">20</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, time.UTC),</span><br><span class="line"> <span class="number">5</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m, _ := json.Marshal(t)</span><br><span class="line"> fmt.Printf(<span class="string">"%s"</span>, m)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:{“Time”: “2020-12-20T00:00:00Z”, “N”: 5};</li><li>B:”2020-12-20T00:00:00Z”;</li><li>C:{“N”: 5};</li><li>D</li></ul><h3 id="答案解析-6"><a href="#答案解析-6" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>B<br><code>json.Marshal</code> 函数优先调用 <code>MarshalJSON</code>,然后是 <code>MarshalText</code>,如果都没有,才会走正常的类型编码逻辑。<br>如果值实现了 <code>json.Marshaler</code> 接口并且不是 <code>nil</code> 指针,则 <code>Marshal</code> 函数会调用其 <code>MarshalJSON</code> 方法以生成 JSON。<br>如果不存在 <code>MarshalJSON</code> 方法,但该值实现 <code>encoding.TextMarshaler</code> 接口,则 <code>Marshal</code> 函数调用其 <code>MarshalText</code> 方法并将结果编码为 JSON 字符串。<br>在本题中匿名结构体内嵌了Time结构体,而该结构体实现了MarshalJSON 方法,因此会调用MarshalJSON方法以生成 JSON。</p></blockquote><h2 id="Go每日一题-类型-数据溢出-补码"><a href="#Go每日一题-类型-数据溢出-补码" class="headerlink" title="Go每日一题 (类型 数据溢出 补码)"></a>Go每日一题 (类型 数据溢出 补码)</h2><p>今天给两道类似的题目,注意,有半数以上的人可能会做错!</p><ul><li>题一</li></ul><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a <span class="type">int8</span> = <span class="number">-1</span></span><br><span class="line"> <span class="keyword">var</span> b <span class="type">int8</span> = <span class="number">-128</span> / a</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println</span>(b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>题二</li></ul><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> a <span class="type">int8</span> = <span class="number">-1</span></span><br><span class="line"> <span class="keyword">var</span> b <span class="type">int8</span> = <span class="number">-128</span> / a</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println</span>(b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>它们分别输出什么?</p><h3 id="答案解析-7"><a href="#答案解析-7" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>题一 -128,题二编译错误?</p></blockquote><p>因为 <code>var b int8 = -128 / a</code> 不是常量表达式,因此 untyped 常量 -128 隐式转换为 int8 类型(即和 a 的类型一致),<br>所以 <code>-128 / a</code> 的结果是 int8 类型,值是 128,超出了 int8 的范围。因为结果不是常量,允许溢出,<br>128 的二进制表示是 10000000,正好是 -128 的补码。所以,第一题的结果是 -128。</p><p>对于 <code>var b int8 = -128 / a</code>,因为 a 是 int8 类型常量,所以 -128 / a 是常量表达式,在编译器计算,结果必然也是常量。<br>因为 a 的类型是 int8,因此 -128 也会隐式转为 int8 类型,128 这个结果超过了 int8 的范围,但常量不允许溢出,因此编译报错。</p><h2 id="Go每日一题-init"><a href="#Go每日一题-init" class="headerlink" title="Go每日一题 (init)"></a>Go每日一题 (init)</h2><p>关于 init 函数,下面说法正确的是:</p><ul><li>A. 一个包中,可以包含多个 init 函数;</li><li>B. 程序运行时,先执行依赖包的 init 函数,再执行 main 包内的 init 函数;</li><li>C. main 包中,不能有 init 函数;</li><li>D. init 函数可以被其他函数调用;</li></ul><h3 id="答案解析-8"><a href="#答案解析-8" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>案及解析:AB。</p></blockquote><p>go文件的初始化顺序</p><ol><li>引入的包</li><li>当前包中的常量变量</li><li>当前包的init</li><li>main函数</li></ol><h2 id="Go每日一题-Go-中-函数-是-一等公民"><a href="#Go每日一题-Go-中-函数-是-一等公民" class="headerlink" title="Go每日一题 (Go 中 函数 是 一等公民)"></a>Go每日一题 (Go 中 函数 是 一等公民)</h2><p>下面这段代码输出什么以及原因?</p><figure class="highlight golang"><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">func</span> <span class="title">hello</span><span class="params">()</span></span> []<span class="type">string</span> { </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> h := hello</span><br><span class="line"> <span class="keyword">if</span> h == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"nil"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"not nil"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. nil</li><li>B. not nil</li><li>C. compilation error</li></ul><h3 id="答案解析-9"><a href="#答案解析-9" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>B<br>这道题目里面,是将函数 hello 赋值给变量 h,而不是函数的返回值(即不是进行函数调用),所以输出 not nil。注意 Go 中函数是一等公民。</p></blockquote><h2 id="Go每日一题-类型断言"><a href="#Go每日一题-类型断言" class="headerlink" title="Go每日一题 (类型断言)"></a>Go每日一题 (类型断言)</h2><p>下面这段代码能否编译通过?如果可以,输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">GetValue</span><span class="params">()</span></span> <span class="type">int</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i := GetValue()</span><br><span class="line"> <span class="keyword">switch</span> i.(<span class="keyword">type</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="type">int</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"int"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">string</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"string"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="keyword">interface</span>{}:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"interface"</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"unknown"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-10"><a href="#答案解析-10" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:编译失败。</p></blockquote><p>考点:类型断言,类型断言的语法形如:i.(type),其中 i 是接口,type 是固定关键字,需要注意的是,只有接口类型才可以使用类型断言。</p><h2 id="Go每日一题-channel-延迟关闭"><a href="#Go每日一题-channel-延迟关闭" class="headerlink" title="Go每日一题 (channel 延迟关闭)"></a>Go每日一题 (channel 延迟关闭)</h2><p>执行下面的代码会发生什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">1000</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> ch <- i</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> a, ok := <-ch</span><br><span class="line"> <span class="keyword">if</span> !ok {</span><br><span class="line"> fmt.Println(<span class="string">"close"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"a: "</span>, a)</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"> <span class="built_in">close</span>(ch)</span><br><span class="line"> fmt.Println(<span class="string">"ok"</span>)</span><br><span class="line"> time.Sleep(time.Second * <span class="number">100</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-11"><a href="#答案解析-11" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>ok<br>close<br>panic: send on closed channel</p></blockquote><p>记住channel的一些关键特性</p><ul><li>给一个 nil channel 发送数据,造成永远阻塞</li><li>从一个 nil channel 接收数据,造成永远阻塞</li><li>给一个已经关闭的 channel 发送数据,引起 panic</li><li>从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值</li><li>无缓冲的channel是同步的,而有缓冲的channel是非同步的</li></ul><blockquote><p>15字口诀:“空读写阻塞,写关闭异常,读关闭空零”</p></blockquote><h2 id="Go每日一题-WaitGroup"><a href="#Go每日一题-WaitGroup" class="headerlink" title="Go每日一题 (WaitGroup)"></a>Go每日一题 (WaitGroup)</h2><p>以下代码有什么问题?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> N = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg = &sync.WaitGroup{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < N; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>)</span></span> {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="built_in">println</span>(i)</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-12"><a href="#答案解析-12" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>输出结果不唯一,代码存在风险, 所有 go 语句未必都能执行到。<br>这是使用 WaitGroup 经常犯下的错误!请各位同学多次运行就会发现输出都会不同甚至又出现报错的问题。<br>这是因为 go 执行太快了,导致 wg.Add(1) 还没有执行 main 函数就执行完毕了。wg.Add 的位置放错了</p></blockquote><ul><li>改为下面的代码试试:</li></ul><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> N = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg = &sync.WaitGroup{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</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> i:= <span class="number">0</span>; i< N; i++ {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="built_in">println</span>(i)</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>原子操作边界问题,在边界前加锁,边界后解锁。</p></blockquote><h2 id="Go每日一题-channel"><a href="#Go每日一题-channel" class="headerlink" title="Go每日一题 (channel)"></a>Go每日一题 (channel)</h2><p>关于 channel,下面语法正确的是</p><ul><li>A. var ch chan int</li><li>B. ch := make(chan int)</li><li>C. <- ch</li><li>D. ch <-</li></ul><h3 id="答案解析-13"><a href="#答案解析-13" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:ABC。<br>A、B 都是声明 channel;C 读取 channel;写 channel 是必须带上值,所以 D 错误。</p></blockquote><h2 id="Go每日一题-map-0-值"><a href="#Go每日一题-map-0-值" class="headerlink" title="Go每日一题 (map 0 值)"></a>Go每日一题 (map 0 值)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">type</span> person <span class="keyword">struct</span> { </span><br><span class="line"> name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> <span class="keyword">var</span> m <span class="keyword">map</span>[person]<span class="type">int</span></span><br><span class="line"> p := person{<span class="string">"mike"</span>}</span><br><span class="line"> fmt.Println(m[p])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 0</li><li>B. 1</li><li>C. Compilation error</li></ul><h3 id="答案解析-14"><a href="#答案解析-14" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br>m 是一个 map,值是 nil。从 nil map 中取值不会报错,而是返回相应的零值,这里值是 int 类型,因此返回 0。</p></blockquote><p><code>map[p]</code> 在经过 编译后,走的是 <code>runtime.mapaccess1</code> 的逻辑;<br>而看到 <code>mapaccess1</code> 函数,对于 hmap 是 nil 的情况是直接返回空值;源代码如下:</p><figure class="highlight golang"><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">func</span> <span class="title">mapaccess1</span><span class="params">(t *maptype, h *hmap, key unsafe.Pointer)</span></span> unsafe.Pointer {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> h == <span class="literal">nil</span> || h.count == <span class="number">0</span> {<span class="comment">// h 就是map指向的地址,因为题目中map还没有申请分配内存空间,所以h是nil</span></span><br><span class="line"> <span class="keyword">if</span> t.hashMightPanic() {</span><br><span class="line"> t.hasher(key, <span class="number">0</span>) <span class="comment">// see issue 23734</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> unsafe.Pointer(&zeroVal[<span class="number">0</span>])</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-可变参数函数"><a href="#Go每日一题-可变参数函数" class="headerlink" title="Go每日一题 (可变参数函数)"></a>Go每日一题 (可变参数函数)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">hello</span><span class="params">(num ...<span class="type">int</span>)</span></span> { </span><br><span class="line"> num[<span class="number">0</span>] = <span class="number">18</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> i := []<span class="type">int</span>{<span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>}</span><br><span class="line"> hello(i...)</span><br><span class="line"> fmt.Println(i[<span class="number">0</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 18</li><li>B. 5</li><li>C. Compilation error</li></ul><h3 id="答案解析-15"><a href="#答案解析-15" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A. 18。<br>可变参数是切片,切片是引用,所以func内赋值会带出来。</p></blockquote><h2 id="Go每日一题-强类型"><a href="#Go每日一题-强类型" class="headerlink" title="Go每日一题 (强类型)"></a>Go每日一题 (强类型)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> a := <span class="number">5</span></span><br><span class="line"> b := <span class="number">8.1</span></span><br><span class="line"> fmt.Println(a + b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 13.1</li><li>B. 13</li><li>C. compilation error</li></ul><h3 id="答案解析-16"><a href="#答案解析-16" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:C。<br>a 的类型是 int,b 的类型是 float,两个不同类型的数值不能相加,编译报错。<br>Go语言的类型机制更加严格,没有隐式类型转换,所以不同类型的数据不能直接参与同一个运算。</p></blockquote><h2 id="Go每日一题-json-slice"><a href="#Go每日一题-json-slice" class="headerlink" title="Go每日一题 (json slice)"></a>Go每日一题 (json slice)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"encoding/json"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> AutoGenerated <span class="keyword">struct</span> {</span><br><span class="line"> Age <span class="type">int</span> <span class="string">`json:"age"`</span></span><br><span class="line"> Name <span class="type">string</span> <span class="string">`json:"name"`</span></span><br><span class="line"> Child []<span class="type">int</span> <span class="string">`json:"child"`</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> jsonStr1 := <span class="string">`{"age": 14,"name": "potter", "child":[1,2,3]}`</span></span><br><span class="line"> a := AutoGenerated{}</span><br><span class="line"> json.Unmarshal([]<span class="type">byte</span>(jsonStr1), &a)</span><br><span class="line"> aa := a.Child</span><br><span class="line"> fmt.Println(aa)</span><br><span class="line"> jsonStr2 := <span class="string">`{"age": 12,"name": "potter", "child":[3,4,5,7,8,9]}`</span></span><br><span class="line"> json.Unmarshal([]<span class="type">byte</span>(jsonStr2), &a)</span><br><span class="line"> fmt.Println(aa)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A: [1 2 3] [1 2 3] ;</li><li>B: [1 2 3] [3 4 5];</li><li>C: [1 2 3] [3 4 5 6 7 8 9];</li><li>D: [1 2 3] [3 4 5 0 0 0]</li></ul><h3 id="答案解析-17"><a href="#答案解析-17" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>结果为什么是 [1 2 3] [3 4 5] 呢?</p></blockquote><p>这道题涉及到两个知识点:</p><ul><li>json解析</li><li>slice</li></ul><p>第一次解析,aa.Child 是:[1 2 3],cap = 4。第二次解析,aa.Child 先被重置,之后将 3,4,5,7,8,9 一个个 append,<br>最后 aa.Child 是:[3 4 5 6 7 8 9], cap = 6。</p><p>aa在第一次赋值时,就已经确定了长度和容量;第二次反序列化json时,aa的长度和容量是没有变化的,<br>但是底层数组却发生了变化(底层数组的数据内容,长度,容量都改变了),aa是从底层数组上取数据的</p><h2 id="Go每日一题-数据竞争-main-goroutine-和其他-goroutine-调度问题-局部变量问题"><a href="#Go每日一题-数据竞争-main-goroutine-和其他-goroutine-调度问题-局部变量问题" class="headerlink" title="Go每日一题 (数据竞争 main goroutine 和其他 goroutine 调度问题 局部变量问题)"></a>Go每日一题 (数据竞争 main goroutine 和其他 goroutine 调度问题 局部变量问题)</h2><p>以下代码有什么问题,怎么解决?</p><figure class="highlight golang"><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">total, sum := <span class="number">0</span>, <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i <= <span class="number">10</span>; i++ {</span><br><span class="line"> sum += i</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> total += i</span><br><span class="line"> }()</span><br><span class="line">}</span><br><span class="line">fmt.Printf(<span class="string">"total:%d sum %d"</span>, total, sum)</span><br></pre></td></tr></table></figure><h3 id="答案解析-18"><a href="#答案解析-18" class="headerlink" title="答案解析"></a>答案解析</h3><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> total, sum := <span class="number">0</span>, <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">1</span>; i <= <span class="number">10</span>; i++ {</span><br><span class="line"> sum += i</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>)</span></span> {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line"> total += i</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(<span class="number">1e9</span>)</span><br><span class="line"> fmt.Printf(<span class="string">"total:%d sum %d"</span>, total, sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>解决一下问题:</p><ul><li>i 使用的问题</li><li>data race。因为存在多 goroutine 同时写 total 变量的问题,所以有数据竞争。可以加上 -race 参数验证:</li><li>main 函数先退出了,开启的 goroutine 根本没有机会执行。</li></ul><h2 id="Go每日一题-数据类型"><a href="#Go每日一题-数据类型" class="headerlink" title="Go每日一题 (数据类型)"></a>Go每日一题 (数据类型)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := [<span class="number">2</span>]<span class="type">int</span>{<span class="number">5</span>, <span class="number">6</span>}</span><br><span class="line"> b := [<span class="number">3</span>]<span class="type">int</span>{<span class="number">5</span>, <span class="number">6</span>}</span><br><span class="line"> <span class="keyword">if</span> a == b {</span><br><span class="line"> fmt.Println(<span class="string">"equal"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"not equal"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. compilation error</li><li>B. equal</li><li>C. not equal</li></ul><h3 id="答案解析-19"><a href="#答案解析-19" class="headerlink" title="答案解析"></a>答案解析</h3><p>Go 中的数组是值类型,可比较,另外一方面,<br>数组的长度也是数组类型的组成部分,所以 a 和 b 是不同的类型,是不能比较的,所以编译错误。</p><h2 id="Go每日一题-cap"><a href="#Go每日一题-cap" class="headerlink" title="Go每日一题 (cap)"></a>Go每日一题 (cap)</h2><p>以下哪种类型可以使用 cap() 函数?</p><ul><li>A. array</li><li>B. slice</li><li>C. map</li><li>D. channel</li></ul><h3 id="答案解析-20"><a href="#答案解析-20" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:ABD。<br>知识点:cap(),cap() 函数不适用 map。</p></blockquote><h2 id="Go每日一题-interface-零值"><a href="#Go每日一题-interface-零值" class="headerlink" title="Go每日一题 (interface 零值)"></a>Go每日一题 (interface 零值)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">interface</span>{}</span><br><span class="line"> <span class="keyword">if</span> i == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"nil"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"not nil"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. nil</li><li>B. not nil</li><li>C. compilation error</li></ul><h3 id="答案解析-21"><a href="#答案解析-21" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br>当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。</p></blockquote><h2 id="Go每日一题-map-零值"><a href="#Go每日一题-map-零值" class="headerlink" title="Go每日一题 (map 零值)"></a>Go每日一题 (map 零值)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br><span class="line"> <span class="built_in">delete</span>(s, <span class="string">"h"</span>)</span><br><span class="line"> fmt.Println(s[<span class="string">"h"</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. runtime panic</li><li>B. 0</li><li>C. compilation error</li></ul><h3 id="答案解析-22"><a href="#答案解析-22" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B。<br>删除 map 不存在的键值对时,不会报错,相当于没有任何作用;获取不存在的减值对时,返回值类型对应的零值,所以返回 0。</p></blockquote><h2 id="Go每日一题-符号输出"><a href="#Go每日一题-符号输出" class="headerlink" title="Go每日一题 (符号输出)"></a>Go每日一题 (符号输出)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> { </span><br><span class="line"> i := <span class="number">-5</span></span><br><span class="line"> j := +<span class="number">5</span></span><br><span class="line"> fmt.Printf(<span class="string">"%+d %+d"</span>, i, j)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. -5 +5</li><li>B. +5 +5</li><li>C. 0 0</li></ul><h3 id="答案解析-23"><a href="#答案解析-23" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:A。<br><code>%d</code>表示输出十进制数字,<code>+</code>表示输出数值的符号。这里不表示取反。</p></blockquote><h2 id="Go每日一题-接口-内嵌-组合"><a href="#Go每日一题-接口-内嵌-组合" class="headerlink" title="Go每日一题 (接口 内嵌 组合)"></a>Go每日一题 (接口 内嵌 组合)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> People <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *People)</span></span> ShowA() {</span><br><span class="line"> fmt.Println(<span class="string">"showA"</span>)</span><br><span class="line"> p.ShowB()</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *People)</span></span> ShowB() {</span><br><span class="line"> fmt.Println(<span class="string">"showB"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Teacher <span class="keyword">struct</span> {</span><br><span class="line"> People</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Teacher)</span></span> ShowB() {</span><br><span class="line"> fmt.Println(<span class="string">"teacher showB"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> t := Teacher{}</span><br><span class="line"> t.ShowB()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-24"><a href="#答案解析-24" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:teacher showB。</p></blockquote><p>知识点:结构体嵌套。<br>在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;<br>通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。<br>此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。<br>这个例子中的 <code>ShowB()</code> 就是同名方法。</p>]]></content>
<summary type="html">
<blockquote>
<p>好似又过了几个秋</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="每日一题" scheme="http://blog.caoxl.com/tags/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
</entry>
<entry>
<title>Golang 每日一题 202212篇</title>
<link href="http://blog.caoxl.com/2023/02/27/Golang-Daily-Questions-2/"/>
<id>http://blog.caoxl.com/2023/02/27/Golang-Daily-Questions-2/</id>
<published>2023-02-27T08:24:44.000Z</published>
<updated>2023-02-28T03:44:23.985Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>从问题中发现问题, 从提问中提升自己</p></blockquote><span id="more"></span><h2 id="Go每日一题-闭包"><a href="#Go每日一题-闭包" class="headerlink" title="Go每日一题 (闭包)"></a>Go每日一题 (闭包)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">app</span><span class="params">()</span></span> <span class="function"><span class="keyword">func</span><span class="params">(<span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> t := <span class="string">"Hi"</span></span><br><span class="line"> c := <span class="function"><span class="keyword">func</span><span class="params">(b <span class="type">string</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> t = t + <span class="string">" "</span> + b</span><br><span class="line"> <span class="keyword">return</span> t</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> c</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := app()</span><br><span class="line"> fmt.Printf(<span class="string">"a: %p\n"</span>, &a)</span><br><span class="line"> b := app()</span><br><span class="line"> fmt.Printf(<span class="string">"b: %p\n"</span>, &b)</span><br><span class="line"> a(<span class="string">"go"</span>)</span><br><span class="line"> fmt.Println(b(<span class="string">"All"</span>))</span><br><span class="line"> <span class="comment">//fmt.Println(a("All"))</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>A:Hi All;B:Hi go All;C:Hi;D:go All</p><h3 id="答案解析"><a href="#答案解析" class="headerlink" title="答案解析"></a>答案解析</h3><p>如果最后再加一行代码:fmt.Println(a(“All”)), 它输出什么?</p><ul><li>自测结果:</li></ul><blockquote><p>a: 0x1400000e028<br>b: 0x1400000e038<br>Hi All<br>Hi go All</p></blockquote><h2 id="Go每日一题-闭包-指针变量"><a href="#Go每日一题-闭包-指针变量" class="headerlink" title="Go每日一题 (闭包 指针变量)"></a>Go每日一题 (闭包 指针变量)</h2><p>下面代码段输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Person <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> person := &Person{<span class="number">28</span>}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 1.</span></span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(person.age)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2.</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">(p *Person)</span></span> {</span><br><span class="line"> fmt.Println(p.age)</span><br><span class="line"> }(person)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(person.age)</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> person.age = <span class="number">29</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>答案及解析:29 29 28。变量 person 是一个指针变量。</p><ol><li><code>person.age</code> 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;</li><li>defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29;</li><li>很简单,闭包引用,输出 29; 又由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 29 28。</li></ol><h2 id="Go每日一题-结构体-指针"><a href="#Go每日一题-结构体-指针" class="headerlink" title="Go每日一题 (结构体 指针)"></a>Go每日一题 (结构体 指针)</h2><p>下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> S <span class="keyword">struct</span> {</span><br><span class="line"> m <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">()</span></span> *S {</span><br><span class="line"> <span class="keyword">return</span> __ <span class="comment">//A</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> p := __ <span class="comment">//B</span></span><br><span class="line"> fmt.Println(p.m) <span class="comment">//print "foo"</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>A. <code>&S{"foo"}</code> B. <code>*f()</code> 或者 <code>f()</code></p><blockquote><p>f() 函数返回参数是指针类型,所以可以用 & 取结构体的指针;<br>B 处,如果填 *f(),则 p 是 S 类型;如果填 f(),则 p 是 *S 类型,不过都可以使用 p.m 取得结构体的成员。</p></blockquote><h2 id="Go每日一题-常量表达式"><a href="#Go每日一题-常量表达式" class="headerlink" title="Go每日一题 (常量表达式)"></a>Go每日一题 (常量表达式)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> ans <span class="type">float64</span> = <span class="number">15</span> + <span class="number">25</span> + <span class="number">5.2</span></span><br><span class="line"> fmt.Println(ans)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>A:不能编译;B:45;C:45.2;D:45.0</p><h3 id="答案解析-3"><a href="#答案解析-3" class="headerlink" title="答案解析"></a>答案解析</h3><p>常量表达式是指仅包含常量操作数,且是在编译的时候进行计算的。</p><p>而常量,在 Go 语言中又可以分为无类型常量和有类型常量,也可以分为字面值常量和具名常量。说人话?!</p><p>通过代码看看:</p><figure class="highlight golang"><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">const</span> a = <span class="number">1</span> + <span class="number">2</span> <span class="comment">// a == 3,是无类型常量</span></span><br><span class="line"><span class="keyword">const</span> b <span class="type">int8</span> = <span class="number">1</span> + <span class="number">2</span> <span class="comment">// b == 3,是有类型常量,类型是 int8</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 而 1、2 这样的就是字面值常量</span></span><br><span class="line"><span class="comment">// a、b 这样的就是具名常量</span></span><br></pre></td></tr></table></figure><h2 id="Go每日一题-字符串类型"><a href="#Go每日一题-字符串类型" class="headerlink" title="Go每日一题 (字符串类型)"></a>Go每日一题 (字符串类型)</h2><p>下面的代码有几处语法问题,各是什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x <span class="type">string</span> = <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">if</span> x == <span class="literal">nil</span> {</span><br><span class="line"> x = <span class="string">"default"</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-4"><a href="#答案解析-4" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>2 处有语法问题。<br>golang 的字符串类型是不能赋值 nil 的,也不能跟 nil 比较。</p></blockquote><h2 id="Go每日一题-defer"><a href="#Go每日一题-defer" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>return 之后的 defer 语句会执行吗,下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a <span class="type">bool</span> = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"1"</span>)</span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">if</span> a == <span class="literal">true</span> {</span><br><span class="line"> fmt.Println(<span class="string">"2"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"3"</span>)</span><br><span class="line"> }()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-5"><a href="#答案解析-5" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>2 1<br>defer 关键字后面的函数或者方法想要执行必须先注册,<strong>return 之后的 defer 是不能注册的</strong>, 也就不能执行后面的函数或方法。</p></blockquote><h2 id="Go每日一题-切片"><a href="#Go每日一题-切片" class="headerlink" title="Go每日一题 (切片)"></a>Go每日一题 (切片)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> s2 := s1[<span class="number">1</span>:]</span><br><span class="line"> s2[<span class="number">1</span>] = <span class="number">4</span></span><br><span class="line"> fmt.Println(s1)</span><br><span class="line"> s2 = <span class="built_in">append</span>(s2, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br><span class="line"> fmt.Println(s1)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-6"><a href="#答案解析-6" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>[1 2 4]<br>[1 2 4]</p></blockquote><p>golang 中切片底层的数据结构是数组。<br>当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组,这会导致 s2[1] = 4 语句影响 s1。</p><p>而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1。</p><p>但是为什么对 s2 赋值后影响的却是 s1 的第三个元素呢?<br>这是因为切片 s2 是从数组的第二个元素开始,s2 索引为 1 的元素对应的是 s1 索引为 2 的元素。</p><h2 id="Go每日一题-代码块-作用域"><a href="#Go每日一题-代码块-作用域" class="headerlink" title="Go每日一题 (代码块 作用域)"></a>Go每日一题 (代码块 作用域)</h2><p>下面选项正确的是?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> a := <span class="number">1</span>; <span class="literal">false</span> {</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> b := <span class="number">2</span>; <span class="literal">false</span> {</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">println</span>(a, b)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>A. 1 2<br>B. compilation error</p><h3 id="答案解析-7"><a href="#答案解析-7" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>A。<br>解析: <a href="https://studygolang.com/articles/35587">https://studygolang.com/articles/35587</a></p></blockquote><h2 id="Go每日一题-切片-len"><a href="#Go每日一题-切片-len" class="headerlink" title="Go每日一题 (切片 len)"></a>Go每日一题 (切片 len)</h2><p>以下代码输出什么:</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x *<span class="keyword">struct</span> {</span><br><span class="line"> s [][<span class="number">32</span>]<span class="type">byte</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">println</span>(<span class="built_in">len</span>(x.s[<span class="number">99</span>]))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:运行时 panic;</li><li>B:32;</li><li>C:编译错误;</li><li>D:0</li></ul><h3 id="答案解析-8"><a href="#答案解析-8" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>正确答案:B。</p></blockquote><p>注意这里不是定义一个结构体类型,而是定义一个结构体类型指针变量,即 x 是一个指针,指针类型是一个匿名结构体。<br>很显然,x 的值是 nil,因为没有初始化,可以打印证实这一点。</p><blockquote><p>解析: <a href="https://polarisxu.studygolang.com/posts/go/action/weekly-question-104/">https://polarisxu.studygolang.com/posts/go/action/weekly-question-104/</a></p></blockquote><h2 id="Go每日一题-类型方法"><a href="#Go每日一题-类型方法" class="headerlink" title="Go每日一题 (类型方法)"></a>Go每日一题 (类型方法)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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">func</span> <span class="params">(i <span class="type">int</span>)</span></span> PrintInt () {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"> i.PrintInt()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 1</li><li>B. compilation error</li></ul><h3 id="答案解析-9"><a href="#答案解析-9" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B。<br><strong>基于类型创建的方法必须定义在同一个包内</strong>,上面的代码基于 int 类型创建了 PrintInt() 方法,<br>由于 int 类型和方法 PrintInt() 定义在不同的包内,所以编译出错。解决的办法可以定义一种新的类型:</p></blockquote><figure class="highlight golang"><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">type</span> Myint <span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(i Myint)</span></span> PrintInt () {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i Myint = <span class="number">1</span></span><br><span class="line"> i.PrintInt()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-类型方法-1"><a href="#Go每日一题-类型方法-1" class="headerlink" title="Go每日一题 (类型方法)"></a>Go每日一题 (类型方法)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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="keyword">type</span> People <span class="keyword">interface</span> {</span><br><span class="line"> Speak(<span class="type">string</span>) <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(stu *Student)</span></span> Speak(think <span class="type">string</span>) (talk <span class="type">string</span>) {</span><br><span class="line"> <span class="keyword">if</span> think == <span class="string">"speak"</span> {</span><br><span class="line"> talk = <span class="string">"speak"</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> talk = <span class="string">"hi"</span></span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> peo People = Student{}</span><br><span class="line"> think := <span class="string">"speak"</span></span><br><span class="line"> fmt.Println(peo.Speak(think))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-10"><a href="#答案解析-10" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>B.<br>编译错误: <code>./main.go:21:19: cannot use Student{} (value of type Student) as type People in variable declaration: Student does not implement People (Speak method has pointer receiver)</code><br>值类型 Student 没有实现接口的 Speak() 方法,而是指针类型 *Student 实现改方法。</p></blockquote><h2 id="Go每日一题-iota"><a href="#Go每日一题-iota" class="headerlink" title="Go每日一题 (iota)"></a>Go每日一题 (iota)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">const</span> (</span><br><span class="line"> a = <span class="literal">iota</span></span><br><span class="line"> b = <span class="literal">iota</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> name = <span class="string">"name"</span></span><br><span class="line"> c = <span class="literal">iota</span></span><br><span class="line"> d = <span class="literal">iota</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(a)</span><br><span class="line"> fmt.Println(b)</span><br><span class="line"> fmt.Println(c)</span><br><span class="line"> fmt.Println(d)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-11"><a href="#答案解析-11" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:0 1 1 2<br>iota 是 golang 语言的常量计数器,只能在常量的表达式中使用。<br><strong>iota 在 const 关键字出现时将被重置为0</strong>,const中每新增一行常量声明将使 iota 计数一次。</p></blockquote><h2 id="Go每日一题-动态类型"><a href="#Go每日一题-动态类型" class="headerlink" title="Go每日一题 (动态类型)"></a>Go每日一题 (动态类型)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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="keyword">type</span> People <span class="keyword">interface</span> {</span><br><span class="line"> Show()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(stu *Student)</span></span> Show() {</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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> s *Student</span><br><span class="line"> <span class="keyword">if</span> s == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"s is nil"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"s is not nil"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> p People = s</span><br><span class="line"> <span class="keyword">if</span> p == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"p is nil"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"p is not nil"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-12"><a href="#答案解析-12" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析: <code>s is nil</code> 和 <code>p is not nil</code><br>这道题会不会有点诧异,我们分配给变量 p 的值明明是 nil,然而 p 却不是 nil。<br>记住一点,当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。<br>上面的代码,给变量 p 赋值之后,p 的动态值是 nil,但是动态类型却是 *Student,是一个 nil 指针,所以相等条件不成立。</p></blockquote><h2 id="Go每日一题-iota-String方法"><a href="#Go每日一题-iota-String方法" class="headerlink" title="Go每日一题 (iota String方法)"></a>Go每日一题 (iota String方法)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> Direction <span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> North Direction = <span class="literal">iota</span></span><br><span class="line"> East</span><br><span class="line"> South</span><br><span class="line"> West</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d Direction)</span></span> String() <span class="type">string</span> {</span><br><span class="line"> <span class="keyword">return</span> [...]<span class="type">string</span>{<span class="string">"North"</span>, <span class="string">"East"</span>, <span class="string">"South"</span>, <span class="string">"West"</span>}[d]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(South)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-13"><a href="#答案解析-13" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:South<br>根据 iota 的用法推断出 South 的值是 2;另外,如果类型定义了 String() 方法,<br>当使用 <code>fmt.Printf()</code>、<code>fmt.Print()</code> 和 <code>fmt.Println()</code> 会自动使用 <code>String()</code> 方法,实现字符串的打印。</p></blockquote><h2 id="Go每日一题-map不能寻址"><a href="#Go每日一题-map不能寻址" class="headerlink" title="Go每日一题 (map不能寻址)"></a>Go每日一题 (map不能寻址)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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="keyword">type</span> Math <span class="keyword">struct</span> {</span><br><span class="line"> x, y <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]Math{</span><br><span class="line"> <span class="string">"foo"</span>: Math{<span class="number">2</span>, <span class="number">3</span>},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m[<span class="string">"foo"</span>].x = <span class="number">4</span></span><br><span class="line"> fmt.Println(m[<span class="string">"foo"</span>].x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 4</li><li>B. compilation error</li></ul><h3 id="答案解析-14"><a href="#答案解析-14" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B<br>./main.go:14:2: cannot assign to struct field m[“foo”].x in map<br>错误原因:对于类似 X = Y的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,<br><strong>但 go 中的 map 的 value 本身是不可寻址的。</strong></p></blockquote><ul><li>使用临时变量</li></ul><figure class="highlight golang"><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="keyword">type</span> Math <span class="keyword">struct</span> {</span><br><span class="line"> x, y <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]Math{</span><br><span class="line"> <span class="string">"foo"</span>: Math{<span class="number">2</span>, <span class="number">3</span>},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> tmp := m[<span class="string">"foo"</span>]</span><br><span class="line"> tmp.x = <span class="number">4</span></span><br><span class="line"> m[<span class="string">"foo"</span>] = tmp</span><br><span class="line"> fmt.Println(m[<span class="string">"foo"</span>].x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>修改数据结构</li></ul><figure class="highlight golang"><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="keyword">type</span> Math <span class="keyword">struct</span> {</span><br><span class="line"> x, y <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]*Math{</span><br><span class="line"> <span class="string">"foo"</span>: &Math{<span class="number">2</span>, <span class="number">3</span>},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m[<span class="string">"foo"</span>].x = <span class="number">4</span></span><br><span class="line"> fmt.Println(m[<span class="string">"foo"</span>].x)</span><br><span class="line"> fmt.Printf(<span class="string">"%#v"</span>, m[<span class="string">"foo"</span>]) <span class="comment">// %#v 格式化输出详细信息</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-select死锁"><a href="#Go每日一题-select死锁" class="headerlink" title="Go每日一题 (select死锁)"></a>Go每日一题 (select死锁)</h2><p>下面代码输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"sync"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> foo := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> bar := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> foo <- <-bar:</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"default"</span>)</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A:default</li><li>B:panic</li></ul><h3 id="答案解析-15"><a href="#答案解析-15" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:B</p></blockquote><h2 id="Go每日一题-切片不能比较"><a href="#Go每日一题-切片不能比较" class="headerlink" title="Go每日一题 (切片不能比较)"></a>Go每日一题 (切片不能比较)</h2><p>下面的代码有什么问题?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println([...]<span class="type">int</span>{<span class="number">1</span>} == [<span class="number">2</span>]<span class="type">int</span>{<span class="number">1</span>})</span><br><span class="line"> fmt.Println([]<span class="type">int</span>{<span class="number">1</span>} == []<span class="type">int</span>{<span class="number">1</span>})</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-16"><a href="#答案解析-16" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>有两处错误<br>go 中不同类型是不能比较的,而数组长度是数组类型的一部分,所以 […]int{1} 和 [2]int{1} 是两种不同的类型,不能比较;<br>切片是不能比较的;</p></blockquote><h2 id="Go每日一题-数组-循环"><a href="#Go每日一题-数组-循环" class="headerlink" title="Go每日一题 (数组 循环)"></a>Go每日一题 (数组 循环)</h2><p>下面这段代码能否正常结束?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> v {</span><br><span class="line"> v = <span class="built_in">append</span>(v, i)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-17"><a href="#答案解析-17" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:不会出现死循环,能正常结束</p></blockquote><p>循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数</p><h2 id="Go每日一题-切片-协程"><a href="#Go每日一题-切片-协程" class="headerlink" title="Go每日一题 (切片 协程)"></a>Go每日一题 (切片 协程)</h2><p>下面这段代码输出什么?为什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> m = [...]<span class="type">int</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 class="keyword">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(k, v)</span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(time.Second * <span class="number">3</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-18"><a href="#答案解析-18" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>2 3<br>2 3<br>2 3</p></blockquote><p>for range 使用短变量声明 (:=) 的形式迭代变量,需要注意的是,变量 k、v 在每次循环体中都会被重用,而不是重新声明。</p><p>各个 goroutine 中输出的 i、v 值都是 for range 循环结束后的 i、v 最终值,而不是各个 goroutine 启动时的 k, v值。可以理解为闭包引用,使用的是上下文环境的值。两种可行的 fix 方法:</p><ul><li>使用函数传递</li></ul><figure class="highlight golang"><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">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(k, v <span class="type">int</span>)</span></span> {</span><br><span class="line">fmt.Println(k, v)</span><br><span class="line">}(k, v)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>使用临时变量保留当前值</li></ul><figure class="highlight golang"><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">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"> k := k <span class="comment">// 这里的 := 会重新声明变量,而不是重用</span></span><br><span class="line"> v := v</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(k, v)</span><br><span class="line"> }()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-defer-1"><a href="#Go每日一题-defer-1" class="headerlink" title="Go每日一题 (defer)"></a>Go每日一题 (defer)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">f</span><span class="params">(n <span class="type">int</span>)</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> r += n</span><br><span class="line"> <span class="built_in">recover</span>()</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> f <span class="function"><span class="keyword">func</span><span class="params">()</span></span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">defer</span> f()</span><br><span class="line"> f = <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> r += <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> n + <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(f(<span class="number">3</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-19"><a href="#答案解析-19" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:7</p></blockquote><p><code>defer</code>的7个隐性必备知识点。</p><ol><li><code>defer</code>的执行顺序</li><li><code>defer</code>与<code>return</code>谁先谁后</li><li>函数的返回值初始化与<code>defer</code>间接影响</li><li>有名函数返回值遇见<code>defer</code>情况</li><li><code>defer</code>遇见<code>panic</code></li><li><code>defer</code>中包含<code>panic</code></li><li><code>defer</code>下的函数参数包含子函数</li></ol><ul><li><p><a href="https://studygolang.com/articles/27408">Golang中的Defer必掌握的7知识点</a></p></li><li><p>结论:</p></li><li><ol><li>多个defer出现的时候,它是一个“栈”的关系,也就是<strong>先进后出</strong>。</li></ol></li><li><ol start="2"><li><strong>return之后的语句先执行,defer后的语句后执行</strong></li></ol></li><li><ol start="3"><li>只要声明函数的返回值变量名称,就会在函数初始化时候为之赋值为0,而且在函数体作用域可见</li></ol></li><li><ol start="4"><li>先<code>return</code>,再<code>defer</code>,所以在执行完<code>return</code>之后,还要再执行<code>defer</code>里的语句,依然可以修改本应该返回的结果。</li></ol></li><li><ol start="5"><li>遇到<code>panic</code>时,遍历本协程的<code>defer</code>链表,并执行<code>defer</code>。在执行<code>defer</code>过程中:遇到<code>recover</code>则停止<code>panic</code>,返回<code>recover</code>处继续往下执行。 如果没有遇到<code>recover</code>,遍历完本协程的defer链表后,向stderr抛出panic信息。 defer 最大的功能是 panic 后依然有效</li></ol></li><li><ol start="6"><li><code>panic</code>仅有最后一个可以被<code>revover</code>捕获。</li></ol></li></ul><h2 id="Go每日一题-数组-循环-1"><a href="#Go每日一题-数组-循环-1" class="headerlink" title="Go每日一题 (数组 循环)"></a>Go每日一题 (数组 循环)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a = [<span class="number">5</span>]<span class="type">int</span>{<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">var</span> r [<span class="number">5</span>]<span class="type">int</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i, v := <span class="keyword">range</span> a {</span><br><span class="line"> <span class="keyword">if</span> i == <span class="number">0</span> {</span><br><span class="line"> a[<span class="number">1</span>] = <span class="number">12</span></span><br><span class="line"> a[<span class="number">2</span>] = <span class="number">13</span></span><br><span class="line"> }</span><br><span class="line"> r[i] = v</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"r = "</span>, r)</span><br><span class="line"> fmt.Println(<span class="string">"a = "</span>, a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-20"><a href="#答案解析-20" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>r = [1 2 3 4 5]<br>a = [1 12 13 4 5]</p></blockquote><p><strong>range 表达式是副本参与循环</strong>,就是说例子中参与循环的是 a 的副本,<br>而不是真正的 a。就这个例子来说,假设 b 是 a 的副本,则 range 循环代码是这样的:</p><figure class="highlight golang"><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">for</span> i, v := <span class="keyword">range</span> b {</span><br><span class="line"> <span class="keyword">if</span> i == <span class="number">0</span> {</span><br><span class="line"> a[<span class="number">1</span>] = <span class="number">12</span></span><br><span class="line"> a[<span class="number">2</span>] = <span class="number">13</span></span><br><span class="line"> }</span><br><span class="line"> r[i] = v</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-可变函数-切片-make-append"><a href="#Go每日一题-可变函数-切片-make-append" class="headerlink" title="Go每日一题 (可变函数 切片 make append)"></a>Go每日一题 (可变函数 切片 make append)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">change</span><span class="params">(s ...<span class="type">int</span>)</span></span> {</span><br><span class="line"> s = <span class="built_in">append</span>(s, <span class="number">3</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> slice := <span class="built_in">make</span>([]<span class="type">int</span>, <span class="number">5</span>, <span class="number">5</span>)</span><br><span class="line"> slice[<span class="number">0</span>] = <span class="number">1</span></span><br><span class="line"> slice[<span class="number">1</span>] = <span class="number">2</span></span><br><span class="line"> change(slice...)</span><br><span class="line"> fmt.Println(slice)</span><br><span class="line"> change(slice[<span class="number">0</span>:<span class="number">2</span>]...)</span><br><span class="line"> fmt.Println(slice)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-21"><a href="#答案解析-21" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>[1 2 0 0 0]<br>[1 2 3 0 0]</p></blockquote><p>Go 提供的语法糖<code>...</code>,可以将 slice 传进可变函数,不会创建新的切片。<br>第一次调用 change() 时,append() 操作使切片底层数组发生了扩容,原 slice 的底层数组不会改变;<br>第二次调用change() 函数时,使用了操作符[i,j]获得一个新的切片,假定为 slice1, 它的底层数组和原切片底层数组是重合的,不过 slice1 的长度、容量分别是 2、5,所以在 change() 函数中对 slice1 底层数组的修改会影响到原切片。</p><h2 id="Go每日一题-短赋值-循环"><a href="#Go每日一题-短赋值-循环" class="headerlink" title="Go每日一题 (短赋值 循环)"></a>Go每日一题 (短赋值 循环)</h2><p>下面这段代码输出结果正确吗?</p><figure class="highlight golang"><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="keyword">type</span> Foo <span class="keyword">struct</span> {</span><br><span class="line"> bar <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []Foo{</span><br><span class="line"> {<span class="string">"A"</span>},</span><br><span class="line"> {<span class="string">"B"</span>},</span><br><span class="line"> {<span class="string">"C"</span>},</span><br><span class="line"> }</span><br><span class="line"> s2 := <span class="built_in">make</span>([]*Foo, <span class="built_in">len</span>(s1))</span><br><span class="line"> <span class="keyword">for</span> i, value := <span class="keyword">range</span> s1 {</span><br><span class="line"> s2[i] = &value</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(s1[<span class="number">0</span>], s1[<span class="number">1</span>], s1[<span class="number">2</span>])</span><br><span class="line"> fmt.Println(s2[<span class="number">0</span>], s2[<span class="number">1</span>], s2[<span class="number">2</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>输出:<br>{A} {B} {C}<br>&{A} &{B} &{C}</p></blockquote><h3 id="答案解析-22"><a href="#答案解析-22" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案及解析:s2 的输出结果错误。<br>s2 的输出是 &{C} &{C} &{C},在前面题目我们提到过,for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。<br>所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2 输出的时候显示出了三个 &{c}。</p></blockquote><ul><li>可行的解决办法如下:</li></ul><figure class="highlight golang"><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> i := <span class="keyword">range</span> s1 {</span><br><span class="line"> s2[i] = &s1[i]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>从问题中发现问题, 从提问中提升自己</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="每日一题" scheme="http://blog.caoxl.com/tags/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
</entry>
<entry>
<title>Golang 每日一题 202301篇</title>
<link href="http://blog.caoxl.com/2023/02/23/Golang-Daily-Questions-1/"/>
<id>http://blog.caoxl.com/2023/02/23/Golang-Daily-Questions-1/</id>
<published>2023-02-23T02:16:05.000Z</published>
<updated>2023-02-27T08:25:30.769Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>书读百遍其义自见, 多看多问多巩固基础<br>下面是对<code>https://studygolang.com/</code>每日一题的合集; 如有侵权,请联系我删除!</p></blockquote><span id="more"></span><h2 id="Go每日一题-map-循环"><a href="#Go每日一题-map-循环" class="headerlink" title="Go每日一题 (map 循环)"></a>Go每日一题 (map 循环)</h2><p>下面代码里的 counter 的输出值?</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>{</span><br><span class="line"> <span class="string">"A"</span>: <span class="number">21</span>,</span><br><span class="line"> <span class="string">"B"</span>: <span class="number">22</span>,</span><br><span class="line"> <span class="string">"C"</span>: <span class="number">23</span>,</span><br><span class="line"> }</span><br><span class="line"> counter := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"> <span class="keyword">if</span> counter == <span class="number">0</span> {</span><br><span class="line"> <span class="built_in">delete</span>(m, <span class="string">"A"</span>)</span><br><span class="line"> }</span><br><span class="line"> counter++</span><br><span class="line"> fmt.Println(k, v)</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"counter is "</span>, counter)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. 2</li><li>B. 3</li><li>C. 2 或 3</li></ul><h3 id="答案解析"><a href="#答案解析" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:C。</p><p><code>for range map</code> 是无序的,如果第一次循环到 A,则输出 3;否则输出 2。</p><h2 id="Go每日一题-线程-协程"><a href="#Go每日一题-线程-协程" class="headerlink" title="Go每日一题 (线程 协程)"></a>Go每日一题 (线程 协程)</h2><p>关于协程,下面说法正确是()</p><ul><li>A. 协程和线程都可以实现程序的并发执行;</li><li>B. 线程比协程更轻量级;</li><li>C. 协程不存在死锁问题;</li><li>D. 通过 channel 来进行协程间的通信;</li></ul><h3 id="答案解析-1"><a href="#答案解析-1" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:AD。</p><h2 id="Go每日一题-for-循环"><a href="#Go每日一题-for-循环" class="headerlink" title="Go每日一题 (for 循环)"></a>Go每日一题 (for 循环)</h2><p>关于循环语句,下面说法正确的有()</p><ul><li>A. 循环语句既支持 for 关键字,也支持 while 和 do-while;</li><li>B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;</li><li>C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;</li><li>D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;</li></ul><h3 id="答案解析-2"><a href="#答案解析-2" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:CD。</p><h2 id="Go每日一题-多重赋值"><a href="#Go每日一题-多重赋值" class="headerlink" title="Go每日一题 (多重赋值)"></a>Go每日一题 (多重赋值)</h2><p>下面代码输出正确的是?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i := <span class="number">1</span></span><br><span class="line"> s := []<span class="type">string</span>{<span class="string">"A"</span>, <span class="string">"B"</span>, <span class="string">"C"</span>}</span><br><span class="line"> i, s[i<span class="number">-1</span>] = <span class="number">2</span>, <span class="string">"Z"</span></span><br><span class="line"> fmt.Printf(<span class="string">"s: %v \n"</span>, s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. s: [Z,B,C]</li><li>B. s: [A,Z,C]</li></ul><h3 id="答案解析-3"><a href="#答案解析-3" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:A。</p><p>知识点:多重赋值。</p><p>多重赋值分为两个步骤,有先后顺序:</p><ul><li>计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式;</li><li>赋值;</li></ul><p>所以本例,会先计算 s[i-1],等号右边是两个表达式是常量,所以赋值运算等同于 i, s[0] = 2, “Z”。</p><h2 id="Go每日一题-强制类型转化"><a href="#Go每日一题-强制类型转化" class="headerlink" title="Go每日一题 (强制类型转化)"></a>Go每日一题 (强制类型转化)</h2><p>关于类型转化,下面选项正确的是?</p><figure class="highlight golang"><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">A.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = i</span><br><span class="line"></span><br><span class="line">B.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = (MyInt)i</span><br><span class="line"></span><br><span class="line">C.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = MyInt(i)</span><br><span class="line"></span><br><span class="line">D.</span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> i <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> j MyInt = i.(MyInt)</span><br></pre></td></tr></table></figure><h3 id="答案解析-4"><a href="#答案解析-4" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:C。</p><p>知识点:强制类型转化。</p><h2 id="Go每日一题-switch"><a href="#Go每日一题-switch" class="headerlink" title="Go每日一题 (switch)"></a>Go每日一题 (switch)</h2><p>关于switch语句,下面说法正确的有?</p><ul><li>A. 条件表达式必须为常量或者整数;</li><li>B. 单个case中,可以出现多个结果选项;</li><li>C. 需要用break来明确退出一个case;</li><li>D. 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;</li></ul><h3 id="答案解析-5"><a href="#答案解析-5" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:BD。</p><h2 id="Go每日一题-类型断言-方法集"><a href="#Go每日一题-类型断言-方法集" class="headerlink" title="Go每日一题 (类型断言 方法集)"></a>Go每日一题 (类型断言 方法集)</h2><p>如果 Add() 函数的调用代码为:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a Integer = <span class="number">1</span></span><br><span class="line"> <span class="keyword">var</span> b Integer = <span class="number">2</span></span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">interface</span>{} = &a</span><br><span class="line"> sum := i.(*Integer).Add(b)</span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>则Add函数定义正确的是:</p><ul><li>A.</li></ul><figure class="highlight golang"><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="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a Integer)</span></span> Add(b Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>B.</li></ul><figure class="highlight golang"><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="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a Integer)</span></span> Add(b *Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> a + *b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>C.</li></ul><figure class="highlight golang"><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="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Integer)</span></span> Add(b Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> *a + b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>D.</li></ul><figure class="highlight golang"><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="keyword">type</span> Integer <span class="type">int</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Integer)</span></span> Add(b *Integer) Integer {</span><br><span class="line"> <span class="keyword">return</span> *a + *b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-6"><a href="#答案解析-6" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:AC。</p><p>知识点:类型断言、方法集。</p><blockquote><p>go 中有些的变量不可以寻址,指的是不能通过&获得其地址。<br>所以 func(*A) 只能接收 *A, func(A) 可以接收 A 或者 *A ,通过指针一定能得到变量的值 *A -> A<br>func(A) 可以接收 *A 和 A,func(*A) 只能接收 A,因为有些变量不可寻址(&获取地址)</p></blockquote><h2 id="Go每日一题-循环-defer"><a href="#Go每日一题-循环-defer" class="headerlink" title="Go每日一题 (循环 defer)"></a>Go每日一题 (循环 defer)</h2><p>下面这段代码输出什么,说明原因。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> slice := []<span class="type">int</span>{<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">int</span>]*<span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> key,val := <span class="keyword">range</span> slice {</span><br><span class="line"> m[key] = &val</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k,<span class="string">"->"</span>,*v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>扩展题目</li></ul><figure class="highlight golang"><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="keyword">type</span> Test <span class="keyword">struct</span> {</span><br><span class="line"> name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(this *Test)</span></span> Point(){</span><br><span class="line"> fmt.Println(this.name)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ts := []Test{</span><br><span class="line"> {<span class="string">"a"</span>},</span><br><span class="line"> {<span class="string">"b"</span>},</span><br><span class="line"> {<span class="string">"c"</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, t := <span class="keyword">range</span> ts {</span><br><span class="line"> <span class="comment">//fmt.Println(reflect.TypeOf(t))</span></span><br><span class="line"> <span class="keyword">defer</span> t.Point()</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-7"><a href="#答案解析-7" class="headerlink" title="答案解析"></a>答案解析</h3><figure class="highlight markdown"><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">0 -> 3</span><br><span class="line">1 -> 3</span><br><span class="line">2 -> 3</span><br><span class="line">3 -> 3</span><br></pre></td></tr></table></figure><p>解析:这是新手常会犯的错误写法,for range 循环的时候会创建每个元素的副本,而不是元素的引用,所以 m[key] = &val 取的都是变量 val 的地址,所以最后 map 中的所有元素的值都是变量 val 的地址,因为最后 val 被赋值为3,所有输出都是3.</p><p>正确的写法:</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> slice := []<span class="type">int</span>{<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">int</span>]*<span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> key,val := <span class="keyword">range</span> slice {</span><br><span class="line"> value := val</span><br><span class="line"> m[key] = &value</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> k,v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k,<span class="string">"===>"</span>,*v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-异常-defer"><a href="#Go每日一题-异常-defer" class="headerlink" title="Go每日一题 (异常 defer)"></a>Go每日一题 (异常 defer)</h2><p>下面这段代码输出的内容</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> defer_call()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">defer_call</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> { fmt.Println(<span class="string">"打印前"</span>) }()</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> { fmt.Println(<span class="string">"打印中"</span>) }()</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> { fmt.Println(<span class="string">"打印后"</span>) }()</span><br><span class="line"></span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"触发异常"</span>)</span><br><span class="line">} </span><br></pre></td></tr></table></figure><h3 id="答案解析-8"><a href="#答案解析-8" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>打印后<br>打印中<br>打印前<br>panic: 触发异常</p></blockquote><p>解析:defer 的执行顺序是<strong>后进先出</strong>。当出现 panic 语句的时候,会先按照 defer 的后进先出的顺序执行,最后才会执行panic。</p><h2 id="Go每日一题-map"><a href="#Go每日一题-map" class="headerlink" title="Go每日一题 (map)"></a>Go每日一题 (map)</h2><p>map 的 key 为什么是无序的?</p><p>在遍历 map 的时候,我们会发现,输出的 key 是无序的。为什么?</p><h3 id="答案解析-9"><a href="#答案解析-9" class="headerlink" title="答案解析"></a>答案解析</h3><p>map 在扩容后,会发生 key 的搬迁,原来落在同一个 bucket 中的 key,搬迁后,有些 key 就要远走高飞了(bucket 序号加上了 2^B)。<br>而遍历的过程,就是按顺序遍历 bucket,同时按顺序遍历 bucket 中的 key。搬迁后,key 的位置发生了重大的变化,有些 key 飞上高枝,有些 key 则原地不动。<br>这样,遍历 map 的结果就不可能按原来的顺序了。</p><p>当然,如果我就一个 hard code 的 map,我也不会向 map 进行插入删除的操作,按理说每次遍历这样的 map 都会返回一个固定顺序的 key/value 序列吧。的确是这样,但是 Go 杜绝了这种做法,因为这样会给新手程序员带来误解,以为这是一定会发生的事情,在某些情况下,可能会酿成大错。</p><p>当然,Go 做得更绝,当我们在遍历 map 时,并不是固定地从 0 号 bucket 开始遍历,每次都是从一个随机值序号的 bucket 开始遍历,并且是从这个 bucket 的一个随机序号的 cell 开始遍历。这样,即使你是一个写死的 map,仅仅只是遍历它,也不太可能会返回一个固定序列的 key/value 对了。</p><p>多说一句,“迭代 map 的结果是无序的”这个特性是从 go 1.0 开始加入的。</p><h2 id="Go每日一题-slice-append"><a href="#Go每日一题-slice-append" class="headerlink" title="Go每日一题 (slice append)"></a>Go每日一题 (slice append)</h2><p>下面两段代码输出什么。</p><figure class="highlight golang"><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="comment">// 1.</span></span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := <span class="built_in">make</span>([]<span class="type">int</span>, <span class="number">5</span>)</span><br><span class="line"> s = <span class="built_in">append</span>(s, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"> fmt.Println(s)</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 2.</span></span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := <span class="built_in">make</span>([]<span class="type">int</span>,<span class="number">0</span>)</span><br><span class="line"> s = <span class="built_in">append</span>(s,<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><br></pre></td></tr></table></figure><h3 id="答案解析-10"><a href="#答案解析-10" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>代码 1 输出:[0 0 0 0 0 1 2 3]<br>代码 2 输出:[1 2 3 4]</p></blockquote><p>参考解析:这道题考的是使用 <code>append</code> 向 <code>slice</code> 添加元素,第一段代码常见的错误是 [1 2 3],需要注意。</p><h2 id="Go每日一题-map-线程安全"><a href="#Go每日一题-map-线程安全" class="headerlink" title="Go每日一题 (map 线程安全)"></a>Go每日一题 (map 线程安全)</h2><p>Go 的 map 可以边遍历边删除吗?</p><h3 id="答案解析-11"><a href="#答案解析-11" class="headerlink" title="答案解析"></a>答案解析</h3><p>map 并不是一个线程安全的数据结构。同时读写一个 map 是未定义的行为,如果被检测到,会直接 <code>panic</code>。</p><p>上面说的是发生在多个协程同时读写同一个 map 的情况下。 如果在同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以这样做的。但是,遍历的结果就可能不会是相同的了,有可能结果遍历结果集中包含了删除的 key,也有可能不包含,这取决于删除 key 的时间:是在遍历到 key 所在的 bucket 时刻前或者后。</p><p>一般而言,这可以通过读写锁来解决:<code>sync.RWMutex</code>。</p><p>读之前调用 <code>RLock()</code> 函数,读完之后调用 <code>RUnlock()</code> 函数解锁;写之前调用 <code>Lock()</code> 函数,写完之后,调用 <code>Unlock() 解锁。</code></p><p>另外,<code>sync.Map</code> 是线程安全的 map,也可以使用。</p><h2 id="Go每日一题-func-多返回值"><a href="#Go每日一题-func-多返回值" class="headerlink" title="Go每日一题 (func 多返回值)"></a>Go每日一题 (func 多返回值)</h2><p>下面这段代码有什么缺陷:</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">sum</span><span class="params">(x, y <span class="type">int</span>)</span></span>(total <span class="type">int</span>, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> x+y, <span class="literal">nil</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-12"><a href="#答案解析-12" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>第二个返回值没有命名。</p></blockquote><p>在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。<br>如果有多个返回值必须加上括号<code>()</code>;如果只有一个返回值且命名也必须加上括号<code>()</code>。<br>这里的第一个返回值有命名 total,第二个没有命名,所以错误。</p><h2 id="Go每日一题-map-取地址"><a href="#Go每日一题-map-取地址" class="headerlink" title="Go每日一题 (map 取地址)"></a>Go每日一题 (map 取地址)</h2><p>以下代码是否能编译通过?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"> fmt.Println(&m[<span class="string">"qcrao"</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-13"><a href="#答案解析-13" class="headerlink" title="答案解析"></a>答案解析</h3><p>这个问题,相当于问:可以对 map 的元素直接取地址吗?</p><p>以上代码编译报错:</p><blockquote><p>./main.go:8:15: invalid operation: cannot take address of m[“qcrao”] (map index expression of type int)</p></blockquote><p>即无法对 <code>map</code> 的 <code>key</code> 或 <code>value</code> 进行取址。</p><p>如果通过其他 hack 的方式,例如 <code>unsafe.Pointer</code> 等获取到了 key 或 value 的地址,也不能长期持有,因为一旦发生扩容,key 和 value 的位置就会改变,之前保存的地址也就失效了。</p><h2 id="Go每日一题-map-相等判断"><a href="#Go每日一题-map-相等判断" class="headerlink" title="Go每日一题 (map 相等判断)"></a>Go每日一题 (map 相等判断)</h2><p>如何确认两个 map 是否相等?</p><h3 id="答案解析-14"><a href="#答案解析-14" class="headerlink" title="答案解析"></a>答案解析</h3><p>map 深度相等的条件:</p><ul><li>都为 nil</li><li>非空、长度相等,指向同一个 map 实体对象</li><li>相应的 key 指向的 value “深度”相等</li></ul><p>直接将使用 <code>map1 == map2</code> 是错误的。这种写法只能比较 map 是否为 nil。</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span></span><br><span class="line"> <span class="keyword">var</span> n <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(m == <span class="literal">nil</span>)</span><br><span class="line"> fmt.Println(n == <span class="literal">nil</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 不能通过编译</span></span><br><span class="line"> <span class="comment">//fmt.Println(m == n)</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出结果:</p><blockquote><p>true<br>true</p></blockquote><p>因此只能是遍历 map 的每个元素,比较元素是否都是深度相等。</p><h2 id="Go每日一题-struct"><a href="#Go每日一题-struct" class="headerlink" title="Go每日一题 (struct)"></a>Go每日一题 (struct)</h2><p>空 struct{} 占多少空间?有什么用途?</p><h3 id="答案解析-15"><a href="#答案解析-15" class="headerlink" title="答案解析"></a>答案解析</h3><p>使用空结构体 <code>struct{}</code> 可以节省内存,一般作为占位符使用,表明这里并不需要一个值。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fmt.Println(unsafe.Sizeof(<span class="keyword">struct</span>{}{})) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><p>比如使用 map 表示集合时,只关注 key,value 可以使用 <code>struct{}</code> 作为占位符。如果使用其他类型作为占位符,例如 int,bool,不仅浪费了内存,而且容易引起歧义。</p><figure class="highlight golang"><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="keyword">type</span> Set <span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> set := <span class="built_in">make</span>(Set)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, item := <span class="keyword">range</span> []<span class="type">string</span>{<span class="string">"A"</span>, <span class="string">"A"</span>, <span class="string">"B"</span>, <span class="string">"C"</span>} {</span><br><span class="line"> set[item] = <span class="keyword">struct</span>{}{}</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(set)) <span class="comment">// 3</span></span><br><span class="line"> <span class="keyword">if</span> _, ok := set[<span class="string">"A"</span>]; ok {</span><br><span class="line"> fmt.Println(<span class="string">"A exists"</span>) <span class="comment">// A exists</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>再比如,使用信道(channel)控制并发时,我们只是需要一个信号,但并不需要传递值,这个时候,也可以使用 <code>struct{}</code> 代替。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>{}, <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <-ch</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> }()</span><br><span class="line"> ch <- <span class="keyword">struct</span>{}{}</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>再比如,声明只包含方法的结构体。</p><figure class="highlight golang"><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">type</span> Lamp <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(l Lamp)</span></span> On() {</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"On"</span>)</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(l Lamp)</span></span> Off() {</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"Off"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Go每日一题-init"><a href="#Go每日一题-init" class="headerlink" title="Go每日一题 (init)"></a>Go每日一题 (init)</h2><p><code>init()</code> 函数是什么时候执行的?</p><h3 id="答案解析-16"><a href="#答案解析-16" class="headerlink" title="答案解析"></a>答案解析</h3><p><code>init()</code> 函数是 Go 程序初始化的一部分。<br>Go 程序初始化先于 main 函数,由 runtime 初始化每个导入的包,初始化顺序不是按照从上到下的导入顺序,而是按照解析的依赖关系,没有依赖的包最先初始化。</p><p>每个包首先初始化包作用域的常量和变量(常量优先于变量),然后执行包的 <code>init()</code> 函数。<br>同一个包,甚至是同一个源文件可以有多个 <code>init()</code> 函数。<code>init()</code> 函数没有入参和返回值,不能被其他函数调用,同一个包内多个 <code>init()</code> 函数的执行顺序不作保证。</p><blockquote><p>一句话总结: <code>import</code> –> <code>const</code> –> <code>var</code> –> <code>init()</code> –> <code>main()</code></p></blockquote><ul><li>示例:</li></ul><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"init1:"</span>, a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"init2:"</span>, a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">10</span></span><br><span class="line"><span class="keyword">const</span> b = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"main:"</span>, a)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 执行结果</span></span><br><span class="line"><span class="comment">// init1: 10</span></span><br><span class="line"><span class="comment">// init2: 10</span></span><br><span class="line"><span class="comment">// main: 10</span></span><br></pre></td></tr></table></figure><h2 id="Go每日一题-new-make"><a href="#Go每日一题-new-make" class="headerlink" title="Go每日一题 (new make)"></a>Go每日一题 (new make)</h2><p><code>new()</code> 与 <code>make()</code> 的区别</p><h3 id="答案解析-17"><a href="#答案解析-17" class="headerlink" title="答案解析"></a>答案解析</h3><p><code>new(T)</code> 和 <code>make(T,args)</code> 是 Go 语言内建函数,用来分配内存,但适用的类型不同。</p><p><code>new(T)</code> 会为 T 类型的新值分配已置零的内存空间,并返回地址(指针),即类型为 *T 的值。<br>换句话说就是,返回一个指针,该指针指向新分配的、类型为 T 的零值。<br><code>new()</code> 适用于值类型,如数组、结构体等。</p><p><code>make(T,args)</code> 返回初始化之后的 T 类型的值,这个值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。<br><code>make()</code> 只适用于 slice、map 和 channel。</p><h2 id="Go每日一题-new-make-1"><a href="#Go每日一题-new-make-1" class="headerlink" title="Go每日一题 (new make)"></a>Go每日一题 (new make)</h2><p>下面这段代码能否通过编译,不能的话原因是什么;如果通过,输出什么。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> list := <span class="built_in">new</span>([]<span class="type">int</span>)</span><br><span class="line"> list = <span class="built_in">append</span>(list, <span class="number">1</span>)</span><br><span class="line"> fmt.Println(list)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-18"><a href="#答案解析-18" class="headerlink" title="答案解析"></a>答案解析</h3><p>不能通过编译,<code>new([]int)</code> 之后的 list 是一个 <code>*[]int</code> 类型的指针,不能对指针执行 <code>append</code> 操作。可以使用 <code>make()</code> 初始化之后再用。<br>同样的,map 和 channel 建议使用 make() 或字面量的方式初始化,不要用 <code>new()</code> 。</p><h2 id="Go每日一题-slice-append-1"><a href="#Go每日一题-slice-append-1" class="headerlink" title="Go每日一题 (slice append)"></a>Go每日一题 (slice append)</h2><p>下面这段代码能否通过编译,如果可以,输出什么?</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> s2 := []<span class="type">int</span>{<span class="number">4</span>, <span class="number">5</span>}</span><br><span class="line"> s1 = <span class="built_in">append</span>(s1, s2)</span><br><span class="line"> fmt.Println(s1)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-19"><a href="#答案解析-19" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>不能通过编译。<br>append() 的第二个参数不能直接使用 slice,需使用 … 操作符,<br>将一个切片追加到另一个切片上:append(s1,s2…)。或者直接跟上元素,形如:append(s1,1,2,3)。</p></blockquote><h2 id="Go每日一题-bit"><a href="#Go每日一题-bit" class="headerlink" title="Go每日一题 (bit)"></a>Go每日一题 (bit)</h2><p>以下代码输出什么?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a, b <span class="type">float64</span> = <span class="number">1.0</span>, <span class="number">4.0</span></span><br><span class="line"> fmt.Println(a | b)</span><br><span class="line">} </span><br></pre></td></tr></table></figure><ul><li>A:5</li><li>B:+Inf</li><li>C:panic</li><li>D:不能编译</li></ul><h3 id="答案解析-20"><a href="#答案解析-20" class="headerlink" title="答案解析"></a>答案解析</h3><p>正确答案:D</p><blockquote><p><code>|</code> 操作是按位或操作符,它的操作数只能是整数,而上面这道题的操作数是 float64,因此编译不通过。</p></blockquote><h2 id="Go每日一题-接口-内存分配"><a href="#Go每日一题-接口-内存分配" class="headerlink" title="Go每日一题 (接口 内存分配)"></a>Go每日一题 (接口 内存分配)</h2><p>Go 1.15 中 var i interface{} = a 会有额外堆内存分配吗?</p><p>具体代码是:</p><figure class="highlight golang"><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">var</span> a <span class="type">int</span> = <span class="number">3</span></span><br><span class="line"><span class="comment">// 以下有额外内存分配吗?</span></span><br><span class="line"><span class="keyword">var</span> i <span class="keyword">interface</span>{} = a</span><br></pre></td></tr></table></figure><h3 id="答案解析-21"><a href="#答案解析-21" class="headerlink" title="答案解析"></a>答案解析</h3><p>在 Go 中,接口被实现为一对指针</p><figure class="highlight golang"><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="keyword">type</span> iface <span class="keyword">struct</span> {</span><br><span class="line"> tab *itab</span><br><span class="line"> data unsafe.Pointer</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其中 tab 是指向类型信息的指针;data 是指向值的指针。因此,一般来说接口意味着必须在堆中动态分配该值。</p><p>然而,<strong>Go 1.15 发行说明</strong>在 runtime 部分中提到了一个有趣的改进:</p><blockquote><p>Converting a small integer value into an interface value no longer causes allocation.</p></blockquote><p>意思是说,<strong>将小整数转换为接口值不再需要进行内存分配</strong>。<strong>小整数是指 0 到 255 之间的数。</strong></p><h2 id="Go每日一题-常量-len-位移运算"><a href="#Go每日一题-常量-len-位移运算" class="headerlink" title="Go每日一题 (常量 len 位移运算)"></a>Go每日一题 (常量 len 位移运算)</h2><p>以下程序输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> s = <span class="string">"Go101.org"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// len(s) == 9</span></span><br><span class="line"><span class="comment">// 1 << 9 == 512</span></span><br><span class="line"><span class="comment">// 512 / 128 == 4</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a <span class="type">byte</span> = <span class="number">1</span> << <span class="built_in">len</span>(s) / <span class="number">128</span></span><br><span class="line"><span class="keyword">var</span> b <span class="type">byte</span> = <span class="number">1</span> << <span class="built_in">len</span>(s[:]) / <span class="number">128</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">println</span>(a, b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A: 0 0</li><li>B: 0 4</li><li>C: 4 0</li><li>D: 4 4</li></ul><h3 id="答案解析-22"><a href="#答案解析-22" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>4 0(即选 C)</p></blockquote><p>len 是一个内置函数。在官方标准库文档关于 len 函数有这么一句:</p><blockquote><p>For some arguments, such as a string literal or a simple array expression, the result can be a constant. See the Go language specification’s “Length and capacity” section for details.</p></blockquote><p>明确支持,当参数是字符串字面量和简单 array 表达式,len 函数返回值是常量,这很重要。</p><h2 id="Go每日一题-结构体比较"><a href="#Go每日一题-结构体比较" class="headerlink" title="Go每日一题 (结构体比较)"></a>Go每日一题 (结构体比较)</h2><p>下面代码是否可以编译通过?为什么?</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> sn1 := <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line"> name <span class="type">string</span></span><br><span class="line"> }{</span><br><span class="line"> age: <span class="number">11</span>, name: <span class="string">"qq"</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> sn2 := <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line"> name <span class="type">string</span></span><br><span class="line"> }{</span><br><span class="line"> age: <span class="number">11</span>, name: <span class="string">"qq"</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> sn1 == sn2 {</span><br><span class="line"> fmt.Println(<span class="string">"sn1 == sn2"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> sm1 := <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line"> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span></span><br><span class="line"> }{</span><br><span class="line"> age: <span class="number">11</span>, m: <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"a"</span>: <span class="string">"1"</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> sm2 := <span class="keyword">struct</span> {</span><br><span class="line"> age <span class="type">int</span></span><br><span class="line"> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span></span><br><span class="line"> }{</span><br><span class="line"> age: <span class="number">11</span>, m: <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"a"</span>: <span class="string">"1"</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> sm1 == sm2 {</span><br><span class="line"> fmt.Println(<span class="string">"sm1 == sm2"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-23"><a href="#答案解析-23" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>编译不通过。<br>./main.go:39:5: invalid operation: sm1 == sm2 (struct containing map[string]string cannot be compared)</p></blockquote><p>只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。</p><h2 id="Go每日一题-常量"><a href="#Go每日一题-常量" class="headerlink" title="Go每日一题 (常量)"></a>Go每日一题 (常量)</h2><p>下面代码有什么问题?</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cl = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bl = <span class="number">123</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">println</span>(&bl, bl)</span><br><span class="line"> <span class="built_in">println</span>(&cl, cl)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-24"><a href="#答案解析-24" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>./main.go:9:11: invalid operation: cannot take address of cl (untyped int constant 100)</p></blockquote><p>常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,</p><p>在golang中,常量是无法取出地址的,因为字面量符号并没有地址而言。</p><h2 id="Go每日一题-slice-初始化-append"><a href="#Go每日一题-slice-初始化-append" class="headerlink" title="Go每日一题 (slice 初始化 append)"></a>Go每日一题 (slice 初始化 append)</h2><p>下面这段代码能否通过编译,不能的话原因是什么;如果通过,输出什么。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> list := <span class="built_in">new</span>([]<span class="type">int</span>)</span><br><span class="line"> list = <span class="built_in">append</span>(list, <span class="number">1</span>)</span><br><span class="line"> fmt.Println(list)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-25"><a href="#答案解析-25" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>不能通过编译<br>new([]int) 之后的 list 是一个 *[]int 类型的指针,不能对指针执行 append 操作。可以使用 make() 初始化之后再用。<br>同样的,map 和 channel 建议使用 make() 或字面量的方式初始化,不要用 new() 。</p></blockquote><h2 id="Go每日一题-slice-append-2"><a href="#Go每日一题-slice-append-2" class="headerlink" title="Go每日一题 (slice append)"></a>Go每日一题 (slice append)</h2><p>写出程序运行的结果:</p><figure class="highlight golang"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> s := <span class="built_in">make</span>([]<span class="type">int</span>, <span class="number">10</span>)</span><br><span class="line"> s = <span class="built_in">append</span>(s, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"> fmt.Println(s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-26"><a href="#答案解析-26" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>[0 0 0 0 0 0 0 0 0 0 1 2 3]<br>切片追加, make 初始化均为 0</p></blockquote><h2 id="Go每日一题-变量声明"><a href="#Go每日一题-变量声明" class="headerlink" title="Go每日一题 (变量声明)"></a>Go每日一题 (变量声明)</h2><p>下面这段代码能否通过编译,如果可以,输出什么?</p><figure class="highlight golang"><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="keyword">var</span>(</span><br><span class="line"> size := <span class="number">1024</span></span><br><span class="line"> max_size = size*<span class="number">2</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(size,max_size)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-27"><a href="#答案解析-27" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>不能通过编译。<br>这道题的主要知识点是变量声明的简短模式,形如:x := 100. 但这种声明方式有限制:</p><ul><li>必须使用显示初始化;</li><li>不能提供数据类型,编译器会自动推导;</li><li>只能在函数内部使用简短模式;</li></ul></blockquote><h2 id="Go每日一题-结构体指针使用"><a href="#Go每日一题-结构体指针使用" class="headerlink" title="Go每日一题 (结构体指针使用)"></a>Go每日一题 (结构体指针使用)</h2><p>通过指针变量 p 访问其成员变量 name,有哪几种方式?(多选)</p><ul><li>A. p.name</li><li>B. (&p).name</li><li>C. (*p).name</li><li>D. p->name</li></ul><h3 id="答案解析-28"><a href="#答案解析-28" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>答案:AC<br>解析:<code>&</code> 取址运算符,<code>*</code> 指针解引用。</p></blockquote><h2 id="Go每日一题-类型别名"><a href="#Go每日一题-类型别名" class="headerlink" title="Go每日一题 (类型别名)"></a>Go每日一题 (类型别名)</h2><p>下面这段代码能否通过编译?如果通过,输出什么?</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyInt1 <span class="type">int</span></span><br><span class="line"><span class="keyword">type</span> MyInt2 = <span class="type">int</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="type">int</span> =<span class="number">0</span></span><br><span class="line"> <span class="keyword">var</span> i1 MyInt1 = i </span><br><span class="line"> <span class="keyword">var</span> i2 MyInt2 = i</span><br><span class="line"> fmt.Println(i1,i2)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="答案解析-29"><a href="#答案解析-29" class="headerlink" title="答案解析"></a>答案解析</h3><blockquote><p>编译不通过,cannot use i (type int) as type MyInt1 in assignment</p></blockquote><h2 id="Go每日一题-数组比较"><a href="#Go每日一题-数组比较" class="headerlink" title="Go每日一题 (数组比较)"></a>Go每日一题 (数组比较)</h2><p>下面这段代码输出什么?</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := [<span class="number">2</span>]<span class="type">int</span>{<span class="number">5</span>, <span class="number">6</span>}</span><br><span class="line"> b := [<span class="number">3</span>]<span class="type">int</span>{<span class="number">5</span>, <span class="number">6</span>}</span><br><span class="line"> <span class="keyword">if</span> a == b {</span><br><span class="line"> fmt.Println(<span class="string">"equal"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"not equal"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>A. compilation error</li><li>B. equal</li><li>C. not equal</li></ul><h3 id="答案解析-30"><a href="#答案解析-30" class="headerlink" title="答案解析"></a>答案解析</h3><p>参考答案及解析:A。</p><blockquote><p>./main.go:8:10: invalid operation: a == b (mismatched types [2]int and [3]int)</p></blockquote><p>Go 中的数组是值类型,可比较,另外一方面,数组的长度也是数组类型的组成部分,所以 a 和 b 是不同的类型,是不能比较的,所以编译错误。</p>]]></content>
<summary type="html">
<blockquote>
<p>书读百遍其义自见, 多看多问多巩固基础<br>下面是对<code>https://studygolang.com/</code>每日一题的合集; 如有侵权,请联系我删除!</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
<category term="每日一题" scheme="http://blog.caoxl.com/tags/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
</entry>
<entry>
<title>Go 单元测试</title>
<link href="http://blog.caoxl.com/2022/10/26/Go-Testing/"/>
<id>http://blog.caoxl.com/2022/10/26/Go-Testing/</id>
<published>2022-10-26T09:38:16.000Z</published>
<updated>2022-10-26T09:55:09.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>本文讲Go的几种单元测试</p></blockquote><span id="more"></span><h1 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h1><ul><li><a href="https://pkg.go.dev/testing">单元测试 官方</a></li><li><a href="http://cngolib.com/testing.html">单元测试 中文</a></li></ul><p>Go 官方的 <code>testing</code> 包</p><blockquote><p>要编写一个测试文件,需要创建一个名称以 <code>_test.go</code> 结尾的文件,该文件包含 <code>TestXxx</code> 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 <code>go test</code> 命令时将被包含</p></blockquote><p><img src="https://uploader.shimo.im/f/vJj1CBKfjxtchaaF.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="单元测试"></p><ul><li>运行结果</li></ul><p><img src="https://uploader.shimo.im/f/7nYodXNsPGy2iiZH.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="单元测试-运行结果"></p><p>这里介绍几个常用的参数:</p><ul><li><code>-bench regexp</code>: 执行相应的 <code>benchmarks</code>,例如 <code>-bench=</code> (基准测试)</li><li><code>-cover</code>: 开启测试覆盖率</li><li><code>-run regexp</code>: 只运行 <code>regexp</code> 匹配的函数,例如 <code>-run=Array</code> 那么就执行包含有 Array 开头的函数;</li><li><code>-v</code> 显示测试的详细命令</li></ul><h1 id="基准测试"><a href="#基准测试" class="headerlink" title="基准测试"></a>基准测试</h1><blockquote><p><code>benchmark</code> 和普通的单元测试用例一样,都位于 <code>_test.go</code> 文件中。<br>函数名以 <code>Benchmark</code> 开头,参数是 <code>b *testing.B</code>。</p></blockquote><h2 id="运行用例"><a href="#运行用例" class="headerlink" title="运行用例"></a>运行用例</h2><blockquote><p><code>go test <module name>/<package name></code> 用来运行某个 package 内的所有测试用例。</p></blockquote><ul><li>运行当前 <code>package</code> 内的用例:<code>go test example</code> 或 <code>go test .</code></li><li>运行子 <code>package</code> 内的用例: <code>go test example/<package name></code> 或 <code>go test ./<package name></code></li><li>如果想递归测试当前目录下的所有的 <code>package:go test ./...</code> 或 <code>go test example/...</code></li></ul><p><img src="https://uploader.shimo.im/f/i4NUicQyrcsFh8gq.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="基准测试"></p><h2 id="查看内存分配"><a href="#查看内存分配" class="headerlink" title="查看内存分配"></a>查看内存分配</h2><p><img src="https://uploader.shimo.im/f/uKUNYxbKlcQEXpx7.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="代码示例"></p><p><img src="https://uploader.shimo.im/f/du3Qu0Rxao4KAEPE.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="运行结果"></p><p>可以看到生成 <code>100w</code> 个数字的随机序列,<code>GenerateWithCap</code> 的耗时比 <code>Generate</code> 少 <strong>20%</strong></p><ul><li>使用 <code>-benchmem</code> 参数看到内存分配的情况:</li></ul><p><img src="https://uploader.shimo.im/f/M0Q2d18BKHuHcqAk.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="内存分配"></p><p><code>Generate</code> 分配的内存是 <code>GenerateWithCap</code> 的 5 倍,设置了切片容量,内存只分配一次,而不设置切片容量,内存分配了 <code>38</code> 次。</p><h1 id="模糊测试"><a href="#模糊测试" class="headerlink" title="模糊测试"></a>模糊测试</h1><blockquote><p>Go模糊测试会调用<code>f.Add</code>函数和<code>f.Fuzz</code>函数</p></blockquote><ul><li><code>f.Add</code>函数把指定输入作为模糊测试的种子语料库(seed corpus),<code>fuzzing</code>基于种子语料库生成随机输入。</li><li><code>f.Fuzz</code>函数接收一个<code>fuzz target</code>函数作为入参。<code>fuzz target</code>函数有多个参数,第一个参数是<code>*testing.T</code>,其它参数是被模糊的类型</li></ul><p><img src="https://uploader.shimo.im/f/iMZr0tJYzbvCNdwY.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="代码示例"></p><p><img src="https://uploader.shimo.im/f/H2IQdDkPd9jw8MmR.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="运行结果"></p><p>上面的<code>fuzzing</code>测试结果是<strong>FAIL</strong>,引起<strong>FAIL</strong>的输入数据被写到了一个语料库文件里。下次运行<code>go test</code>命令的时候,即使没有<code>-fuzz</code>参数,这个语料库文件里的测试数据也会被用到。</p><p>可以用文本编辑器打开<code>testdata/fuzz/FuzzReverse</code>目录下的文件,看看引起<code>Fuzzing</code>测试失败的测试数据长什么样</p><p><img src="https://uploader.shimo.im/f/IVrdm4zqf4fiRwIg.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="测试数据"></p><p>发现问题,试着修改BUG</p><p><img src="https://uploader.shimo.im/f/PNITx1KLEGOBkDEh.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="修改BUG"></p><p>在Go语言里,<strong>字符串是只读的字节切片</strong>(<code>In Go, a string is a read only slice of bytes</code>),字节切片里的每个字节不一定都是有效的UTF-8编码的字节,详情可以参考<a href="https://link.segmentfault.com/?enc=B3kcESuUAVJF4D0X44hUMw==.bmCkSvG4A+68HkD28A2qI0BTpCMCKz9E7D+HxvJagOc=">a string is a read only slice of bytes</a></p><p><img src="https://uploader.shimo.im/f/8fQPVLYqpXx240sP.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="模糊测试"></p><p><img src="https://uploader.shimo.im/f/vloFe9tjF3vt9Cnm.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NjY3NzczMTMsImZpbGVHVUlEIjoiWEtxNE1WckdvYWZ5MVFrTiIsImlhdCI6MTY2Njc3NzAxMywiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjoxMzg4MjAwNH0.S-n43ThSgOm-egKKVqiZMWtlKPWrXi7quWv4AzKTBOg" alt="优化代码后-模糊测试"></p><p>运行模糊测试 <code>go test -fuzz=Fuzz</code>,执行几秒后,使用 <code>ctrl-C</code> 结束测试。</p>]]></content>
<summary type="html">
<blockquote>
<p>本文讲Go的几种单元测试</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="单元测试" scheme="http://blog.caoxl.com/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/"/>
<category term="Golang" scheme="http://blog.caoxl.com/tags/Golang/"/>
<category term="基准测试" scheme="http://blog.caoxl.com/tags/%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95/"/>
<category term="模糊测试" scheme="http://blog.caoxl.com/tags/%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95/"/>
</entry>
<entry>
<title>Go RabbitMQ 简单示例</title>
<link href="http://blog.caoxl.com/2022/10/26/Golang-RabbitMQ/"/>
<id>http://blog.caoxl.com/2022/10/26/Golang-RabbitMQ/</id>
<published>2022-10-26T08:57:29.000Z</published>
<updated>2022-10-26T09:03:32.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>本文仅分享RabbitMQ在go的简单示例, 具体项目需要更优化的封装</p></blockquote><span id="more"></span><h1 id="创建RabbitMQ示例"><a href="#创建RabbitMQ示例" class="headerlink" title="创建RabbitMQ示例"></a>创建RabbitMQ示例</h1><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> RabbitMQ</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"fmt"</span></span><br><span class="line"><span class="string">"github.com/streadway/amqp"</span></span><br><span class="line"><span class="string">"log"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// MQUrl 格式 amqp://账号:密码@rabbitmq服务器地址:端口号/vhost</span></span><br><span class="line"><span class="keyword">const</span> MQUrl = <span class="string">"amqp://go-test:123456@127.0.0.1:5672/gotest"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> RabbitMQ <span class="keyword">struct</span> {</span><br><span class="line">conn *amqp.Connection</span><br><span class="line">channel *amqp.Channel</span><br><span class="line">QueueName <span class="type">string</span> <span class="comment">// 队列名称</span></span><br><span class="line">Exchange <span class="type">string</span> <span class="comment">// 交换机</span></span><br><span class="line">Key <span class="type">string</span> <span class="comment">// Key</span></span><br><span class="line">MQUrl <span class="type">string</span> <span class="comment">// 连接信息</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// NewRabbitMQ 创建结构体实例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewRabbitMQ</span><span class="params">(queueName, exchange, key <span class="type">string</span>)</span></span> *RabbitMQ {</span><br><span class="line">rabbitmq := &RabbitMQ{</span><br><span class="line">QueueName: queueName,</span><br><span class="line">Exchange: exchange,</span><br><span class="line">Key: key,</span><br><span class="line">MQUrl: MQUrl,</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line">rabbitmq.conn, err = amqp.Dial(rabbitmq.MQUrl)</span><br><span class="line">rabbitmq.failOnErr(err, <span class="string">"创建连接错误!"</span>)</span><br><span class="line"></span><br><span class="line">rabbitmq.channel, err = rabbitmq.conn.Channel()</span><br><span class="line">rabbitmq.failOnErr(err, <span class="string">"获取channel失败!"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> rabbitmq</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Destroy 断开channel和connection</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *RabbitMQ)</span></span> Destroy() {</span><br><span class="line">_ = r.channel.Close()</span><br><span class="line">_ = r.conn.Close()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// failOnErr 错误处理函数</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *RabbitMQ)</span></span> failOnErr(err <span class="type">error</span>, message <span class="type">string</span>) {</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"><span class="comment">//log.Fatalf("%s:%s", message, err)</span></span><br><span class="line"><span class="built_in">panic</span>(fmt.Sprintf(<span class="string">"%s:%s"</span>, message, err))</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// NewRabbitMQSimple 创建简单模式下的RabbitMQ实例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewRabbitMQSimple</span><span class="params">(queueName <span class="type">string</span>)</span></span> *RabbitMQ {</span><br><span class="line"><span class="keyword">return</span> NewRabbitMQ(queueName, <span class="string">""</span>, <span class="string">""</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// PublishSimple 简单模式下生产消息</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *RabbitMQ)</span></span> PublishSimple(message <span class="type">string</span>) {</span><br><span class="line"><span class="comment">// 1. 申请队列, 如果队列不存在会自动创建, 如果存在则跳过创建</span></span><br><span class="line"><span class="comment">// 保证队列存在, 消息能发送到队列中</span></span><br><span class="line">_, err := r.channel.QueueDeclare(</span><br><span class="line">r.QueueName,</span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否持久化</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否为自动删除</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否具有排他性</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否阻塞</span></span><br><span class="line"><span class="literal">nil</span>, <span class="comment">// 额外属性</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line">log.Fatalf(<span class="string">"%s:%s"</span>, <span class="string">"申请队列失败"</span>, err.Error())</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 发送消息到队列中</span></span><br><span class="line">_ = r.channel.Publish(</span><br><span class="line">r.Exchange,</span><br><span class="line">r.QueueName,</span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 如果为true, 会根据exchange类型和routekey规则,如果无法找到符合条件的队列那么会把发送的消息返回给发送者</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 如果为true, 当exchange发送消息到队列后发现队列上没有绑定消费者,则会把消息发还给发送者</span></span><br><span class="line">amqp.Publishing{</span><br><span class="line">ContentType: <span class="string">"text/plain"</span>,</span><br><span class="line">Body: []<span class="type">byte</span>(message),</span><br><span class="line">})</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ConsumeSimple 简单模式下消费消息</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *RabbitMQ)</span></span> ConsumeSimple() {</span><br><span class="line"><span class="comment">// 1. 申请队列, 如果队列不存在会自动创建, 如果存在则跳过创建</span></span><br><span class="line"><span class="comment">// 保证队列存在, 消息能发送到队列中</span></span><br><span class="line">_, err := r.channel.QueueDeclare(</span><br><span class="line">r.QueueName,</span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否持久化</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否为自动删除</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否具有排他性</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否阻塞</span></span><br><span class="line"><span class="literal">nil</span>, <span class="comment">// 额外属性</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line">log.Fatalf(<span class="string">"%s:%s"</span>, <span class="string">"申请队列失败"</span>, err.Error())</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 接收消息</span></span><br><span class="line">msgs, err := r.channel.Consume(</span><br><span class="line">r.QueueName,</span><br><span class="line"><span class="string">""</span>, <span class="comment">// 用来区分多个消费者</span></span><br><span class="line"><span class="literal">true</span>, <span class="comment">// 是否自动应答</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 是否具有排他性</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 如果设置为true,表示不能将同一个connection中发送的消息传递给这个connection中的消费者</span></span><br><span class="line"><span class="literal">false</span>, <span class="comment">// 队列消费是否阻塞</span></span><br><span class="line"><span class="literal">nil</span>,</span><br><span class="line">)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line">log.Fatalf(<span class="string">"%s:%s"</span>, <span class="string">"申请队列失败"</span>, err.Error())</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">forever := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">bool</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">for</span> msg := <span class="keyword">range</span> msgs {</span><br><span class="line">log.Printf(<span class="string">"Received a message: %s"</span>, msg.Body)</span><br><span class="line">fmt.Println(msg.Body)</span><br><span class="line">}</span><br><span class="line">}()</span><br><span class="line">log.Printf(<span class="string">"[*] Waiting for message, To exit press CTRL+C"</span>)</span><br><span class="line"><-forever</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="建立消费文件和生产文件"><a href="#建立消费文件和生产文件" class="headerlink" title="建立消费文件和生产文件"></a>建立消费文件和生产文件</h1><ul><li>消费文件 <code>consume.go</code></li></ul><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"Go/rabbitmq/RabbitMQ"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> rabbitmq := RabbitMQ.NewRabbitMQSimple(<span class="string">"go-simple"</span>)</span><br><span class="line"> rabbitmq.ConsumeSimple()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>生产文件 <code>publish.go</code></li></ul><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"Go/rabbitmq/RabbitMQ"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> rabbitmq := RabbitMQ.NewRabbitMQSimple(<span class="string">"go-simple"</span>)</span><br><span class="line"> rabbitmq.PublishSimple(<span class="string">"Hello Go RabbitMQ!"</span>)</span><br><span class="line"> fmt.Println(<span class="string">"发送成功"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>本文仅分享RabbitMQ在go的简单示例, 具体项目需要更优化的封装</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="RabbitMQ" scheme="http://blog.caoxl.com/tags/RabbitMQ/"/>
<category term="Golang" scheme="http://blog.caoxl.com/tags/Golang/"/>
</entry>
<entry>
<title>分享一下最近的面试笔记【转载】</title>
<link href="http://blog.caoxl.com/2022/10/26/Golang-Interview-Repint/"/>
<id>http://blog.caoxl.com/2022/10/26/Golang-Interview-Repint/</id>
<published>2022-10-26T04:26:43.000Z</published>
<updated>2022-10-26T09:58:36.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>本文是转载自 <a href="https://learnku.com/go/t/65436">分享一下最近的面试笔记</a><br>很久没有面试过, 此文借他人的面试经历, 对一些知识做一些答案</p></blockquote><span id="more"></span><h1 id="Go篇"><a href="#Go篇" class="headerlink" title="Go篇"></a>Go篇</h1><h2 id="channel-必问"><a href="#channel-必问" class="headerlink" title="channel (必问)"></a>channel (必问)</h2><p><code>channel</code> 实现原理,底层实现结构,讲讲怎么优雅的关闭一个 <code>channel</code>。<code>channel</code> 有哪些应用,什么情况下 <code>channel</code> 会造成内存泄露?</p><h2 id="map-必问"><a href="#map-必问" class="headerlink" title="map (必问)"></a>map (必问)</h2><p><code>map</code> 的底层实现,什么情况下会扩容,怎么扩容?是线程安全的吗?那 sync 包中的 map 是怎么实现线程安全的?</p><h2 id="context"><a href="#context" class="headerlink" title="context"></a>context</h2><p>这个一般只会问问 <code>context</code> 包的作用,还有一个碰到很少见的问题,<code>context.Value</code> 查找的过程是什么?</p><h2 id="gmp-必问"><a href="#gmp-必问" class="headerlink" title="gmp (必问)"></a>gmp (必问)</h2><blockquote><p>背就完事了</p></blockquote><h2 id="gc-必问"><a href="#gc-必问" class="headerlink" title="gc (必问)"></a>gc (必问)</h2><p>背不下来,提桶跑路吧。插一句,这个有的公司喜欢问各个版本 stw 的时机都是什么。</p><h2 id="反射"><a href="#反射" class="headerlink" title="反射"></a>反射</h2><h2 id="内存管理"><a href="#内存管理" class="headerlink" title="内存管理"></a>内存管理</h2><h2 id="内存逃逸"><a href="#内存逃逸" class="headerlink" title="内存逃逸"></a>内存逃逸</h2><h2 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h2><p>数组和切片区别,make 和 new 区别。进程、协程、线程区别。</p><h1 id="MySQL篇"><a href="#MySQL篇" class="headerlink" title="MySQL篇"></a>MySQL篇</h1><h2 id="MySQL索引-必问"><a href="#MySQL索引-必问" class="headerlink" title="MySQL索引 (必问)"></a>MySQL索引 (必问)</h2><p>包括索引的类型,索引的数据结果,b + 树和 hash 的区别,b + 树和 b 树的区别,索引创建原则。最左原则,聚簇索引,前缀索引等等。</p><h2 id="MySQL事务-必问"><a href="#MySQL事务-必问" class="headerlink" title="MySQL事务 (必问)"></a>MySQL事务 (必问)</h2><p>包括事务隔离级别,各个隔离界别解决的问题,<code>mvvc</code> 的原理等。</p><h2 id="MySQL锁-必问"><a href="#MySQL锁-必问" class="headerlink" title="MySQL锁 (必问)"></a>MySQL锁 (必问)</h2><p>行锁,表锁,悲观锁啊,乐观锁啊,排它锁,共享锁,间隙锁,范围锁,临键锁,两阶段锁,死锁的原因,事务中各个隔离级别用的锁的类型已经怎么用的锁。</p><h2 id="MySQL数据类型-常问"><a href="#MySQL数据类型-常问" class="headerlink" title="MySQL数据类型 (常问)"></a>MySQL数据类型 (常问)</h2><p>mysql 有哪些数据类型,<code>varchar</code> 与 <code>char</code> 的区别。<code>int</code> 和 <code>int (11)</code> 区别,<code>tinyint</code> 与 <code>int</code> 区别等等。</p><h2 id="MySQL日志"><a href="#MySQL日志" class="headerlink" title="MySQL日志"></a>MySQL日志</h2><p><code>binlog</code> 日志的类型有几种,各有什么优缺点,以及一般怎么设置。<code>undolog</code>,<code>redolog</code>,为什么有 <code>binlog</code> 了还要 <code>redolog</code>,两者有什么区别以及两者生成的时机。</p><h2 id="MySQL架构方面"><a href="#MySQL架构方面" class="headerlink" title="MySQL架构方面"></a>MySQL架构方面</h2><p>mysql 高可用怎么部署,mysql 读写分离原理是什么? 主从同步慢怎么解决? 如果大批量的删除一堆数据,怎么快速释放内存?<code>delete</code> 删除会立刻释放内存吗?<code>truncate</code> 和 <code>drop</code> 呢?</p><h2 id="MySQL问题排查方面"><a href="#MySQL问题排查方面" class="headerlink" title="MySQL问题排查方面"></a>MySQL问题排查方面</h2><p>有没有使用过慢查询,以及怎么解决慢查询,数据量比较大,查询比较慢,你怎么解决的,一般看 mysql 的执行计划,都看哪些参数,以及参数的含义是什么。</p><h1 id="Redis篇"><a href="#Redis篇" class="headerlink" title="Redis篇"></a>Redis篇</h1><h2 id="redis数据类型-必问"><a href="#redis数据类型-必问" class="headerlink" title="redis数据类型 (必问)"></a>redis数据类型 (必问)</h2><p>redis 常用数据类型,以及使用场景等等。各数据类型的底层数据结构是什么?比如 <code>zset</code> 底层实现是跳跃表 (<code>skiplist</code>),讲讲 <code>skiplist</code> 为什么这么快。</p><h2 id="redis的架构-必问"><a href="#redis的架构-必问" class="headerlink" title="redis的架构 (必问)"></a>redis的架构 (必问)</h2><p>这个一般问的是 redis 为什么这么快,除了基于内存操作,还有什么其他原因。redis 是单线程还是多线程?哪部分是单线程,哪部分是多线程?redis 的线程模型(这个问题贼爱问)。</p><h2 id="redis内存持久化"><a href="#redis内存持久化" class="headerlink" title="redis内存持久化"></a>redis内存持久化</h2><p>为什么要持久化?持久化机制、持久化方式是什么,各有什么优缺点。怎么选择合适的持久化方式,有没有了解过混合持久化?</p><h2 id="redis内存淘汰策略"><a href="#redis内存淘汰策略" class="headerlink" title="redis内存淘汰策略"></a>redis内存淘汰策略</h2><blockquote><p>背就完事了</p></blockquote><h2 id="redis事务和管道"><a href="#redis事务和管道" class="headerlink" title="redis事务和管道"></a>redis事务和管道</h2><p>一个是在客户端打包,一个是在服务端打包而已。</p><h2 id="redis的集群方案-高频面试点"><a href="#redis的集群方案-高频面试点" class="headerlink" title="redis的集群方案 (高频面试点)"></a>redis的集群方案 (高频面试点)</h2><p>这个最爱问了,主从,哨兵,分片(分为客户端和服务端分片)</p><h2 id="redis缓存异常解决"><a href="#redis缓存异常解决" class="headerlink" title="redis缓存异常解决"></a>redis缓存异常解决</h2><p>缓存穿透、缓存击穿、缓存雪崩解决方式。</p><h2 id="缓存一致性"><a href="#缓存一致性" class="headerlink" title="缓存一致性"></a>缓存一致性</h2><p>怎么保证 redis 和 mysql 的数据一致性,这个各抒己见吧,可以看看 B 站的毛剑有次的分享,专门讲了怎么解决缓存一致性的问题。</p><h2 id="杂项-1"><a href="#杂项-1" class="headerlink" title="杂项"></a>杂项</h2><p>因为我之前做过一个高并发项目,所以一般爱问分布式锁的实现,还有 redis 的队列和专业的 mq 区别,有没有了解 redis 的 red lock 以及 redis 的 stream 能不能作为专业的 mq,为什么?大批量的删除 redis 中的 key,会有什么问题,怎么解决?redis 怎么实现延时队列,怎么实现异步队列。布隆过滤器了解过吗?</p><h1 id="MQ篇"><a href="#MQ篇" class="headerlink" title="MQ篇"></a>MQ篇</h1><h2 id="使用过哪些消息队列"><a href="#使用过哪些消息队列" class="headerlink" title="使用过哪些消息队列"></a>使用过哪些消息队列</h2><h2 id="RabbitMQ实现消息队列"><a href="#RabbitMQ实现消息队列" class="headerlink" title="RabbitMQ实现消息队列"></a>RabbitMQ实现消息队列</h2><h2 id="RabbitMQ死信队列"><a href="#RabbitMQ死信队列" class="headerlink" title="RabbitMQ死信队列"></a>RabbitMQ死信队列</h2><h2 id="Kafka"><a href="#Kafka" class="headerlink" title="Kafka"></a>Kafka</h2><h1 id="微服务篇"><a href="#微服务篇" class="headerlink" title="微服务篇"></a>微服务篇</h1><h2 id="什么是微服务"><a href="#什么是微服务" class="headerlink" title="什么是微服务?"></a>什么是微服务?</h2><h2 id="怎么划分服务-结合实际项目讲"><a href="#怎么划分服务-结合实际项目讲" class="headerlink" title="怎么划分服务 (结合实际项目讲)"></a>怎么划分服务 (结合实际项目讲)</h2><h2 id="服务治理"><a href="#服务治理" class="headerlink" title="服务治理"></a>服务治理</h2><p>包括但不限于:限流,熔断、负载、监控、鉴权、服务发现和注册等等</p><h2 id="rpc相关内容"><a href="#rpc相关内容" class="headerlink" title="rpc相关内容"></a>rpc相关内容</h2><h2 id="protobuf编码为什么这么高效-和json比较呢"><a href="#protobuf编码为什么这么高效-和json比较呢" class="headerlink" title="protobuf编码为什么这么高效? 和json比较呢?"></a>protobuf编码为什么这么高效? 和json比较呢?</h2><h2 id="gateway相关问题"><a href="#gateway相关问题" class="headerlink" title="gateway相关问题"></a>gateway相关问题</h2>]]></content>
<summary type="html">
<blockquote>
<p>本文是转载自 <a href="https://learnku.com/go/t/65436">分享一下最近的面试笔记</a><br>很久没有面试过, 此文借他人的面试经历, 对一些知识做一些答案</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试题" scheme="http://blog.caoxl.com/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="笔试题" scheme="http://blog.caoxl.com/tags/%E7%AC%94%E8%AF%95%E9%A2%98/"/>
<category term="Golang" scheme="http://blog.caoxl.com/tags/Golang/"/>
</entry>
<entry>
<title>pprof 性能分析</title>
<link href="http://blog.caoxl.com/2022/10/26/High-Performance-Pprof/"/>
<id>http://blog.caoxl.com/2022/10/26/High-Performance-Pprof/</id>
<published>2022-10-26T01:54:59.000Z</published>
<updated>2022-10-26T02:57:07.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>面对一个未知的程序,如何去分析这个程序的性能,并找到瓶颈点呢?</p></blockquote><span id="more"></span><p><code>pprof</code> 就是用来解决这个问题的。pprof 包含两部分:</p><ul><li>编译到程序中的 <code>runtime/pprof</code> 包</li><li>性能剖析工具 <code>go tool pprof</code></li></ul><h1 id="性能分析"><a href="#性能分析" class="headerlink" title="性能分析"></a>性能分析</h1><blockquote><p>记录性能数据会对程序的性能产生影响,建议一次只记录一类数据。</p></blockquote><h2 id="CPU性能分析"><a href="#CPU性能分析" class="headerlink" title="CPU性能分析"></a>CPU性能分析</h2><h3 id="生成profile"><a href="#生成profile" class="headerlink" title="生成profile"></a>生成profile</h3><p>Go 的运行时性能分析接口都位于 <code>runtime/pprof</code> 包中。只需要调用 <code>runtime/pprof</code> 库即可得到我们想要的数据。</p><p>假设我们实现了这么一个程序,随机生成了 5 组数据,并且使用冒泡排序法排序。</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math/rand"</span></span><br><span class="line"> <span class="string">"os"</span></span><br><span class="line"> <span class="string">"runtime/pprof"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">generate</span><span class="params">(n <span class="type">int</span>)</span></span> []<span class="type">int</span> {</span><br><span class="line"> rand.Seed(time.Now().UnixNano())</span><br><span class="line"> nums := <span class="built_in">make</span>([]<span class="type">int</span>, <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < n; i++ {</span><br><span class="line"> nums = <span class="built_in">append</span>(nums, rand.Int())</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> nums</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">bubbleSort</span><span class="params">(nums []<span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">len</span>(nums); i++ {</span><br><span class="line"> <span class="keyword">for</span> j := <span class="number">1</span>; j < <span class="built_in">len</span>(nums)<span class="number">-1</span>; j++ {</span><br><span class="line"> <span class="keyword">if</span> nums[j] < nums[j<span class="number">-1</span>] {</span><br><span class="line"> nums[j], nums[j<span class="number">-1</span>] = nums[j<span class="number">-1</span>], nums[j]</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><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> n := <span class="number">10</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">5</span>; i++ {</span><br><span class="line"> nums := generate(n)</span><br><span class="line"> bubbleSort(nums)</span><br><span class="line"> n *= <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"执行完毕"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果我们想度量这个应用程序的 CPU 性能数据,只需要在 main 函数中添加 2 行代码即可:</p><figure class="highlight golang"><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="keyword">import</span> (</span><br><span class="line"> <span class="string">"math/rand"</span></span><br><span class="line"> <span class="string">"os"</span></span><br><span class="line"> <span class="string">"runtime/pprof"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> pprof.StartCPUProfile(os.Stdout)</span><br><span class="line"> <span class="keyword">defer</span> pprof.StopCPUProfile()</span><br><span class="line"> n := <span class="number">10</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">5</span>; i++ {</span><br><span class="line"> nums := generate(n)</span><br><span class="line"> bubbleSort(nums)</span><br><span class="line"> n *= <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> fmt.Println(<span class="string">"执行完毕"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>为了简单,直接将数据输出到标准输出 <code>os.Stdout</code>。运行该程序,将输出定向到文件 <code>cpu.pprof</code> 中。</p><p>一般来说,不建议将结果直接输出到标准输出,因为如果程序本身有输出,则会相互干扰,直接记录到一个文件中是最好的方式。</p><figure class="highlight golang"><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">func</span> <span class="title">startCPUProfile</span><span class="params">()</span></span> {</span><br><span class="line"> f, _ := os.OpenFile(<span class="string">"./cpu.pprof"</span>, os.O_CREATE|os.O_RDWR, <span class="number">0644</span>)</span><br><span class="line"> <span class="keyword">defer</span> f.Close()</span><br><span class="line"> _ = pprof.StartCPUProfile(f)</span><br><span class="line"> <span class="keyword">defer</span> pprof.StopCPUProfile()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> startCPUProfile()</span><br><span class="line"> n := <span class="number">10</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">5</span>; i++ {</span><br><span class="line"> nums := generate(n)</span><br><span class="line"> bubbleSort(nums)</span><br><span class="line"> n *= <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"执行完毕"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样只需运行 <code>go run main.go</code> 即可。</p><h3 id="分析数据"><a href="#分析数据" class="headerlink" title="分析数据"></a>分析数据</h3><p>接下来,可以用 <code>go tool pprof</code> 分析这份数据</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">go</span> tool pprof -http=:<span class="number">9999</span> cpu.pprof</span><br></pre></td></tr></table></figure><p>访问 <code>localhost:9999</code>, 即可看到</p><p>除了在网页中查看分析数据外,我们也可以在命令行中使用交互模式查看</p><figure class="highlight golang"><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="keyword">go</span> tool pprof /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile2456682500/mem.pprof <span class="number">1</span></span><br><span class="line"></span><br><span class="line">Type: inuse_space</span><br><span class="line">Time: Oct <span class="number">26</span>, <span class="number">2022</span> at <span class="number">10</span>:<span class="number">35</span>am (CST)</span><br><span class="line">Entering interactive mode (<span class="keyword">type</span> <span class="string">"help"</span> <span class="keyword">for</span> commands, <span class="string">"o"</span> <span class="keyword">for</span> options)</span><br><span class="line">(pprof) top</span><br><span class="line">Showing nodes accounting <span class="keyword">for</span> <span class="number">548.73</span>kB, <span class="number">98.95</span>% of <span class="number">554.55</span>kB total</span><br><span class="line">Dropped <span class="number">56</span> nodes (cum <= <span class="number">2.77</span>kB)</span><br><span class="line"> flat flat% sum% cum cum%</span><br><span class="line"> <span class="number">524.61</span>kB <span class="number">94.60</span>% <span class="number">94.60</span>% <span class="number">546.48</span>kB <span class="number">98.55</span>% main.concat</span><br><span class="line"> <span class="number">21.88</span>kB <span class="number">3.94</span>% <span class="number">98.55</span>% <span class="number">21.88</span>kB <span class="number">3.94</span>% main.randomString</span><br><span class="line"> <span class="number">2.25</span>kB <span class="number">0.41</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.allocm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">548.61</span>kB <span class="number">98.93</span>% main.main</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">548.61</span>kB <span class="number">98.93</span>% runtime.main</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.newm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.resetspinning</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.schedule</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.startm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.wakep</span><br></pre></td></tr></table></figure><p>还可以按照 <code>cum</code> (累计消耗)排序:</p><figure class="highlight golang"><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">(pprof) top -cum</span><br><span class="line">Showing nodes accounting <span class="keyword">for</span> <span class="number">548.73</span>kB, <span class="number">98.95</span>% of <span class="number">554.55</span>kB total</span><br><span class="line">Dropped <span class="number">56</span> nodes (cum <= <span class="number">2.77</span>kB)</span><br><span class="line"> flat flat% sum% cum cum%</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">0</span>% <span class="number">548.61</span>kB <span class="number">98.93</span>% main.main</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">0</span>% <span class="number">548.61</span>kB <span class="number">98.93</span>% runtime.main</span><br><span class="line"> <span class="number">524.61</span>kB <span class="number">94.60</span>% <span class="number">94.60</span>% <span class="number">546.48</span>kB <span class="number">98.55</span>% main.concat</span><br><span class="line"> <span class="number">21.88</span>kB <span class="number">3.94</span>% <span class="number">98.55</span>% <span class="number">21.88</span>kB <span class="number">3.94</span>% main.randomString</span><br><span class="line"> <span class="number">2.25</span>kB <span class="number">0.41</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.allocm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.newm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.resetspinning</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.schedule</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.startm</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">98.95</span>% <span class="number">3.31</span>kB <span class="number">0.6</span>% runtime.wakep</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>help</code> 可以查看所有支持的命令和选项:</p><figure class="highlight golang"><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">(pprof) help</span><br><span class="line"> Commands:</span><br><span class="line"> callgrind Outputs a graph in callgrind format</span><br><span class="line"> comments Output all profile comments</span><br><span class="line"> disasm Output assembly listings annotated with samples</span><br><span class="line"> dot Outputs a graph in DOT format</span><br><span class="line"> eog Visualize graph through eog</span><br><span class="line"> evince Visualize graph through evince</span><br><span class="line"> gif Outputs a graph image in GIF format</span><br><span class="line"> gv Visualize graph through gv</span><br><span class="line">......</span><br></pre></td></tr></table></figure><h2 id="内存性能分析"><a href="#内存性能分析" class="headerlink" title="内存性能分析"></a>内存性能分析</h2><h3 id="生成profile-1"><a href="#生成profile-1" class="headerlink" title="生成profile"></a>生成profile</h3><p>假设我们实现了这么一个程序,生成长度为 N 的随机字符串,拼接在一起。</p><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"github.com/pkg/profile"</span></span><br><span class="line"> <span class="string">"math/rand"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> letterBytes = <span class="string">"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">randomString</span><span class="params">(n <span class="type">int</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> b := <span class="built_in">make</span>([]<span class="type">byte</span>, n)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> b {</span><br><span class="line"> b[i] = letterBytes[rand.Intn(<span class="built_in">len</span>(letterBytes))]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="type">string</span>(b)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">concat</span><span class="params">(n <span class="type">int</span>)</span></span> <span class="type">string</span> {</span><br><span class="line"> s := <span class="string">""</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < n; i++ {</span><br><span class="line"> s += randomString(n)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> s</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> profile.Start(profile.MemProfile, profile.MemProfileRate(<span class="number">1</span>)).Stop()</span><br><span class="line"> concat(<span class="number">100</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>接下来,我们使用一个易用性更强的库 <code>pkg/profile</code> 来采集性能数据,<code>pkg/profile</code> 封装了 <code>runtime/pprof</code> 的接口,使用起来更简单。</p><p>比如我们想度量 <code>concat()</code> 的 CPU 性能数据,只需要一行代码即可生成 profile 文件。</p><figure class="highlight golang"><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="keyword">import</span> (</span><br><span class="line"> <span class="string">"github.com/pkg/profile"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> profile.Start().Stop()</span><br><span class="line"> concat(<span class="number">100</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行 <code>go run main.go</code>:</p><figure class="highlight golang"><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">go</span> run main.<span class="keyword">go</span> </span><br><span class="line"><span class="number">2022</span>/<span class="number">10</span>/<span class="number">26</span> <span class="number">10</span>:<span class="number">33</span>:<span class="number">08</span> profile: cpu profiling enabled, /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile3671108945/cpu.pprof</span><br><span class="line"><span class="number">2022</span>/<span class="number">10</span>/<span class="number">26</span> <span class="number">10</span>:<span class="number">33</span>:<span class="number">08</span> profile: cpu profiling disabled, /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile3671108945/cpu.pprof</span><br></pre></td></tr></table></figure><p><code>CPU profile</code> 文件已经在 <code>tmp</code> 目录生成,得到 <code>profile</code> 文件后,就可以像之前一样,用 <code>go tool pprof</code> 命令,在浏览器或命令行进行分析了。</p><p>接下来将使用类似的方式,进行<strong>采集内存数据</strong>,同样地,只需简单地修改 main 函数即可。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> profile.Start(profile.MemProfile, profile.MemProfileRate(<span class="number">1</span>)).Stop()</span><br><span class="line"> concat(<span class="number">100</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行 <code>go run main.go</code></p><figure class="highlight golang"><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">go</span> run main.<span class="keyword">go</span> </span><br><span class="line"><span class="number">2022</span>/<span class="number">10</span>/<span class="number">26</span> <span class="number">10</span>:<span class="number">35</span>:<span class="number">50</span> profile: memory profiling enabled (rate <span class="number">1</span>), /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile2456682500/mem.pprof</span><br><span class="line"><span class="number">2022</span>/<span class="number">10</span>/<span class="number">26</span> <span class="number">10</span>:<span class="number">35</span>:<span class="number">50</span> profile: memory profiling disabled, /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile2456682500/mem.pprof</span><br></pre></td></tr></table></figure><h3 id="分析数据-1"><a href="#分析数据-1" class="headerlink" title="分析数据"></a>分析数据</h3><p>接下来,我们就可以在浏览器中分析内存性能数据:</p><figure class="highlight golang"><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">go</span> tool pprof -http=:<span class="number">9999</span> /<span class="keyword">var</span>/folders/dd/<span class="number">11</span>ddhj_s2dbdnj8mx91b97800000gn/T/profile2456682500/mem.pprof</span><br><span class="line"></span><br><span class="line">Serving web UI on http:<span class="comment">//localhost:9999</span></span><br></pre></td></tr></table></figure><h1 id="Benchmark生成profile"><a href="#Benchmark生成profile" class="headerlink" title="Benchmark生成profile"></a>Benchmark生成profile</h1><p><code>Benchmark</code>除了直接在命令行中查看测试的结果外,也可以生成 <code>profile</code> 文件,使用 <code>go tool pprof</code> 分析。</p><p><code>testing</code> 支持生成 <code>CPU</code>、<code>memory</code> 和 <code>block</code> 的 <code>profile</code> 文件。</p><ul><li><code>-cpuprofile=$FILE</code></li><li><code>-memprofile=$FILE, -memprofilerate=N</code> 调整记录速率为原来的<code>1/N</code></li><li><code>-blockprofile=$FILE</code></li></ul><p>只需要在 <code>go test</code> 添加 <code>-cpuprofile</code> 参数即可生成 <code>BenchmarkFib</code> 对应的 <code>CPU profile</code> 文件:</p><figure class="highlight golang"><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="keyword">go</span> test -bench=<span class="string">"Fib$"</span> -cpuprofile=cpu.pprof .</span><br><span class="line">goos: darwin</span><br><span class="line">goarch: amd64</span><br><span class="line">pkg: Go/pprof/benchmark</span><br><span class="line">cpu: Intel(R) Core(TM) i5<span class="number">-7267</span>U CPU @ <span class="number">3.10</span>GHz</span><br><span class="line">BenchmarkFib<span class="number">-4</span> <span class="number">265</span> <span class="number">4732255</span> ns/op</span><br><span class="line">PASS</span><br><span class="line">ok Go/pprof/benchmark <span class="number">1.827</span>s</span><br></pre></td></tr></table></figure><p>用例执行完毕后,当前目录多出了一个 <code>cpu.pprof</code> 文件,接下来就可以使用 <code>go tool pprof</code> 命令进行分析了。</p><figure class="highlight golang"><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">go</span> tool pprof -http=:<span class="number">9999</span> cpu.pprof</span><br><span class="line">Serving web UI on http:<span class="comment">//localhost:9999</span></span><br></pre></td></tr></table></figure><p>也可以使用 <code>-text</code> 选项可以直接将结果以文本形式打印出来。</p><figure class="highlight golang"><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">go</span> tool pprof -text cpu.pprof</span><br><span class="line">Type: cpu</span><br><span class="line">Time: Oct <span class="number">26</span>, <span class="number">2022</span> at <span class="number">10</span>:<span class="number">53</span>am (CST)</span><br><span class="line">Duration: <span class="number">1.75</span>s, Total samples = <span class="number">540</span>ms (<span class="number">30.80</span>%)</span><br><span class="line">Showing nodes accounting <span class="keyword">for</span> <span class="number">540</span>ms, <span class="number">100</span>% of <span class="number">540</span>ms total</span><br><span class="line"> flat flat% sum% cum cum%</span><br><span class="line"> <span class="number">540</span>ms <span class="number">100</span>% <span class="number">100</span>% <span class="number">540</span>ms <span class="number">100</span>% Go/pprof/benchmark.fib</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">100</span>% <span class="number">540</span>ms <span class="number">100</span>% Go/pprof/benchmark.BenchmarkFib</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">100</span>% <span class="number">540</span>ms <span class="number">100</span>% testing.(*B).launch</span><br><span class="line"> <span class="number">0</span> <span class="number">0</span>% <span class="number">100</span>% <span class="number">540</span>ms <span class="number">100</span>% testing.(*B).runN</span><br></pre></td></tr></table></figure><p><code>pprof</code> 支持多种输出格式(图片、文本、Web等),直接在命令行中运行 <code>go tool pprof</code> 即可看到所有支持的选项:</p><figure class="highlight golang"><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="keyword">go</span> tool pprof</span><br><span class="line">Details:</span><br><span class="line"> Output formats (<span class="keyword">select</span> at most one):</span><br><span class="line"> -callgrind Outputs a graph in callgrind format</span><br><span class="line"> -comments Output all profile comments</span><br><span class="line"> -disasm Output assembly listings annotated with samples</span><br><span class="line"> -dot Outputs a graph in DOT format</span><br><span class="line"> -eog Visualize graph through eog</span><br><span class="line"> -evince Visualize graph through evince</span><br><span class="line"> -gif Outputs a graph image in GIF format</span><br><span class="line"> -gv Visualize graph through gv</span><br><span class="line"> -kcachegrind Visualize report in KCachegrind</span><br><span class="line"> -list Output annotated source <span class="keyword">for</span> functions matching regexp</span><br><span class="line"> -pdf Outputs a graph in PDF format</span><br><span class="line"> -peek Output callers/callees of functions matching regexp</span><br><span class="line"> -png Outputs a graph image in PNG format</span><br><span class="line"> -proto Outputs the profile in compressed protobuf format</span><br><span class="line"> -ps Outputs a graph in PS format</span><br><span class="line"> -raw Outputs a text representation of the raw profile</span><br><span class="line"> -svg Outputs a graph in SVG format</span><br><span class="line"> -tags Outputs all tags in the profile</span><br><span class="line"> -text Outputs top entries in text form</span><br><span class="line"> -top Outputs top entries in text form</span><br><span class="line"> -topproto Outputs top entries in compressed protobuf format</span><br><span class="line"> -traces Outputs all profile samples in text form</span><br><span class="line"> -tree Outputs a text rendering of call graph</span><br><span class="line"> -web Visualize graph through web browser</span><br><span class="line"> -weblist Display annotated source in a web browser</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>面对一个未知的程序,如何去分析这个程序的性能,并找到瓶颈点呢?</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="Golang" scheme="http://blog.caoxl.com/tags/Golang/"/>
<category term="性能分析" scheme="http://blog.caoxl.com/tags/%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90/"/>
<category term="pprof" scheme="http://blog.caoxl.com/tags/pprof/"/>
</entry>
<entry>
<title>Golang 面试题 V</title>
<link href="http://blog.caoxl.com/2022/10/24/Golang-Interview-V/"/>
<id>http://blog.caoxl.com/2022/10/24/Golang-Interview-V/</id>
<published>2022-10-24T09:37:27.000Z</published>
<updated>2022-10-26T08:58:44.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>一些杂的内容</p></blockquote><span id="more"></span><h1 id="go的调度"><a href="#go的调度" class="headerlink" title="go的调度"></a>go的调度</h1><ul><li><p>单线程调度器</p></li><li><p>多线程调度器</p></li><li><p>任务窃取调度器</p></li><li><p>抢占式调度器</p></li><li><p>非均匀存储访问调度器</p></li><li><p><a href="https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/">调度器</a></p></li></ul><h1 id="go-struct能不能比较"><a href="#go-struct能不能比较" class="headerlink" title="go struct能不能比较"></a>go struct能不能比较</h1><p>struct能不能比较? 很显然这句话包含了两种情况</p><ul><li><ol><li>同一个<code>struct</code>的两个实例能不能比较?</li></ol></li><li><ol start="2"><li>两个不同的<code>struct</code>的实例能不能比较?</li></ol></li></ul><blockquote><p>同一个struct的两个实例可比较也不可比较,当结构不包含不可直接比较成员变量时可直接比较,否则不可直接比较<br>两个不同的struct的实例可比较也不可比较, 如果成员变量中含有不可比较成员变量,即使可以强制转换,也不可以比较</p></blockquote><h2 id="可比较与不可比较类型"><a href="#可比较与不可比较类型" class="headerlink" title="可比较与不可比较类型"></a>可比较与不可比较类型</h2><ul><li>可比较: <code>Integer</code>, <code>Floating-point</code>, <code>String</code>, <code>Boolean</code>, <code>Complex(复数型)</code>, <code>Pointer</code>, <code>Channel</code>, <code>Interface</code>, <code>Array</code></li><li>不可比较: <code>Slice</code>, <code>Map</code>, <code>Function</code></li></ul><h1 id="go-defer(for-defer)"><a href="#go-defer(for-defer)" class="headerlink" title="go defer(for defer)"></a>go defer(for defer)</h1><blockquote><p>defer和go一样都是Go语言提供的关键字。defer用于资源的释放,会在函数返回之前进行调用。<br>如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用</p></blockquote><figure class="highlight golang"><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">func</span> <span class="title">f1</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> r++</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="function"><span class="keyword">func</span> <span class="title">f2</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> t := <span class="number">5</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> t = t + <span class="number">5</span></span><br><span class="line"> }()</span><br><span class="line"> <span class="keyword">return</span> t</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f3</span><span class="params">()</span></span> (r <span class="type">int</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">(r <span class="type">int</span>)</span></span> {</span><br><span class="line"> r = r + <span class="number">5</span></span><br><span class="line"> }(r)</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>请读者先不要运行代码,在心里跑一遍结果,然后去验证。</p><p>例1的正确答案不是0,例2的正确答案不是10,如果例3的正确答案不是6……</p><p>函数返回的过程是这样的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。</p><p>其实使用defer时,用一个简单的转换规则改写一下,就不会迷糊了。改写规则是将return语句拆成两句写,return xxx会被改写成:</p><figure class="highlight golang"><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">返回值 = xxx</span><br><span class="line">调用<span class="keyword">defer</span>函数</span><br><span class="line">空的<span class="keyword">return</span></span><br></pre></td></tr></table></figure><blockquote><p>答案: 例1: 1 例2: 5 例3: 1</p></blockquote><h1 id="select可以用于什么"><a href="#select可以用于什么" class="headerlink" title="select可以用于什么"></a>select可以用于什么</h1><blockquote><p>select 只能应用于channel 的操作,<strong>既可以用于channel 的数据接收,也可以用于channel 的数据发送</strong>。 如果select 的多个分支都满足条件,则会随机的选取其中一个满足条件的分支执行。</p></blockquote><h1 id="context包的用途"><a href="#context包的用途" class="headerlink" title="context包的用途"></a>context包的用途</h1><blockquote><p>context 包是Go 1.7 引入的标准库,<strong>主要用于在goroutine 之间传递取消信号、超时时间、截止时间以及一些共享的值等</strong>。 它并不是太完美,但几乎成了并发控制和超时控制的标准做法。 使用上,先创建一个根节点的context,之后根据库提供的四个函数创建相应功能的子节点context。</p></blockquote><h1 id="client如何实现长连接"><a href="#client如何实现长连接" class="headerlink" title="client如何实现长连接"></a>client如何实现长连接</h1><blockquote><p>net.Dialer.KeepAlive:开启长连接(说明默认http client是默认开启长连接的)</p></blockquote><h1 id="主协程如何等其余协程完再操作"><a href="#主协程如何等其余协程完再操作" class="headerlink" title="主协程如何等其余协程完再操作"></a>主协程如何等其余协程完再操作</h1><ul><li><code>channel</code>实现同步</li><li><code>sync.WaitGroup</code></li></ul><h1 id="slice,len,cap,共享,扩容"><a href="#slice,len,cap,共享,扩容" class="headerlink" title="slice,len,cap,共享,扩容"></a>slice,len,cap,共享,扩容</h1><figure class="highlight golang"><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">type</span> slice <span class="keyword">struct</span> {</span><br><span class="line"> array unsafe.Pointer</span><br><span class="line"> <span class="built_in">len</span> <span class="type">int</span></span><br><span class="line"> <span class="built_in">cap</span> <span class="type">int</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当<code>slice</code>的<code>len==cap</code>后,再向<code>slice</code>中追加元素时,会发生扩容</p><p>扩容遵循如下规则:(但结果不完全遵循此规则)</p><ul><li>如果原<code>slice</code>容量小于1024,则新<code>slice</code>容量将扩大为原来的<code>2</code>倍;</li><li>如果原<code>slice</code>容量大于等于1024,则新<code>slice</code>容量将扩大为原来的<code>1.25</code>倍;</li><li>如果扩容后的大小仍不能满足,那么直接扩容到所需的容量</li><li>在以上计算完新<code>slice</code>容量后,交由管理内存的组件申请内存,按照给出的表向上取整进行内存申请,申请出来的内存长度,作为<code>slice</code>扩容后的容量</li></ul><h1 id="map如何顺序读取"><a href="#map如何顺序读取" class="headerlink" title="map如何顺序读取"></a>map如何顺序读取</h1><blockquote><p>由于map底层实现与 slice不同, map底层使用<code>hash</code>表实现,<strong>插入数据位置是随机的</strong>, 所以遍历过程中新插入的数据不能保证遍历。</p></blockquote><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"sort"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>)</span><br><span class="line"> m[<span class="string">"b"</span>] = <span class="string">"2"</span></span><br><span class="line"> m[<span class="string">"a"</span>] = <span class="string">"1"</span></span><br><span class="line"> m[<span class="string">"c"</span>] = <span class="string">"3"</span></span><br><span class="line"></span><br><span class="line"> keys := <span class="built_in">make</span>([]<span class="type">string</span>, <span class="number">0</span>, <span class="built_in">len</span>(m))</span><br><span class="line"> <span class="keyword">for</span> k, _ := <span class="keyword">range</span> m {</span><br><span class="line"> keys = <span class="built_in">append</span>(keys, k)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> sort.Strings(keys)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, k := <span class="keyword">range</span> keys {</span><br><span class="line"> fmt.Printf(<span class="string">"Key:%+v, Value:%+v\n"</span>, k, m[k])</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>对 key 排序</strong>,那么我们便可将 map 的 key 全部拿出来,放到一个数组中,然后对这个数组排序后对有序数组遍历,再间接取 map 里的值就行了。</p><h1 id="大文件排序"><a href="#大文件排序" class="headerlink" title="大文件排序"></a>大文件排序</h1><ul><li>随机数</li><li>类型转换</li><li>文件操作</li><li><code>priorityqueue</code>优先级队列</li></ul><h1 id="基本排序,哪些是稳定的"><a href="#基本排序,哪些是稳定的" class="headerlink" title="基本排序,哪些是稳定的"></a>基本排序,哪些是稳定的</h1><ul><li><p>常见的7种排序算法</p><ul><li>选择排序</li><li>冒泡排序</li><li>插入排序</li><li>希尔排序</li><li>归并排序 </li><li>快速排序 </li><li>堆排序</li></ul></li><li><p>稳定的排序算法</p><ul><li>冒泡排序</li><li>插入排序</li><li>归并排序</li></ul></li></ul><h1 id="http-get跟head"><a href="#http-get跟head" class="headerlink" title="http get跟head"></a>http get跟head</h1><p>HTTP协议中GET、POST和HEAD的区别? <strong>HEAD: 只请求页面的首部。 GET: 请求指定的页面信息,并返回实体主体</strong>。 POST: 请求服务器接受所指定的文档作为对所标识的URL.</p><h1 id="http-keep-alive"><a href="#http-keep-alive" class="headerlink" title="http keep-alive"></a>http keep-alive</h1><p>当使用<code>Keep-Alive</code>模式(又称持久连接、连接重用)时,<code>Keep-Alive</code>功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,<code>Keep-Alive</code>功能避免了建立或者重新建立连接</p><h1 id="http能不能一次连接多次请求,不等后端返回"><a href="#http能不能一次连接多次请求,不等后端返回" class="headerlink" title="http能不能一次连接多次请求,不等后端返回"></a>http能不能一次连接多次请求,不等后端返回</h1><p>如果是<code>http1.0</code>,一个tcp连接只能发一个<code>http</code>请求,如果是<code>http1.1</code>及以上,引入了持续连接的概念,所以一个<code>tcp</code>连接能发多个<code>http</code>请求。</p><h1 id="linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程"><a href="#linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程" class="headerlink" title="linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程"></a>linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程</h1><ul><li>查看端口占用</li></ul><figure class="highlight golang"><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">// lsof</span></span><br><span class="line">lsof -i:端口号</span><br><span class="line"></span><br><span class="line"><span class="comment">// netstat</span></span><br><span class="line">netstat -tunlp | grep 端口号</span><br></pre></td></tr></table></figure><ul><li>查看cpu负载</li></ul><figure class="highlight golang"><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">top</span><br><span class="line"></span><br><span class="line">vmstat <span class="number">1</span> <span class="number">5</span></span><br><span class="line"></span><br><span class="line">sar -u <span class="number">1</span> <span class="number">5</span></span><br><span class="line"></span><br><span class="line">iostat -c <span class="number">1</span> <span class="number">2</span></span><br><span class="line"></span><br><span class="line">dstat -c</span><br></pre></td></tr></table></figure><ul><li>查看内存占用</li></ul><figure class="highlight golang"><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">df -m</span><br><span class="line"></span><br><span class="line">free</span><br><span class="line"></span><br><span class="line">cat /proc/meminfo</span><br><span class="line"></span><br><span class="line">ps aux -sort -rss</span><br><span class="line"></span><br><span class="line">vmstat -s</span><br></pre></td></tr></table></figure><h1 id="git文件版本,使用顺序,merge跟rebase"><a href="#git文件版本,使用顺序,merge跟rebase" class="headerlink" title="git文件版本,使用顺序,merge跟rebase"></a>git文件版本,使用顺序,merge跟rebase</h1><ul><li>使用 rebase 和 merge 的基本原则:<ul><li>下游分支更新上游分支内容的时候使用 <code>rebase</code></li><li>上游分支合并下游分支内容的时候使用 <code>merge</code></li><li>更新当前分支的内容时一定要使用 <code>--rebase</code> 参数</li></ul></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>一些杂的内容</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 面试题 IV</title>
<link href="http://blog.caoxl.com/2022/10/19/Golang-Interview-IV/"/>
<id>http://blog.caoxl.com/2022/10/19/Golang-Interview-IV/</id>
<published>2022-10-19T07:44:19.000Z</published>
<updated>2022-10-26T08:58:44.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>坚持, 量变才会引起质变</p></blockquote><span id="more"></span><h1 id="new和make的区别?"><a href="#new和make的区别?" class="headerlink" title="new和make的区别?"></a><code>new</code>和<code>make</code>的区别?</h1><ul><li><code>new</code>只用于分配内存,返回一个指向地址的指针。它为每个新类型分配一片内存,初始化为0且返回类型<code>*T</code>的<strong>内存地址</strong>,它相当于<code>&T{}</code></li><li><code>make</code>只可用于<code>slice</code>,<code>map</code>,<code>channel</code>的初始化,返回的是<strong>引用</strong>。</li></ul><h1 id="Go面向对象是如何实现的?"><a href="#Go面向对象是如何实现的?" class="headerlink" title="Go面向对象是如何实现的?"></a>Go面向对象是如何实现的?</h1><p>Go实现面向对象的两个关键是<code>struct</code>和<code>interface</code>。</p><ul><li>封装:对于同一个包,对象对包内的文件可见;对不同的包,需要将对象以大写开头才是可见的。</li><li>继承: 继承是编译时特征,在struct内加入所需要继承的类即可; (组合)</li><li>多态: 多态是运行时特征,Go多态通过<code>interface</code>来实现。类型和接口是松耦合的,某个类型的实例可以赋给它所实现的任意接口类型的变量。</li></ul><p>Go支持多重继承,就是在类型中嵌入所有必要的父类型。</p><h1 id="uint型变量值分别为-1,2,它们相减的结果是多少?"><a href="#uint型变量值分别为-1,2,它们相减的结果是多少?" class="headerlink" title="uint型变量值分别为 1,2,它们相减的结果是多少?"></a>uint型变量值分别为 1,2,它们相减的结果是多少?</h1><figure class="highlight golang"><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">var</span> a <span class="type">uint</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> b <span class="type">uint</span> = <span class="number">2</span></span><br><span class="line">fmt.Println(a - b)</span><br></pre></td></tr></table></figure><blockquote><p>结果会溢出,如果是32位系统,结果是<code>2^32-1</code>,如果是64位系统,结果<code>2^64-1</code></p></blockquote><h1 id="有没有函数在main之前执行?怎么用?"><a href="#有没有函数在main之前执行?怎么用?" class="headerlink" title="有没有函数在main之前执行?怎么用?"></a>有没有函数在main之前执行?怎么用?</h1><blockquote><p>init函数</p></blockquote><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>init函数非常特殊:<ul><li>初始化不能采用初始化表达式初始化的变量</li><li>程序运行前执行注册 </li><li>实现<code>sync.Once</code>功能</li><li>不能被其它函数调用</li><li><code>init</code>函数没有入口参数和返回值</li><li>每个包可以有多个<code>init</code>函数,每个源文件也可以有多个<code>init</code>函数</li><li>同一个包的<code>init</code>执行顺序,<code>golang</code>没有明确定义,编程时要注意程序不要依赖这个执行顺序</li><li>不同包的<code>init</code>函数按照包导入的依赖关系决定执行顺序</li></ul></li></ul><h1 id="下面这句代码是什么作用,为什么要定义一个空值?"><a href="#下面这句代码是什么作用,为什么要定义一个空值?" class="headerlink" title="下面这句代码是什么作用,为什么要定义一个空值?"></a>下面这句代码是什么作用,为什么要定义一个空值?</h1><figure class="highlight golang"><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="keyword">type</span> GobCodec <span class="keyword">struct</span> {</span><br><span class="line"> conn io.ReadWriteCloser</span><br><span class="line"> buf *bufio.Writer</span><br><span class="line"> dec *gob.Decoder</span><br><span class="line"> enc *gob.Encoder</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Codec <span class="keyword">interface</span> {</span><br><span class="line"> io.Closer</span><br><span class="line"> ReadHeader(*Header) <span class="type">error</span></span><br><span class="line"> ReadBody(<span class="keyword">interface</span>{}) <span class="type">error</span></span><br><span class="line"> Write(*Header, <span class="keyword">interface</span>{}) <span class="type">error</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _ Codec = (*GobCodec)(<span class="literal">nil</span>)</span><br></pre></td></tr></table></figure><blockquote><p>将<code>nil</code>转换为<code>*GobCodec</code>类型,然后再转换为<code>Codec</code>接口,如果转换失败,说明<code>*GobCodec</code>没有实现<code>Codec</code>接口的所有方法。</p></blockquote><h1 id="mutex有几种模式?"><a href="#mutex有几种模式?" class="headerlink" title="mutex有几种模式?"></a>mutex有几种模式?</h1><blockquote><p><code>mutex</code>有两种模式:<code>normal</code> 和 <code>starvation</code></p></blockquote><h2 id="正常模式"><a href="#正常模式" class="headerlink" title="正常模式"></a>正常模式</h2><p>所有<code>goroutine</code>按照<code>FIFO</code>的顺序进行锁获取,被唤醒的<code>goroutine</code>和新请求锁的<code>goroutine</code>同时进行锁获取,通常<strong>新请求锁的<code>goroutine</code>更容易获取锁</strong>(持续占有cpu),被唤醒的<code>goroutine</code>则不容易获取到锁。公平性:否。</p><h2 id="饥饿模式"><a href="#饥饿模式" class="headerlink" title="饥饿模式"></a>饥饿模式</h2><p>所有尝试获取锁的<code>goroutine</code>进行等待排队,新请求锁的<code>goroutine</code>不会进行锁获取(禁用自旋),而是加入队列尾部等待获取锁。公平性:是。</p><h1 id="go如何进行调度的。GMP中状态流转"><a href="#go如何进行调度的。GMP中状态流转" class="headerlink" title="go如何进行调度的。GMP中状态流转"></a>go如何进行调度的。GMP中状态流转</h1><p>Go里面GMP分别代表:G:<code>goroutine</code>,M:<code>线程</code>(真正在CPU上跑的),P:<code>调度器</code>。</p><blockquote><p>调度器是M和G之间桥梁。</p></blockquote><p>go进行调度过程</p><ul><li>某个线程尝试创建一个新的<code>G</code>,那么这个G就会被安排到这个线程的<code>G</code>本地队列<code>LRQ</code>中,如果<code>LRQ</code>满了,就会分配到全局队列<code>GRQ</code>中;</li><li>尝试获取当前线程的<code>M</code>,如果无法获取,就会从空闲的<code>M</code>列表中找一个,如果空闲列表也没有,那么就创建一个<code>M</code>,然后绑定<code>G</code>与<code>P</code>运行。</li><li>进入调度循环:<ul><li>找到一个合适的<code>G</code></li><li>执行<code>G</code>,完成以后退出</li></ul></li></ul><h1 id="Go什么时候发生阻塞?阻塞时,调度器会怎么做。"><a href="#Go什么时候发生阻塞?阻塞时,调度器会怎么做。" class="headerlink" title="Go什么时候发生阻塞?阻塞时,调度器会怎么做。"></a>Go什么时候发生阻塞?阻塞时,调度器会怎么做。</h1><ul><li>用于<strong>原子、互斥量或通道</strong>操作导致<code>goroutine</code>阻塞,调度器将把当前阻塞的<code>goroutine</code>从本地运行队列<code>LRQ</code>换出,并重新调度其它<code>goroutine</code>;</li><li>由于<strong>网络请求</strong>和<strong>IO</strong>导致的阻塞,Go提供了网络轮询器(<code>Netpoller</code>)来处理,后台用<code>epoll</code>等技术实现IO多路复用</li></ul><p>其他回答</p><ul><li><strong>channel阻塞</strong>: 当goroutine读写channel发生阻塞时,会调用<code>gopark</code>函数,该G脱离当前的M和P,调度器将新的G放入当前M。</li><li><strong>系统调用</strong>: 当某个G由于系统调用陷入内核态,该P就会脱离当前M,此时P会更新自己的状态为<code>Psyscall</code>,M与G相互绑定,进行系统调用。结束以后,若该P状态还是<code>Psyscall</code>,则直接关联该M和G,否则使用闲置的处理器处理该G。</li><li><strong>系统监控</strong>: 当某个G在P上运行的时间超过10ms时候,或者P处于<code>Psyscall</code>状态过长等情况就会调用<code>retake</code>函数,触发新的调度。</li><li><strong>主动让出</strong>: 由于是协作式调度,该G会主动让出当前的P(通过<code>GoSched</code>),更新状态为<code>Grunnable</code>,该P会调度队列中的G运行。</li></ul><h1 id="如果有一个G一直占用资源怎么办?"><a href="#如果有一个G一直占用资源怎么办?" class="headerlink" title="如果有一个G一直占用资源怎么办?"></a>如果有一个G一直占用资源怎么办?</h1><p>如果有个<code>goroutine</code>一直占用资源,那么GMP模型会<strong>从正常模式转变为饥饿模式</strong>(类似于mutex),允许其它<code>goroutine</code>使用<code>work stealing</code>抢占(禁用自旋锁)。</p><h2 id="什么是work-stealing算法?"><a href="#什么是work-stealing算法?" class="headerlink" title="什么是work stealing算法?"></a>什么是<code>work stealing</code>算法?</h2><p>指,一个线程如果处于空闲状态,则帮其它正在忙的线程分担压力,从全局队列取一个G任务来执行,可以极大提高执行效率。</p><h1 id="Go竞态条件了解吗?"><a href="#Go竞态条件了解吗?" class="headerlink" title="Go竞态条件了解吗?"></a>Go竞态条件了解吗?</h1><blockquote><p>所谓竞态竞争,就是<strong>当两个或以上的goroutine访问相同资源时候,对资源进行读/写</strong>。</p></blockquote><p>比如<code>var a int = 0</code>,有两个协程分别对<code>a+=1</code>,我们发现<strong>最后a不一定为2</strong>.这就是<strong>竞态竞争</strong>。</p><p>通常我们可以用<code>go run -race xx.go</code>来进行检测。</p><blockquote><p>解决方法是,对临界区资源上锁,或者使用原子操作(atomics),原子操作的开销小于上锁。</p></blockquote><h1 id="如果若干个goroutine,有一个panic会怎么做?"><a href="#如果若干个goroutine,有一个panic会怎么做?" class="headerlink" title="如果若干个goroutine,有一个panic会怎么做?"></a>如果若干个goroutine,有一个panic会怎么做?</h1><p>有一个<code>panic</code>,那么剩余<code>goroutine</code>也会退出,程序退出。如果不想程序退出,那么必须通过调用 <code>recover()</code> 方法来捕获 <code>panic</code> 并恢复将要崩掉的程序。</p><h1 id="defer可以捕获goroutine的子goroutine吗?"><a href="#defer可以捕获goroutine的子goroutine吗?" class="headerlink" title="defer可以捕获goroutine的子goroutine吗?"></a><code>defer</code>可以捕获<code>goroutine</code>的子<code>goroutine</code>吗?</h1><blockquote><p>不可以<br>它们处于不同的调度器P中。对于<code>子goroutine</code>,必须通过 <code>recover()</code> 机制来进行恢复,然后结合日志进行打印(或者通过channel传递error),下面是一个例子:</p></blockquote><figure class="highlight golang"><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="comment">// Ping 心跳函数</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Ping</span><span class="params">(ctx context.Context)</span></span> <span class="type">error</span> {</span><br><span class="line"> <span class="comment">// ... code ...</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> r := <span class="built_in">recover</span>(); r != <span class="literal">nil</span> {</span><br><span class="line"> log.Println(ctx, <span class="string">"ping panic: %v, stack: %v"</span>, r, <span class="type">string</span>(debug.Stack()))</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ... code ...</span></span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ... code ...</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="Go解析Tag是怎么实现的?"><a href="#Go解析Tag是怎么实现的?" class="headerlink" title="Go解析Tag是怎么实现的?"></a>Go解析Tag是怎么实现的?</h1><blockquote><p>Go解析tag采用的是<strong>反射</strong>。<br>具体来说使用<code>reflect.ValueOf</code>方法获取其反射值,然后获取其<code>Type</code>属性,之后再通过<code>Field(i)</code>获取第<code>i+1</code>个field,再<code>.Tag</code>获得Tag。<br>反射实现的原理在: <code>src/reflect/type.go</code>中</p></blockquote><h1 id="channel死锁的场景"><a href="#channel死锁的场景" class="headerlink" title="channel死锁的场景"></a><code>channel</code>死锁的场景</h1><p>当一个<code>channel</code>中没有数据,而直接读取时,会发生死锁:</p><figure class="highlight golang"><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">q := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>,<span class="number">2</span>)</span><br><span class="line"><-q</span><br></pre></td></tr></table></figure><p>解决方案是采用<code>select</code>语句,再<code>default</code>放默认处理方式:</p><figure class="highlight golang"><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">q := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>,<span class="number">2</span>)</span><br><span class="line"><span class="keyword">select</span>{</span><br><span class="line"> <span class="keyword">case</span> val:=<-q:</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="说说-atomic底层怎么实现的"><a href="#说说-atomic底层怎么实现的" class="headerlink" title="说说 atomic底层怎么实现的"></a>说说 atomic底层怎么实现的</h1><blockquote><p><code>atomic</code>源码位于<code>sync\atomic</code>。通过阅读源码可知,atomic采用<code>CAS(CompareAndSwap)</code>的方式实现的。<br>所谓<code>CAS</code>就是使用了<code>CPU</code>中的原子性操作。在操作共享变量的时候,<code>CAS</code>不需要对其进行加锁,而是通过类似于乐观锁的方式进行检测,总是假设被操作的值未曾改变(即与旧值相等),并一旦确认这个假设的真实性就立即进行值替换。本质上是不断占用<code>CPU</code>资源来避免加锁的开销。</p></blockquote><h1 id="go的调试-x2F-分析工具用过哪些"><a href="#go的调试-x2F-分析工具用过哪些" class="headerlink" title="go的调试/分析工具用过哪些"></a>go的调试/分析工具用过哪些</h1><ul><li>go的自带工具链相当丰富<ul><li><code>go cover</code>: 测试代码覆盖率</li><li><code>pprof</code>: 用于性能调优,针对cpu,内存和并发;</li><li><code>godoc</code>: 用于生成go文档</li><li><code>race</code>:用于竞争检测</li></ul></li></ul><h1 id="实现使用字符串函数名,调用函数。"><a href="#实现使用字符串函数名,调用函数。" class="headerlink" title="实现使用字符串函数名,调用函数。"></a>实现使用字符串函数名,调用函数。</h1><blockquote><p>采用反射的Call方法实现。</p></blockquote><figure class="highlight golang"><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">type</span> Animal <span class="keyword">struct</span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Animal)</span></span> Eat() {</span><br><span class="line"> fmt.Println(<span class="string">"Eat"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := Animal{}</span><br><span class="line"> reflect.ValueOf(&a).MethodByName(<span class="string">"Eat"</span>).Call([]reflect.Value{})</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>坚持, 量变才会引起质变</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 面试题 III</title>
<link href="http://blog.caoxl.com/2022/10/17/Golang-Interview-III/"/>
<id>http://blog.caoxl.com/2022/10/17/Golang-Interview-III/</id>
<published>2022-10-17T09:17:50.000Z</published>
<updated>2022-10-26T08:58:44.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>在面试题中成长,在笔试题中查漏补缺</p></blockquote><span id="more"></span><h1 id="基础语法"><a href="#基础语法" class="headerlink" title="基础语法"></a>基础语法</h1><h2 id="Q1-和-的区别?"><a href="#Q1-和-的区别?" class="headerlink" title="Q1 = 和 := 的区别?"></a>Q1 <code>=</code> 和 <code>:=</code> 的区别?</h2><blockquote><p><code>:=</code> 声明+赋值<br><code>=</code> 仅赋值</p></blockquote><h2 id="Q2-指针的作用"><a href="#Q2-指针的作用" class="headerlink" title="Q2 指针的作用"></a>Q2 指针的作用</h2><blockquote><p>指针用来保存变量的地址。</p></blockquote><figure class="highlight golang"><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">var</span> x = <span class="number">5</span></span><br><span class="line"><span class="keyword">var</span> p *<span class="type">int</span> = &x</span><br><span class="line">fmt.Printf(<span class="string">"x = %d"</span>, *p) <span class="comment">// x 可以用 *p 访问</span></span><br></pre></td></tr></table></figure><ul><li><code>*</code>运算符: 也称为解引用运算符,用于访问地址中的值。</li><li><code>&</code>运算符: 也称为地址运算符,用于返回变量的地址</li></ul><h2 id="Q3-Go允许多个返回值吗?"><a href="#Q3-Go允许多个返回值吗?" class="headerlink" title="Q3 Go允许多个返回值吗?"></a>Q3 Go允许多个返回值吗?</h2><blockquote><p>允许</p></blockquote><h2 id="Q4-Go有异常类型吗?"><a href="#Q4-Go有异常类型吗?" class="headerlink" title="Q4 Go有异常类型吗?"></a>Q4 Go有异常类型吗?</h2><blockquote><p>Go 没有异常类型,只有错误类型(Error),通常使用返回值来表示异常状态。<br><code>panic</code>支持抛出任意类型的异常(而不仅仅是 <code>error</code> 类型的错误),<code>recover</code> 函数调用的返回值和 <code>panic</code> 函数的输入参数类型一致,它们的函数签名如下:</p></blockquote><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">panic</span><span class="params">(<span class="keyword">interface</span>{})</span></span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">recover</span><span class="params">()</span></span> <span class="keyword">interface</span>{}</span><br></pre></td></tr></table></figure><ul><li>捕获异常转成错误</li></ul><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">foo</span><span class="params">()</span></span> (err <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> r := <span class="built_in">recover</span>(); r != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">switch</span> x := r.(<span class="keyword">type</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="type">string</span>:</span><br><span class="line"> err = errors.New(x)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">error</span>:</span><br><span class="line"> err = x</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> err = fmt.Errorf(<span class="string">"Unknown panic: %v"</span>, r)</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="built_in">panic</span>(<span class="string">"TODO"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Q5-什么是协程(Goroutine)"><a href="#Q5-什么是协程(Goroutine)" class="headerlink" title="Q5 什么是协程(Goroutine)"></a>Q5 什么是协程(Goroutine)</h2><blockquote><p><code>Goroutine</code> 是与其他函数或方法同时运行的函数或方法。<br><code>Goroutines</code> 可以被认为是轻量级的线程。<br>与线程相比,创建 <code>Goroutine</code> 的开销很小。Go应用程序同时运行数千个 <code>Goroutine</code> 是非常常见的做法。</p></blockquote><h2 id="Q6-如何高效地拼接字符串"><a href="#Q6-如何高效地拼接字符串" class="headerlink" title="Q6 如何高效地拼接字符串"></a>Q6 如何高效地拼接字符串</h2><p>Go 语言中,字符串是只读的,也就意味着每次修改操作都会创建一个新的字符串。如果需要拼接多次,应使用 <code>strings.Builder</code>,最小化内存拷贝次数。</p><figure class="highlight golang"><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">var</span> str strings.Builder</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">1000</span>; i++ {</span><br><span class="line"> str.WriteString(<span class="string">"a"</span>)</span><br><span class="line">}</span><br><span class="line">fmt.Println(str.String()) </span><br></pre></td></tr></table></figure><h2 id="Q7-什么是-rune-类型"><a href="#Q7-什么是-rune-类型" class="headerlink" title="Q7 什么是 rune 类型"></a>Q7 什么是 <code>rune</code> 类型</h2><blockquote><p><code>rune</code>是Go语言中一种特殊的数据类型,它是<code>int32</code>的别名,几乎在所有方面等同于<code>int32</code>,用于区分字符值和整数值。<br>补充:golang中的字符有两种,uint8(byte)代表ASCII的一个字符,<code>rune</code>代表一个utf-8字符。</p></blockquote><h2 id="Q8-如何判断map中是否包含某个key?"><a href="#Q8-如何判断map中是否包含某个key?" class="headerlink" title="Q8 如何判断map中是否包含某个key?"></a>Q8 如何判断<code>map</code>中是否包含某个<code>key</code>?</h2><figure class="highlight golang"><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">if</span> val, ok := dict[<span class="string">"foo"</span>]; ok {</span><br><span class="line"> <span class="comment">//do something here</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p><code>dict["foo"]</code> 有 2 个返回值,val 和 ok,如果 ok 等于 true,则说明 <code>dict</code> 包含 key “foo”,val 将被赋予 “foo” 对应的值。</p></blockquote><h2 id="Q9-Go支持默认参数或可选参数吗?"><a href="#Q9-Go支持默认参数或可选参数吗?" class="headerlink" title="Q9 Go支持默认参数或可选参数吗?"></a>Q9 Go支持默认参数或可选参数吗?</h2><blockquote><p><code>Go</code>语言不支持可选参数(Python, PHP 支持),也不支持方法重载(Java支持)。<br>Go支持可变参数</p></blockquote><figure class="highlight golang"><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">func</span> <span class="title">myfunc</span><span class="params">(args ...<span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">for</span> _, arg := <span class="keyword">range</span> args {</span><br><span class="line"> fmt.Println(arg)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>形如<code>...type</code>格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数,它是一个语法糖(<code>syntactic sugar</code>),即这种语法对语言的功能并没有影响,但是更方便程序员使用,通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的可能。</p><p>从内部实现机理上来说,类型<code>...type</code>本质上是一个数组切片,也就是<code>[]type</code>,这也是为什么上面的参数 <code>args</code> 可以用 <code>for</code> 循环来获得每个传入的参数。</p><h2 id="Q10-defer-的执行顺序"><a href="#Q10-defer-的执行顺序" class="headerlink" title="Q10 defer 的执行顺序"></a>Q10 <code>defer</code> 的执行顺序</h2><blockquote><p>多个 <code>defer</code> 语句,遵从<strong>后进先出</strong>(<code>Last In First Out,LIFO</code>)的原则,最后声明的 <code>defer</code> 语句,最先得到执行。<br><code>defer</code> 在 <code>return</code> 语句之后执行,但在函数退出之前,<code>defer</code> 可以修改返回值。</p></blockquote><h2 id="Q11-如何交换2个变量的值?"><a href="#Q11-如何交换2个变量的值?" class="headerlink" title="Q11 如何交换2个变量的值?"></a>Q11 如何交换2个变量的值?</h2><blockquote><p>x, y := y, x</p></blockquote><h2 id="Q12-Go语言tag的用处?"><a href="#Q12-Go语言tag的用处?" class="headerlink" title="Q12 Go语言tag的用处?"></a>Q12 Go语言<code>tag</code>的用处?</h2><blockquote><p><code>tag</code> 可以理解为 <code>struct</code> 字段的注解,可以用来定义字段的一个或多个属性。框架/工具可以通过<strong>反射</strong>获取到某个字段定义的属性,采取相应的处理方式。</p></blockquote><h2 id="Q13-如何判断2个字符串切片(slice-是相等的?"><a href="#Q13-如何判断2个字符串切片(slice-是相等的?" class="headerlink" title="Q13 如何判断2个字符串切片(slice) 是相等的?"></a>Q13 如何判断<code>2</code>个字符串切片(<code>slice</code>) 是相等的?</h2><blockquote><p>go 语言中可以使用反射 <code>reflect.DeepEqual(a, b)</code> 判断 a、b 两个切片是否相等,但是通常不推荐这么做,使用反射非常影响性能。</p></blockquote><p>通常采用的方式如下,遍历比较切片中的每一个元素(注意处理越界的情况)</p><figure class="highlight golang"><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">func</span> <span class="title">StringSliceEqualBCE</span><span class="params">(a, b []<span class="type">string</span>)</span></span> <span class="type">bool</span> {</span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">len</span>(a) != <span class="built_in">len</span>(b) {</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">if</span> (a == <span class="literal">nil</span>) != (b == <span class="literal">nil</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><br><span class="line"> b = b[:<span class="built_in">len</span>(a)]</span><br><span class="line"> <span class="keyword">for</span> i, v := <span class="keyword">range</span> a {</span><br><span class="line"> <span class="keyword">if</span> v != b[i] {</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><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Q14-字符串打印时,-v-和-v和-v-的区别"><a href="#Q14-字符串打印时,-v-和-v和-v-的区别" class="headerlink" title="Q14 字符串打印时,%v 和 %+v和 %#v 的区别"></a>Q14 字符串打印时,<code>%v</code> 和 <code>%+v</code>和 <code>%#v</code> 的区别</h2><blockquote><p><code>%v</code> 和 <code>%+v</code> 和 <code>%#v</code> 都可以用来打印 struct 的值</p></blockquote><ul><li>区别在于: <ul><li><code>%v</code> 仅打印各个字段的值</li><li><code>%+v</code> 还会打印各个字段的名称。</li><li><code>%#v</code> 还会打印结构体的名称</li></ul></li></ul><figure class="highlight golang"><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">type</span> Stu <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"%v\n"</span>, Stu{<span class="string">"Tom"</span>}) <span class="comment">// {Tom}</span></span><br><span class="line"> fmt.Printf(<span class="string">"%+v\n"</span>, Stu{<span class="string">"Tom"</span>}) <span class="comment">// {Name:Tom}</span></span><br><span class="line"> fmt.Printf(<span class="string">"%#v\n"</span>, Stu{<span class="string">"Tom"</span>}) <span class="comment">// main.Stu{Name:"Tom"}</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Q15-Go-语言中如何表示枚举值-enums-?"><a href="#Q15-Go-语言中如何表示枚举值-enums-?" class="headerlink" title="Q15 Go 语言中如何表示枚举值(enums)?"></a>Q15 Go 语言中如何表示枚举值(enums)?</h2><blockquote><p>通常使用常量(const) 来表示枚举值。</p></blockquote><h2 id="Q16-空struct-的用途"><a href="#Q16-空struct-的用途" class="headerlink" title="Q16 空struct{}的用途"></a>Q16 空<code>struct{}</code>的用途</h2><blockquote><p>使用空结构体 <code>struct{}</code> 可以节省内存,一般作为占位符使用,表明这里并不需要一个值。</p></blockquote><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fmt.Println(unsafe.Sizeof(<span class="keyword">struct</span>{}{})) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><p>比如使用 <code>map</code> 表示集合时,只关注 <code>key</code>,<code>value</code> <strong>可以使用 <code>struct{}</code> 作为占位符</strong>。如果使用其他类型作为占位符,例如 <code>int</code>,<code>bool</code>,不仅浪费了内存,而且容易引起歧义。</p><p>再比如,使用信道(channel)控制并发时,我们只是需要一个信号,但并不需要传递值,这个时候,也可以使用 <code>struct{}</code> 代替。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>{}, <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <-ch</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> }()</span><br><span class="line"> ch <- <span class="keyword">struct</span>{}{}</span><br><span class="line"> <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><h2 id="Q17-init-函数是什么时候执行的?"><a href="#Q17-init-函数是什么时候执行的?" class="headerlink" title="Q17 init()函数是什么时候执行的?"></a>Q17 <code>init()</code>函数是什么时候执行的?</h2><blockquote><p><code>init()</code>函数是 Go 程序初始化的一部分。<br><strong>Go程序初始化先于 <code>main</code> 函数</strong>,由 <code>runtime</code> 初始化每个导入的包,初始化顺序不是按照从上到下的导入顺序,而是按照解析的依赖关系,没有依赖的包最先初始化。</p></blockquote><p>一句话总结:<code>import</code> –> <code>const</code> –> <code>var</code> –><code>init()</code> -> <code>main()</code></p><p>每个包首先初始化包作用域的常量和变量(常量优先于变量),然后执行包的<code>init()</code>函数。同一个包,甚至是同一个源文件可以有多个<code>init()</code>函数。<code>init()</code>函数没有入参和返回值,不能被其他函数调用,同一个包内多个<code>init()</code>函数的执行顺序不作保证。</p><h2 id="Q18-Go语言的局部变量分配在栈上还是堆上?"><a href="#Q18-Go语言的局部变量分配在栈上还是堆上?" class="headerlink" title="Q18 Go语言的局部变量分配在栈上还是堆上?"></a>Q18 Go语言的局部变量分配在栈上还是堆上?</h2><blockquote><p>由编译器决定。<br>Go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做<strong>逃逸分析</strong>(escape analysis),当发现变量的作用域没有超出函数范围,就可以在栈上,反之则必须分配在堆上。</p></blockquote><figure class="highlight golang"><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">func</span> <span class="title">foo</span><span class="params">()</span></span> *<span class="type">int</span> {</span><br><span class="line"> v := <span class="number">11</span></span><br><span class="line"> <span class="keyword">return</span> &v</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := foo()</span><br><span class="line"> <span class="built_in">println</span>(*m) <span class="comment">// 11</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>foo()</code>函数中,如果 v 分配在栈上,<code>foo</code> 函数返回时,<code>&v</code>就不存在了,但是这段函数是能够正常运行的。Go 编译器发现 v 的引用脱离了 <code>foo</code> 的作用域,会将其分配在堆上。因此,<code>main</code> 函数中仍能够正常访问该值。</p><h3 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h3><ul><li><code>-gcflags=-m</code></li></ul><figure class="highlight golang"><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">go</span> run -gcflags=-m echo.<span class="keyword">go</span></span><br><span class="line"># command-line-arguments</span><br><span class="line">./echo.<span class="keyword">go</span>:<span class="number">3</span>:<span class="number">6</span>: can inline foo</span><br><span class="line">./echo.<span class="keyword">go</span>:<span class="number">8</span>:<span class="number">6</span>: can inline main</span><br><span class="line">./echo.<span class="keyword">go</span>:<span class="number">9</span>:<span class="number">10</span>: inlining call to foo</span><br><span class="line">./echo.<span class="keyword">go</span>:<span class="number">4</span>:<span class="number">2</span>: moved to heap: v</span><br><span class="line"><span class="number">11</span></span><br></pre></td></tr></table></figure><ul><li><a href="https://geektutu.com/post/hpg-escape-analysis.html">参考 - Go 逃逸分析</a></li></ul><h2 id="Q19-2个interface可以比较吗"><a href="#Q19-2个interface可以比较吗" class="headerlink" title="Q19 2个interface可以比较吗"></a>Q19 2个<code>interface</code>可以比较吗</h2><blockquote><p>可以<br>Go 语言中,<code>interface</code> 的内部实现包含了 2 个字段,类型<code>T</code>和 值<code>V</code>,<code>interface</code> 可以使用<code>==</code>或<code>!=</code>比较。2 个 <code>interface</code> 相等有以下 2 种情况</p></blockquote><ol><li>两个 interface 均等于 nil(此时 V 和 T 都处于 unset 状态)</li><li>类型 T 相同,且对应的值 V 相等。</li></ol><figure class="highlight golang"><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="keyword">type</span> Stu <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> StuInt <span class="keyword">interface</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> stu1, stu2 StuInt = &Stu{<span class="string">"Tom"</span>}, &Stu{<span class="string">"Tom"</span>}</span><br><span class="line"> <span class="keyword">var</span> stu3, stu4 StuInt = Stu{<span class="string">"Tom"</span>}, Stu{<span class="string">"Tom"</span>}</span><br><span class="line"> fmt.Println(stu1 == stu2) <span class="comment">// false</span></span><br><span class="line"> fmt.Println(stu3 == stu4) <span class="comment">// true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>stu1</code>和<code>stu2</code>对应的类型是<code>*Stu</code>,值是 <code>Stu</code> 结构体的地址,两个地址不同,因此结果为 <strong>false</strong>。<br><code>stu3</code>和<code>stu4</code>对应的类型是<code>Stu</code>,值是 <code>Stu</code> 结构体,且各字段相等,因此结果为 <strong>true</strong>。</p><h2 id="Q20-2个nil可能不相等吗?"><a href="#Q20-2个nil可能不相等吗?" class="headerlink" title="Q20 2个nil可能不相等吗?"></a>Q20 2个<code>nil</code>可能不相等吗?</h2><blockquote><p>可能</p></blockquote><blockquote><p>接口(interface) 是对非接口值(例如指针,struct等)的封装,内部实现包含 2 个字段,类型<code>T</code>和 值<code>V</code>。一个接口等于 nil,当且仅当 T 和 V 处于 unset 状态(T=nil,V is unset)。</p></blockquote><ul><li>两个接口值比较时,会先比较 T,再比较 V。</li><li>接口值与非接口值比较时,会先将非接口值尝试转换为接口值,再比较。</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> p *<span class="type">int</span> = <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">interface</span>{} = p</span><br><span class="line"> fmt.Println(i == p) <span class="comment">// true</span></span><br><span class="line"> fmt.Println(p == <span class="literal">nil</span>) <span class="comment">// true</span></span><br><span class="line"> fmt.Println(i == <span class="literal">nil</span>) <span class="comment">// false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面这个例子中,将一个 nil 非接口值 p 赋值给接口 i,此时,i 的内部字段为<code>(T=*int, V=nil)</code>,i 与 p 作比较时,将 p 转换为接口后再比较,因此<code>i == p</code>,p 与 nil 比较,直接比较值,所以<code>p == nil</code>。</p><p>但是当 i 与 nil 比较时,会将 nil 转换为接口<code>(T=nil, V=nil)</code>,与i<code>(T=*int, V=nil)</code>不相等,因此<code>i != nil</code>。</p><p><strong>因此 <code>V</code> 为 <code>nil</code> ,但 <code>T</code> 不为 <code>nil</code> 的接口不等于 nil。</strong></p><h2 id="Q21-简述-Go-语言GC-垃圾回收-的工作原理"><a href="#Q21-简述-Go-语言GC-垃圾回收-的工作原理" class="headerlink" title="Q21 简述 Go 语言GC(垃圾回收)的工作原理"></a>Q21 简述 Go 语言GC(垃圾回收)的工作原理</h2><blockquote><p>最常见的垃圾回收算法有<strong>标记清除</strong>(Mark-Sweep) 和<strong>引用计数</strong>(Reference Count),<strong>Go 语言采用的是标记清除算法</strong>。并在此基础上使用了<strong>三色标记法</strong>和<strong>写屏障技术</strong>,提高了效率。</p></blockquote><p>一次完整的 GC 分为四个阶段:</p><ul><li><ol><li>标记准备(<code>Mark Setup</code>, 需STW), 打开写屏障(<code>Write Barrier</code>)</li></ol></li><li><ol start="2"><li>使用三色标记法去标记(<code>Marking</code>, 并发)</li></ol></li><li><ol start="3"><li>标记结束(<code>Mark Termination</code>, 需STW), 关闭写屏障</li></ol></li><li><ol start="4"><li>清理(<code>Sweeping</code>, 并发)</li></ol></li></ul><h3 id="标记清除"><a href="#标记清除" class="headerlink" title="标记清除"></a>标记清除</h3><p>标记清除收集器是跟踪式垃圾收集器,其执行过程可以分成标记(Mark)和清除(Sweep)两个阶段:</p><ul><li>标记阶段: 从根对象出发查找并标记堆中所有存活的对象;</li><li>清除阶段: 遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表。</li></ul><h3 id="三色标记法"><a href="#三色标记法" class="headerlink" title="三色标记法"></a>三色标记法</h3><p>三色标记算法将程序中的对象分成白色、黑色和灰色三类。</p><ul><li>白色: 不确定对象</li><li>灰色: 存活对象, 子对象待处理</li><li>黑色: 存活对象</li></ul><h2 id="Q22-函数返回局部变量的指针是否安全?"><a href="#Q22-函数返回局部变量的指针是否安全?" class="headerlink" title="Q22 函数返回局部变量的指针是否安全?"></a>Q22 函数返回局部变量的指针是否安全?</h2><blockquote><p>这在 Go 中是安全的,Go 编译器将会对每个局部变量进行逃逸分析。<strong>如果发现局部变量的作用域超出该函数,则不会将内存分配在栈(stack)上,而是分配在堆(heap)上。</strong></p></blockquote><h2 id="Q23-非接口的任意类型T-都能够调用-T的方法吗?反过来呢?"><a href="#Q23-非接口的任意类型T-都能够调用-T的方法吗?反过来呢?" class="headerlink" title="Q23 非接口的任意类型T()都能够调用*T的方法吗?反过来呢?"></a>Q23 非接口的任意类型<code>T()</code>都能够调用<code>*T</code>的方法吗?反过来呢?</h2><blockquote><p>一个T类型的值可以调用为<code>*T</code>类型声明的方法,但是仅当此T的值是<strong>可寻址</strong>(addressable) 的情况下。编译器在调用指针属主方法前,会自动取此T值的地址。因为<strong>不是任何T值都是可寻址的,所以并非任何T值都能够调用为类型<code>*T</code>声明的方法</strong><br>反过来,一个<code>*T</code>类型的值可以调用为类型T声明的方法,这是因为解引用指针总是合法的。事实上,你可以认为对于每一个为类型 <code>T</code> 声明的方法,编译器都会为类型<code>*T</code>自动隐式声明一个同名和同签名的方法。</p></blockquote><h3 id="不可寻址"><a href="#不可寻址" class="headerlink" title="不可寻址"></a>不可寻址</h3><ul><li>字符串中的字节;</li><li>map 对象中的元素(slice 对象中的元素是可寻址的,slice的底层是数组)</li><li>常量</li><li>包级别的函数等</li></ul><p>举一个例子,定义类型 <code>T</code>,并为类型<code>*T</code>声明一个方法<code>hello()</code>,变量 <code>t1</code> 可以调用该方法,但是常量 <code>t2</code> 调用该方法时,会产生编译错误。</p><figure class="highlight golang"><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="keyword">type</span> T <span class="type">string</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *T)</span></span> hello() {</span><br><span class="line"> fmt.Println(<span class="string">"hello"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> t1 T = <span class="string">"ABC"</span></span><br><span class="line"> t1.hello() <span class="comment">// hello</span></span><br><span class="line"> <span class="keyword">const</span> t2 T = <span class="string">"ABC"</span></span><br><span class="line"> t2.hello() <span class="comment">// error: cannot call pointer method on t</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="并发编程"><a href="#并发编程" class="headerlink" title="并发编程"></a>并发编程</h1><h2 id="Q24-无缓冲的channel和有缓冲的channel的区别?"><a href="#Q24-无缓冲的channel和有缓冲的channel的区别?" class="headerlink" title="Q24 无缓冲的channel和有缓冲的channel的区别?"></a>Q24 无缓冲的<code>channel</code>和有缓冲的<code>channel</code>的区别?</h2><blockquote><p>对于无缓冲的 <code>channel</code>,发送方将<strong>阻塞</strong>该信道,直到接收方从该信道接收到数据为止,而接收方也将阻塞该信道,直到发送方将数据发送到该信道中为止。<br>对于有缓存的 <code>channel</code>,发送方在没有空插槽(缓冲区使用完)的情况下阻塞,而接收方在信道为空的情况下阻塞。</p></blockquote><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> st := time.Now()</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">bool</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span> <span class="params">()</span></span> {</span><br><span class="line"> time.Sleep(time.Second * <span class="number">2</span>)</span><br><span class="line"> <-ch</span><br><span class="line"> }()</span><br><span class="line"> ch <- <span class="literal">true</span> <span class="comment">// 无缓冲,发送方阻塞直到接收方接收到数据。</span></span><br><span class="line"> fmt.Printf(<span class="string">"cost %.1f s\n"</span>, time.Now().Sub(st).Seconds())</span><br><span class="line"> time.Sleep(time.Second * <span class="number">5</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> st := time.Now()</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">bool</span>, <span class="number">2</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> time.Sleep(time.Second * <span class="number">2</span>)</span><br><span class="line"> <-ch</span><br><span class="line"> }()</span><br><span class="line"> ch <- <span class="literal">true</span></span><br><span class="line"> ch <- <span class="literal">true</span> <span class="comment">// 缓冲区为 2,发送方不阻塞,继续往下执行</span></span><br><span class="line"> fmt.Printf(<span class="string">"cost %.1f s\n"</span>, time.Now().Sub(st).Seconds()) <span class="comment">// cost 0.0 s</span></span><br><span class="line"> ch <- <span class="literal">true</span> <span class="comment">// 缓冲区使用完,发送方阻塞,2s 后接收方接收到数据,释放一个插槽,继续往下执行</span></span><br><span class="line"> fmt.Printf(<span class="string">"cost %.1f s\n"</span>, time.Now().Sub(st).Seconds()) <span class="comment">// cost 2.0 s</span></span><br><span class="line"> time.Sleep(time.Second * <span class="number">5</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Q25-什么是协程泄露-Goroutine-Leak-?"><a href="#Q25-什么是协程泄露-Goroutine-Leak-?" class="headerlink" title="Q25 什么是协程泄露(Goroutine Leak)?"></a>Q25 什么是协程泄露(<code>Goroutine Leak</code>)?</h2><p>协程泄露是指协程创建后,长时间得不到释放,并且还在不断地创建新的协程,最终导致内存耗尽,程序崩溃。常见的导致协程泄露的场景有以下几种:</p><ul><li>缺少接收器,导致发送阻塞</li></ul><p>这个例子中,每执行一次 query,则启动1000个协程向信道 ch 发送数字 0,但只接收了一次,导致 999 个协程被阻塞,不能退出。</p><figure class="highlight golang"><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">func</span> <span class="title">query</span><span class="params">()</span></span> <span class="type">int</span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">1000</span>; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> { ch <- <span class="number">0</span> }()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <-ch</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">4</span>; i++ {</span><br><span class="line"> query()</span><br><span class="line"> fmt.Printf(<span class="string">"goroutines: %d\n"</span>, runtime.NumGoroutine())</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>缺少发送器,导致接收阻塞</li></ul><blockquote><p>那同样的,如果启动 1000 个协程接收信道的信息,但信道并不会发送那么多次的信息,也会导致接收协程被阻塞,不能退出。</p></blockquote><ul><li>死锁(<code>dead lock</code>)</li></ul><blockquote><p>两个或两个以上的协程在执行过程中,由于竞争资源或者由于彼此通信而造成阻塞,这种情况下,也会导致协程被阻塞,不能退出。</p></blockquote><ul><li>无限循环(<code>infinite loops</code>)</li></ul><p>这个例子中,为了避免网络等问题,采用了无限重试的方式,发送 HTTP 请求,直到获取到数据。那如果 HTTP 服务宕机,永远不可达,导致协程不能退出,发生泄漏。</p><figure class="highlight golang"><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">func</span> <span class="title">request</span><span class="params">(url <span class="type">string</span>, wg *sync.WaitGroup)</span></span> {</span><br><span class="line"> i := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">if</span> _, err := http.Get(url); err == <span class="literal">nil</span> {</span><br><span class="line"> <span class="comment">// write to db</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> i++</span><br><span class="line"> <span class="keyword">if</span> i >= <span class="number">3</span> {</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> time.Sleep(time.Second)</span><br><span class="line"> }</span><br><span class="line"> wg.Done()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">1000</span>; i++ {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> request(fmt.Sprintf(<span class="string">"https://127.0.0.1:8080/%d"</span>, i), &wg)</span><br><span class="line"> }</span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Q26-Go可以限制运行时操作系统线程的数量吗?"><a href="#Q26-Go可以限制运行时操作系统线程的数量吗?" class="headerlink" title="Q26 Go可以限制运行时操作系统线程的数量吗?"></a>Q26 Go可以限制运行时操作系统线程的数量吗?</h2><blockquote><p>可以; 可以使用环境变量 <code>GOMAXPROCS</code> 或 <code>runtime.GOMAXPROCS(num int)</code> 设置</p></blockquote><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">runtime.GOMAXPROCS(<span class="number">1</span>) <span class="comment">// 限制同时执行Go代码的操作系统线程数为 1</span></span><br></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><a href="https://blog.51cto.com/u_15300891/3054041">转载 - Go 语言笔试面试题汇总</a></li><li><a href="https://blog.51cto.com/u_15300891/3053999">转载 - Go 语言笔试面试题(基础语法)</a></li><li><a href="https://blog.51cto.com/lxw1844912514/5260754">转载 - Go 语言笔试面试题(实现原理)</a></li><li><a href="https://blog.51cto.com/lxw1844912514/5291739">转载 - Go 语言笔试面试题(并发编程)</a></li><li><a href="https://jishuin.proginn.com/p/763bfbd67827">Go 面试题 004:Go 有异常类型吗?</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>在面试题中成长,在笔试题中查漏补缺</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 面试题 II</title>
<link href="http://blog.caoxl.com/2022/10/14/Golang-Interview-II/"/>
<id>http://blog.caoxl.com/2022/10/14/Golang-Interview-II/</id>
<published>2022-10-14T09:02:21.000Z</published>
<updated>2022-10-26T08:58:44.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>本篇来点代码问答</p></blockquote><span id="more"></span><h1 id="常量与变量"><a href="#常量与变量" class="headerlink" title="常量与变量"></a>常量与变量</h1><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> (</span><br><span class="line"> a, b = <span class="string">"golang"</span>, <span class="number">100</span></span><br><span class="line"> d, e</span><br><span class="line"> f <span class="type">bool</span> = <span class="literal">true</span></span><br><span class="line"> g</span><br><span class="line"> )</span><br><span class="line"> fmt.Println(d, e, g)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>答案:</strong></li></ul><blockquote><p>golang 100 true<br>在同一个 const group 中,如果常量定义与前一行的定义一致,则可以省略类型和值。编译时,会按照前一行的定义自动补全。即等价于</p></blockquote><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> (</span><br><span class="line"> a, b = <span class="string">"golang"</span>, <span class="number">100</span></span><br><span class="line"> d, e = <span class="string">"golang"</span>, <span class="number">100</span></span><br><span class="line"> f <span class="type">bool</span> = <span class="literal">true</span></span><br><span class="line"> g <span class="type">bool</span> = <span class="literal">true</span></span><br><span class="line"> )</span><br><span class="line"> fmt.Println(d, e, g)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> N = <span class="number">100</span></span><br><span class="line"> <span class="keyword">var</span> x <span class="type">int</span> = N</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> M <span class="type">int32</span> = <span class="number">100</span></span><br><span class="line"> <span class="keyword">var</span> y <span class="type">int</span> = M</span><br><span class="line"> fmt.Println(x, y)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>答案:</strong></li></ul><blockquote><p>编译失败: cannot use M (constant 100 of type int32) as type int in variable declaration</p></blockquote><blockquote><p>Go 语言中,常量分为无类型常量和有类型常量两种,<code>const N = 100</code>,属于无类型常量,赋值给其他变量时,如果字面量能够转换为对应类型的变量,则赋值成功,例如,<code>var x int = N</code>。但是对于有类型的常量 <code>const M int32 = 100</code>,赋值给其他变量时,需要类型匹配才能成功,所以显示地类型转换:<br>var y int = int(M)</p></blockquote><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a <span class="type">int8</span> = <span class="number">-1</span></span><br><span class="line"> <span class="keyword">var</span> b <span class="type">int8</span> = <span class="number">-128</span> / a</span><br><span class="line"> fmt.Println(b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>答案:</strong></li></ul><blockquote><p>-128</p></blockquote><blockquote><p>int8 能表示的数字的范围是 <code>[-2^7, 2^7-1]</code>,即 <code>[-128, 127]</code>。<code>-128</code> 是无类型常量,转换为 <code>int8</code>,再除以变量 <code>-1</code>,结果为 <code>128</code>,常量除以变量,结果是一个变量。变量转换时允许溢出,符号位变为1,转为补码后恰好等于 <code>-128</code>。</p></blockquote><blockquote><p>对于有符号整型,最高位是是符号位,计算机用补码表示负数。补码 = 原码取反加一</p></blockquote><figure class="highlight markdown"><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">-1 : 11111111</span><br><span class="line">00000001(原码) 11111110(取反) 11111111(加一)</span><br><span class="line">-128: </span><br><span class="line">10000000(原码) 01111111(取反) 10000000(加一)</span><br><span class="line"></span><br><span class="line">-1 + 1 = 0</span><br><span class="line">11111111 + 00000001 = 00000000(最高位溢出省略)</span><br><span class="line">-128 + 127 = -1</span><br><span class="line">10000000 + 01111111 = 11111111</span><br></pre></td></tr></table></figure><h1 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h1><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line"> <span class="keyword">if</span> err == <span class="literal">nil</span> {</span><br><span class="line"> err := fmt.Errorf(<span class="string">"err"</span>)</span><br><span class="line"> fmt.Println(<span class="number">1</span>, err)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="number">2</span>, err)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>答案:</strong></li></ul><blockquote><p>1 err</p></blockquote><blockquote><p><code>:=</code> 表示声明并赋值,<code>=</code> 表示仅赋值。<br>变量的作用域是大括号,因此在第一个 if 语句 <code>if err == nil</code> 内部重新声明且赋值了与外部变量同名的局部变量 err。对该局部变量的赋值不会影响到外部的 err。因此第二个 if 语句 <code>if err != nil</code> 不成立。所以只打印了 1 err。</p></blockquote><h1 id="defer-延迟调用"><a href="#defer-延迟调用" class="headerlink" title="defer 延迟调用"></a>defer 延迟调用</h1><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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="keyword">type</span> T <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t T)</span></span> f(n <span class="type">int</span>) T {</span><br><span class="line"> fmt.Print(n)</span><br><span class="line"> <span class="keyword">return</span> t</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> t T</span><br><span class="line"> <span class="keyword">defer</span> t.f(<span class="number">1</span>).f(<span class="number">2</span>)</span><br><span class="line"> fmt.Print(<span class="number">3</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>答案:</strong></li></ul><blockquote><p>132<br><code>defer</code> 延迟调用时,需要保存函数指针和参数,因此链式调用的情况下,除了最后一个函数/方法外的函数/方法都会在调用时直接执行。也就是说 <code>t.f(1)</code> 直接执行,然后执行 <code>fmt.Print(3)</code>,最后函数返回时再执行 <code>.f(2)</code>,因此输出是 <code>132</code>。</p></blockquote><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">f</span><span class="params">(n <span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(n)</span><br><span class="line"> n += <span class="number">100</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> f(<span class="number">1</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>1<br>打印 1 而不是 101。<code>defer</code> 语句执行时,会将需要延迟调用的函数和参数保存起来,也就是说,执行到 <code>defer</code> 时,参数 n(此时等于1) 已经被保存了。因此后面对 n 的改动并不会影响延迟函数调用的结果。</p></blockquote><ul><li>下列代码的输出是:</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> n := <span class="number">1</span></span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(n)</span><br><span class="line"> }()</span><br><span class="line"> n += <span class="number">100</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>101<br>匿名函数没有通过传参的方式将 n 传入,因此匿名函数内的 n 和函数外部的 n 是同一个,延迟执行时,已经被改变为 101。</p></blockquote>]]></content>
<summary type="html">
<blockquote>
<p>本篇来点代码问答</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 再深入 II</title>
<link href="http://blog.caoxl.com/2022/09/29/Golang-Learn-More-II/"/>
<id>http://blog.caoxl.com/2022/09/29/Golang-Learn-More-II/</id>
<published>2022-09-29T04:13:03.000Z</published>
<updated>2022-09-29T08:27:06.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>Golang 新手可能会踩的 50 个坑 下</p></blockquote><span id="more"></span><h1 id="在多行-array、slice、map-语句中缺少-号"><a href="#在多行-array、slice、map-语句中缺少-号" class="headerlink" title="在多行 array、slice、map 语句中缺少 , 号"></a>在多行 array、slice、map 语句中缺少 <code>,</code> 号</h1><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := []<span class="type">int</span> {</span><br><span class="line"> <span class="number">1</span>,</span><br><span class="line"> <span class="number">2</span> <span class="comment">// syntax error: unexpected newline, expecting comma or }</span></span><br><span class="line"> }</span><br><span class="line"> y := []<span class="type">int</span>{<span class="number">1</span>,<span class="number">2</span>,}</span><br><span class="line"> z := []<span class="type">int</span>{<span class="number">1</span>,<span class="number">2</span>}</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>声明语句中 <code>}</code> 折叠到单行后,尾部的 <code>,</code> 不是必需的。</p><h1 id="log-Fatal-和-log-Panic-不只是-log"><a href="#log-Fatal-和-log-Panic-不只是-log" class="headerlink" title="log.Fatal 和 log.Panic 不只是 log"></a><code>log.Fatal</code> 和 <code>log.Panic</code> 不只是 log</h1><p>log 标准库提供了不同的日志记录等级,与其他语言的日志库不同,Go 的 log 包在调用 Fatal*()、Panic*() 时能做更多日志外的事,如中断程序的执行等:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> log.Fatal(<span class="string">"Fatal level log: log entry"</span>) <span class="comment">// 输出信息后,程序终止执行</span></span><br><span class="line"> log.Println(<span class="string">"Nomal level log: log entry"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="对内建数据结构的操作并不是同步的"><a href="#对内建数据结构的操作并不是同步的" class="headerlink" title="对内建数据结构的操作并不是同步的"></a>对内建数据结构的操作并不是同步的</h1><p>尽管 Go 本身有大量的特性来支持并发,但并不保证并发的数据安全,用户需自己保证变量等数据以原子操作更新。</p><p><code>goroutine</code> 和 <code>channel</code> 是进行原子操作的好方法,或使用 <code>"sync"</code> 包中的锁。</p><h1 id="range-迭代-string-得到的值"><a href="#range-迭代-string-得到的值" class="headerlink" title="range 迭代 string 得到的值"></a>range 迭代 string 得到的值</h1><p><code>range</code> 得到的索引是字符值(<code>Unicode point</code> / <code>rune</code>)第一个字节的位置,与其他编程语言不同,这个索引并不直接是字符在字符串中的位置。</p><p>注意一个字符可能占多个 rune,比如法文单词 <code>café</code> 中的 é。操作特殊字符可使用norm 包。</p><p><code>for range</code> 迭代会尝试将 string 翻译为 UTF8 文本,对任何无效的码点都直接使用 <code>0XFFFD rune(�)UNicode</code> 替代字符来表示。如果 string 中有任何非 UTF8 的数据,应将 string 保存为 byte slice 再进行操作。</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := <span class="string">"A\xfe\x02\xff\x04"</span></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> fmt.Printf(<span class="string">"%#x "</span>, v) <span class="comment">// 0x41 0xfffd 0x2 0xfffd 0x4 // 错误</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> []<span class="type">byte</span>(data) {</span><br><span class="line"> fmt.Printf(<span class="string">"%#x "</span>, v) <span class="comment">// 0x41 0xfe 0x2 0xff 0x4 // 正确</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="range-迭代-map"><a href="#range-迭代-map" class="headerlink" title="range 迭代 map"></a>range 迭代 map</h1><p>如果你希望以特定的顺序(如按 key 排序)来迭代 map,要注意每次迭代都可能产生不一样的结果。</p><p>Go 的运行时是有意打乱迭代顺序的,所以你得到的迭代结果可能不一致。但也并不总会打乱,得到连续相同的 5 个迭代结果也是可能的,如:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>{<span class="string">"one"</span>: <span class="number">1</span>, <span class="string">"two"</span>: <span class="number">2</span>, <span class="string">"three"</span>: <span class="number">3</span>, <span class="string">"four"</span>: <span class="number">4</span>}</span><br><span class="line"> <span class="keyword">for</span> k, v := <span class="keyword">range</span> m {</span><br><span class="line"> fmt.Println(k, v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果你去 <a href="https://go.dev/play/">Go Playground</a> 重复运行上边的代码,输出是不会变的,只有你更新代码它才会重新编译。重新编译后迭代顺序是被打乱的:</p><h1 id="switch-中的-fallthrough-语句"><a href="#switch-中的-fallthrough-语句" class="headerlink" title="switch 中的 fallthrough 语句"></a>switch 中的 fallthrough 语句</h1><p><code>switch</code> 语句中的 <code>case</code> 代码块会默认带上 <code>break</code>,但可以使用 <code>fallthrough</code> 来强制执行下一个 <code>case</code> 代码块。</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> isSpace := <span class="function"><span class="keyword">func</span><span class="params">(char <span class="type">byte</span>)</span></span> <span class="type">bool</span> {</span><br><span class="line"> <span class="keyword">switch</span> char {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">' '</span>: <span class="comment">// 空格符会直接 break,返回 false // 和其他语言不一样</span></span><br><span class="line"> <span class="comment">// fallthrough // 返回 true</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'\t'</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">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(isSpace(<span class="string">'\t'</span>)) <span class="comment">// true</span></span><br><span class="line"> fmt.Println(isSpace(<span class="string">' '</span>)) <span class="comment">// false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>不过你可以在 <code>case</code> 代码块末尾使用 <code>fallthrough</code>,强制执行下一个 <code>case</code> 代码块。</p><p>也可以改写 <code>case</code> 为多条件判断:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> isSpace := <span class="function"><span class="keyword">func</span><span class="params">(char <span class="type">byte</span>)</span></span> <span class="type">bool</span> {</span><br><span class="line"> <span class="keyword">switch</span> char {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">' '</span>, <span class="string">'\t'</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">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(isSpace(<span class="string">'\t'</span>)) <span class="comment">// true</span></span><br><span class="line"> fmt.Println(isSpace(<span class="string">' '</span>)) <span class="comment">// true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="自增和自减运算"><a href="#自增和自减运算" class="headerlink" title="自增和自减运算"></a>自增和自减运算</h1><p>很多编程语言都自带前置后置的 <code>++</code>、<code>--</code> 运算。但 Go 特立独行,去掉了前置操作,同时 <code>++</code>、<code>—</code> 只作为运算符而非表达式。</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> i := <span class="number">0</span></span><br><span class="line"> ++i <span class="comment">// syntax error: unexpected ++, expecting }</span></span><br><span class="line"> fmt.Println(data[i++]) <span class="comment">// syntax error: unexpected ++, expecting :</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> i := <span class="number">0</span></span><br><span class="line"> i++</span><br><span class="line"> fmt.Println(data[i]) <span class="comment">// 2</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="按位取反"><a href="#按位取反" class="headerlink" title="按位取反"></a>按位取反</h1><p>很多编程语言使用 <code>~</code> 作为一元按位取反(NOT)操作符,Go 重用 <code>^</code> XOR 操作符来<strong>按位取反</strong>:</p><figure class="highlight golang"><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="comment">// 错误的取反操作</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(~<span class="number">2</span>) <span class="comment">// bitwise complement operator is ^</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> d <span class="type">uint8</span> = <span class="number">2</span></span><br><span class="line"> fmt.Printf(<span class="string">"%08b\n"</span>, d) <span class="comment">// 00000010</span></span><br><span class="line"> fmt.Printf(<span class="string">"%08b\n"</span>, ^d) <span class="comment">// 11111101</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同时 <code>^</code> 也是<strong>按位异或</strong>(XOR)操作符。</p><p>一个操作符能重用两次,是因为一元的 NOT 操作 <code>NOT 0x02</code>,与二元的 XOR 操作 <code>0x22 XOR 0xff</code> 是一致的。</p><h1 id="运算符的优先级"><a href="#运算符的优先级" class="headerlink" title="运算符的优先级"></a>运算符的优先级</h1><p>除了位清除(bit clear)操作符,Go 也有很多和其他语言一样的位操作符,但优先级另当别论。</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"0x2 & 0x2 + 0x4 -> %#x\n"</span>, <span class="number">0x2</span>&<span class="number">0x2</span>+<span class="number">0x4</span>) <span class="comment">// & 优先 +</span></span><br><span class="line"> <span class="comment">//prints: 0x2 & 0x2 + 0x4 -> 0x6</span></span><br><span class="line"> <span class="comment">//Go: (0x2 & 0x2) + 0x4</span></span><br><span class="line"> <span class="comment">//C++: 0x2 & (0x2 + 0x4) -> 0x2</span></span><br><span class="line"></span><br><span class="line"> fmt.Printf(<span class="string">"0x2 + 0x2 << 0x1 -> %#x\n"</span>, <span class="number">0x2</span>+<span class="number">0x2</span><<<span class="number">0x1</span>) <span class="comment">// << 优先 +</span></span><br><span class="line"> <span class="comment">//prints: 0x2 + 0x2 << 0x1 -> 0x6</span></span><br><span class="line"> <span class="comment">//Go: 0x2 + (0x2 << 0x1)</span></span><br><span class="line"> <span class="comment">//C++: (0x2 + 0x2) << 0x1 -> 0x8</span></span><br><span class="line"></span><br><span class="line"> fmt.Printf(<span class="string">"0xf | 0x2 ^ 0x2 -> %#x\n"</span>, <span class="number">0xf</span>|<span class="number">0x2</span>^<span class="number">0x2</span>) <span class="comment">// | 优先 ^</span></span><br><span class="line"> <span class="comment">//prints: 0xf | 0x2 ^ 0x2 -> 0xd</span></span><br><span class="line"> <span class="comment">//Go: (0xf | 0x2) ^ 0x2</span></span><br><span class="line"> <span class="comment">//C++: 0xf | (0x2 ^ 0x2) -> 0xf</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>优先级列表</li></ul><figure class="highlight golang"><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">Precedence Operator</span><br><span class="line"> <span class="number">5</span> * / % << >> & &^</span><br><span class="line"> <span class="number">4</span> + - | ^</span><br><span class="line"> <span class="number">3</span> == != < <= > >=</span><br><span class="line"> <span class="number">2</span> &&</span><br><span class="line"> <span class="number">1</span> ||</span><br></pre></td></tr></table></figure><h1 id="不导出的-struct-字段无法被-encode"><a href="#不导出的-struct-字段无法被-encode" class="headerlink" title="不导出的 struct 字段无法被 encode"></a>不导出的 struct 字段无法被 encode</h1><p>以小写字母开头的字段成员是无法被外部直接访问的,所以 struct 在进行 json、xml、gob 等格式的 encode 操作时,这些私有字段会被忽略,导出时得到零值:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> in := MyData{<span class="number">1</span>, <span class="string">"two"</span>}</span><br><span class="line"> fmt.Printf(<span class="string">"%#v\n"</span>, in) <span class="comment">// main.MyData{One:1, two:"two"}</span></span><br><span class="line"></span><br><span class="line"> encoded, _ := json.Marshal(in)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(encoded)) <span class="comment">// {"One":1} // 私有字段 two 被忽略了</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> out MyData</span><br><span class="line"> json.Unmarshal(encoded, &out)</span><br><span class="line"> fmt.Printf(<span class="string">"%#v\n"</span>, out) <span class="comment">// main.MyData{One:1, two:""}</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="程序退出时还有-goroutine-在执行"><a href="#程序退出时还有-goroutine-在执行" class="headerlink" title="程序退出时还有 goroutine 在执行"></a>程序退出时还有 goroutine 在执行</h1><p>程序默认不等所有 <code>goroutine</code> 都执行完才退出,这点需要特别注意:</p><figure class="highlight golang"><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="comment">// 主程序会直接退出</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> workerCount := <span class="number">2</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < workerCount; i++ {</span><br><span class="line"> <span class="keyword">go</span> doIt(i)</span><br><span class="line"> }</span><br><span class="line"> time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line"> fmt.Println(<span class="string">"all done!"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">doIt</span><span class="params">(workerID <span class="type">int</span>)</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"[%v] is running\n"</span>, workerID)</span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second) <span class="comment">// 模拟 goroutine 正在执行</span></span><br><span class="line"> fmt.Printf(<span class="string">"[%v] is done\n"</span>, workerID)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>常用解决办法:使用 <code>"WaitGroup"</code> 变量,它会让主程序等待所有 <code>goroutine</code> 执行完毕再退出。</p><p>如果你的 <code>goroutine</code> 要做消息的循环处理等耗时操作,可以向它们发送一条 <code>kill</code> 消息来关闭它们。或直接关闭一个它们都等待接收数据的 <code>channel</code>:</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 等待所有 goroutine 执行完毕</span></span><br><span class="line"><span class="comment">// 使用传址方式为 WaitGroup 变量传参</span></span><br><span class="line"><span class="comment">// 使用 channel 关闭 goroutine</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>{})</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">interface</span>{})</span><br><span class="line"></span><br><span class="line"> workerCount := <span class="number">2</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < workerCount; i++ {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> doIt(i, ch, done, &wg) <span class="comment">// wg 传指针,doIt() 内部会改变 wg 的值</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < workerCount; i++ { <span class="comment">// 向 ch 中发送数据,关闭 goroutine</span></span><br><span class="line"> ch <- i</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">close</span>(done)</span><br><span class="line"> wg.Wait()</span><br><span class="line"> <span class="built_in">close</span>(ch)</span><br><span class="line"> fmt.Println(<span class="string">"all done!"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">doIt</span><span class="params">(workerID <span class="type">int</span>, ch <-<span class="keyword">chan</span> <span class="keyword">interface</span>{}, done <-<span class="keyword">chan</span> <span class="keyword">struct</span>{}, wg *sync.WaitGroup)</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"[%v] is running\n"</span>, workerID)</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> m := <-ch:</span><br><span class="line"> fmt.Printf(<span class="string">"[%v] m => %v\n"</span>, workerID, m)</span><br><span class="line"> <span class="keyword">case</span> <-done:</span><br><span class="line"> fmt.Printf(<span class="string">"[%v] is done\n"</span>, workerID)</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><br></pre></td></tr></table></figure><h1 id="向无缓冲的-channel-发送数据,只要-receiver-准备好了就会立刻返回"><a href="#向无缓冲的-channel-发送数据,只要-receiver-准备好了就会立刻返回" class="headerlink" title="向无缓冲的 channel 发送数据,只要 receiver 准备好了就会立刻返回"></a>向无缓冲的 channel 发送数据,只要 receiver 准备好了就会立刻返回</h1><p>只有在数据被 <code>receiver</code> 处理时,<code>sender</code> 才会阻塞。因运行环境而异,在 <code>sender</code> 发送完数据后,<code>receiver</code> 的 <code>goroutine</code> 可能没有足够的时间处理下一个数据。如:</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> m := <span class="keyword">range</span> ch {</span><br><span class="line"> fmt.Println(<span class="string">"Processed:"</span>, m)</span><br><span class="line"> time.Sleep(<span class="number">1</span> * time.Second) <span class="comment">// 模拟需要长时间运行的操作</span></span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> ch <- <span class="string">"cmd.1"</span></span><br><span class="line"> ch <- <span class="string">"cmd.2"</span> <span class="comment">// 不会被接收处理</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="向已关闭的-channel-发送数据会造成-panic"><a href="#向已关闭的-channel-发送数据会造成-panic" class="headerlink" title="向已关闭的 channel 发送数据会造成 panic"></a>向已关闭的 channel 发送数据会造成 panic</h1><p>从已关闭的 channel 接收数据是安全的:</p><p>接收状态值 <code>ok</code> 是 <code>false</code> 时表明 <code>channel</code> 中已没有数据可以接收了。类似的,从有缓冲的 <code>channel</code> 中接收数据,缓存的数据获取完再没有数据可取时,状态值也是 <code>false</code></p><p>向已关闭的 <code>channel</code> 中发送数据会造成 <code>panic</code>:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">3</span>; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(idx <span class="type">int</span>)</span></span> {</span><br><span class="line"> ch <- idx</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<-ch) <span class="comment">// 输出第一个发送的值</span></span><br><span class="line"> <span class="built_in">close</span>(ch) <span class="comment">// 不能关闭,还有其他的 sender</span></span><br><span class="line"> time.Sleep(<span class="number">2</span> * time.Second) <span class="comment">// 模拟做其他的操作</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>针对上边有 bug 的这个例子,可使用一个废弃 channel done 来告诉剩余的 goroutine 无需再向 ch 发送数据。此时 <code><- done</code> 的结果是 <code>{}</code>:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>{})</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">3</span>; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(idx <span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> ch <- (idx + <span class="number">1</span>) * <span class="number">2</span>:</span><br><span class="line"> fmt.Println(idx, <span class="string">"Send result"</span>)</span><br><span class="line"> <span class="keyword">case</span> <-done:</span><br><span class="line"> fmt.Println(idx, <span class="string">"Exiting"</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"> fmt.Println(<span class="string">"Result: "</span>, <-ch)</span><br><span class="line"> <span class="built_in">close</span>(done)</span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="使用了值为-nil-的-channel"><a href="#使用了值为-nil-的-channel" class="headerlink" title="使用了值为 nil 的 channel"></a>使用了值为 nil 的 channel</h1><p>在一个值为 <code>nil</code> 的 <code>channel</code> 上发送和接收数据将永久阻塞:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> ch <span class="keyword">chan</span> <span class="type">int</span> <span class="comment">// 未初始化,值为 nil</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">3</span>; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>)</span></span> {</span><br><span class="line"> ch <- i</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"Result: "</span>, <-ch)</span><br><span class="line"> time.Sleep(<span class="number">2</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>runtime 死锁错误:</p><blockquote><p>fatal error: all goroutines are asleep - deadlock!<br>goroutine 1 [chan receive (nil chan)]</p></blockquote><p>利用这个死锁的特性,可以用在 select 中动态的打开和关闭 case 语句块:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> inCh := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> outCh := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> in <-<span class="keyword">chan</span> <span class="type">int</span> = inCh</span><br><span class="line"> <span class="keyword">var</span> out <span class="keyword">chan</span><- <span class="type">int</span></span><br><span class="line"> <span class="keyword">var</span> val <span class="type">int</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> out <- val:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"--------"</span>)</span><br><span class="line"> out = <span class="literal">nil</span></span><br><span class="line"> in = inCh</span><br><span class="line"> <span class="keyword">case</span> val = <-in:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"++++++++++"</span>)</span><br><span class="line"> out = outCh</span><br><span class="line"> in = <span class="literal">nil</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 class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> r := <span class="keyword">range</span> outCh {</span><br><span class="line"> fmt.Println(<span class="string">"Result: "</span>, r)</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> time.Sleep(<span class="number">0</span>)</span><br><span class="line"> inCh <- <span class="number">1</span></span><br><span class="line"> inCh <- <span class="number">2</span></span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="若函数-receiver-传参是传值方式,则无法修改参数的原有值"><a href="#若函数-receiver-传参是传值方式,则无法修改参数的原有值" class="headerlink" title="若函数 receiver 传参是传值方式,则无法修改参数的原有值"></a>若函数 receiver 传参是传值方式,则无法修改参数的原有值</h1><p>方法 <code>receiver</code> 的参数与一般函数的参数类似:如果声明为值,那方法体得到的是一份参数的值拷贝,此时对参数的任何修改都不会对原有值产生影响。</p><p>除非 <code>receiver</code> 参数是 <code>map</code> 或 <code>slice</code> 类型的变量,并且是以指针方式更新 <code>map</code> 中的字段、<code>slice</code> 中的元素的,才会更新原有值:</p><h1 id="struct、array、slice-和-map-的值比较"><a href="#struct、array、slice-和-map-的值比较" class="headerlink" title="struct、array、slice 和 map 的值比较"></a>struct、array、slice 和 map 的值比较</h1><p>可以使用相等运算符 <code>==</code> 来比较结构体变量,前提是<strong>两个结构体的成员都是可比较的类型</strong>:</p><figure class="highlight golang"><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="keyword">type</span> data <span class="keyword">struct</span> {</span><br><span class="line"> num <span class="type">int</span></span><br><span class="line"> fp <span class="type">float32</span></span><br><span class="line"> <span class="built_in">complex</span> <span class="type">complex64</span></span><br><span class="line"> str <span class="type">string</span></span><br><span class="line"> char <span class="type">rune</span></span><br><span class="line"> yes <span class="type">bool</span></span><br><span class="line"> events <-<span class="keyword">chan</span> <span class="type">string</span></span><br><span class="line"> handler <span class="keyword">interface</span>{}</span><br><span class="line"> ref *<span class="type">byte</span></span><br><span class="line"> raw [<span class="number">10</span>]<span class="type">byte</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v1 := data{}</span><br><span class="line"> v2 := data{}</span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, v1 == v2) <span class="comment">// true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果两个结构体中有任意成员是不可比较的,将会造成编译错误。注意数组成员只有在数组元素可比较时候才可比较。</p><figure class="highlight golang"><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="keyword">type</span> data <span class="keyword">struct</span> {</span><br><span class="line"> num <span class="type">int</span></span><br><span class="line"> checks [<span class="number">10</span>]<span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">bool</span> <span class="comment">// 无法比较</span></span><br><span class="line"> doIt <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">bool</span> <span class="comment">// 无法比较</span></span><br><span class="line"> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span> <span class="comment">// 无法比较</span></span><br><span class="line"> bytes []<span class="type">byte</span> <span class="comment">// 无法比较</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v1 := data{}</span><br><span class="line"> v2 := data{}</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, v1 == v2)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>invalid operation: v1 == v2 (struct containing [10]func() bool cannot be compared)</p></blockquote><p>Go 提供了一些库函数来比较那些无法使用 <code>==</code> 比较的变量,比如使用 <code>"reflect"</code> 包的 <code>DeepEqual()</code> :</p><figure class="highlight golang"><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="comment">// 比较相等运算符无法比较的元素</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v1 := data{}</span><br><span class="line"> v2 := data{}</span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, reflect.DeepEqual(v1, v2)) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"> m1 := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"one"</span>: <span class="string">"a"</span>, <span class="string">"two"</span>: <span class="string">"b"</span>}</span><br><span class="line"> m2 := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"two"</span>: <span class="string">"b"</span>, <span class="string">"one"</span>: <span class="string">"a"</span>}</span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, reflect.DeepEqual(m1, m2)) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"> s1 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> s2 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="comment">// 注意两个 slice 相等,值和顺序必须一致</span></span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, reflect.DeepEqual(s1, s2)) <span class="comment">// true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>注意: <code>DeepEqual()</code> 并不总适合于比较 slice</li></ul><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> str = <span class="string">"one"</span></span><br><span class="line"> <span class="keyword">var</span> in <span class="keyword">interface</span>{} = <span class="string">"one"</span></span><br><span class="line"> fmt.Println(<span class="string">"str == in: "</span>, reflect.DeepEqual(str, in)) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"> v1 := []<span class="type">string</span>{<span class="string">"one"</span>, <span class="string">"two"</span>}</span><br><span class="line"> v2 := []<span class="type">string</span>{<span class="string">"two"</span>, <span class="string">"one"</span>}</span><br><span class="line"> fmt.Println(<span class="string">"v1 == v2: "</span>, reflect.DeepEqual(v1, v2)) <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"> data := <span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">interface</span>{}{</span><br><span class="line"> <span class="string">"code"</span>: <span class="number">200</span>,</span><br><span class="line"> <span class="string">"value"</span>: []<span class="type">string</span>{<span class="string">"one"</span>, <span class="string">"two"</span>},</span><br><span class="line"> }</span><br><span class="line"> encoded, _ := json.Marshal(data)</span><br><span class="line"> <span class="keyword">var</span> decoded <span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line"> json.Unmarshal(encoded, &decoded)</span><br><span class="line"> fmt.Println(<span class="string">"data == decoded: "</span>, reflect.DeepEqual(data, decoded)) <span class="comment">// false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>reflect.DeepEqual()</code> 认为空 <code>slice</code> 与 <code>nil slice</code> 并不相等,但注意 <code>byte.Equal()</code> 会认为二者相等:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> b1 []<span class="type">byte</span> = <span class="literal">nil</span></span><br><span class="line"> b2 := []<span class="type">byte</span>{}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// b1 与 b2 长度相等、有相同的字节序</span></span><br><span class="line"> <span class="comment">// nil 与 slice 在字节上是相同的</span></span><br><span class="line"> fmt.Println(<span class="string">"b1 == b2: "</span>, bytes.Equal(b1, b2)) <span class="comment">// true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="从-panic-中恢复"><a href="#从-panic-中恢复" class="headerlink" title="从 panic 中恢复"></a>从 panic 中恢复</h1><p>在一个 defer 延迟执行的函数中调用 <code>recover()</code> ,它便能捕捉 / 中断 <code>panic</code></p><figure class="highlight golang"><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="comment">// 错误的 recover 调用示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">recover</span>() <span class="comment">// 什么都不会捕捉</span></span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"not good"</span>) <span class="comment">// 发生 panic,主程序退出</span></span><br><span class="line"> <span class="built_in">recover</span>() <span class="comment">// 不会被执行</span></span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"ok"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确的 recover 调用示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"recovered: "</span>, <span class="built_in">recover</span>())</span><br><span class="line"> }()</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"not good"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>从上边可以看出,<code>recover()</code> 仅在 defer 执行的函数中调用才会生效。</p><figure class="highlight golang"><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="comment">// 错误的调用示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> doRecover()</span><br><span class="line"> }()</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"not good"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">doRecover</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"recobered: "</span>, <span class="built_in">recover</span>())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>recobered: panic: not good</p></blockquote><h1 id="在-range-迭代-slice、array、map-时通过更新引用来更新元素"><a href="#在-range-迭代-slice、array、map-时通过更新引用来更新元素" class="headerlink" title="在 range 迭代 slice、array、map 时通过更新引用来更新元素"></a>在 range 迭代 slice、array、map 时通过更新引用来更新元素</h1><p>在 <code>range</code> 迭代中,得到的值其实是元素的一份值拷贝,更新拷贝并不会更改原来的元素,即是拷贝的地址并不是原有元素的地址:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> v *= <span class="number">10</span> <span class="comment">// data 中原有元素是不会被修改的</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"data: "</span>, data) <span class="comment">// data: [1 2 3]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果要修改原有元素的值,应该使用索引直接访问:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="keyword">for</span> i, v := <span class="keyword">range</span> data {</span><br><span class="line"> data[i] = v * <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(<span class="string">"data: "</span>, data) <span class="comment">// data: [10 20 30]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果你的集合保存的是指向值的指针,需稍作修改。依旧需要使用索引访问元素,不过可以使用 range 出来的元素直接更新原有值:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []*<span class="keyword">struct</span>{ num <span class="type">int</span> }{{<span class="number">1</span>}, {<span class="number">2</span>}, {<span class="number">3</span>},}</span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> v.num *= <span class="number">10</span> <span class="comment">// 直接使用指针更新</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(data[<span class="number">0</span>], data[<span class="number">1</span>], data[<span class="number">2</span>]) <span class="comment">// &{10} &{20} &{30}</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="slice-中隐藏的数据"><a href="#slice-中隐藏的数据" class="headerlink" title="slice 中隐藏的数据"></a>slice 中隐藏的数据</h1><p>从 <code>slice</code> 中重新切出新 <code>slice</code> 时,新 <code>slice</code> 会引用原 <code>slice</code> 的底层数组。如果跳了这个坑,程序可能会分配大量的临时 <code>slice</code> 来指向原底层数组的部分数据,将导致难以预料的内存使用。</p><figure class="highlight golang"><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">func</span> <span class="title">get</span><span class="params">()</span></span> []<span class="type">byte</span> {</span><br><span class="line"> raw := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">10000</span>)</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(raw), <span class="built_in">cap</span>(raw), &raw[<span class="number">0</span>]) <span class="comment">// 10000 10000 0xc420080000</span></span><br><span class="line"> <span class="keyword">return</span> raw[:<span class="number">3</span>] <span class="comment">// 重新分配容量为 10000 的 slice</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := get()</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(data), <span class="built_in">cap</span>(data), &data[<span class="number">0</span>]) <span class="comment">// 3 10000 0xc420080000</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以通过拷贝临时 slice 的数据,而不是重新切片来解决:</p><figure class="highlight golang"><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">func</span> <span class="title">get</span><span class="params">()</span></span> (res []<span class="type">byte</span>) {</span><br><span class="line"> raw := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">10000</span>)</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(raw), <span class="built_in">cap</span>(raw), &raw[<span class="number">0</span>]) <span class="comment">// 10000 10000 0xc420080000</span></span><br><span class="line"> res = <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">3</span>)</span><br><span class="line"> <span class="built_in">copy</span>(res, raw[:<span class="number">3</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := get()</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(data), <span class="built_in">cap</span>(data), &data[<span class="number">0</span>]) <span class="comment">// 3 3 0xc4200160b8</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="旧-slice"><a href="#旧-slice" class="headerlink" title="旧 slice"></a>旧 slice</h1><p>当你从一个已存在的 slice 创建新 slice 时,二者的数据指向相同的底层数组。如果你的程序使用这个特性,那需要注意 “旧”(stale) slice 问题。</p><p>某些情况下,向一个 slice 中追加元素而它指向的底层数组容量不足时,将会重新分配一个新数组来存储数据。而其他 slice 还指向原来的旧底层数组。</p><figure class="highlight golang"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 超过容量将重新分配数组来拷贝值、重新存储</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s1 := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(s1), <span class="built_in">cap</span>(s1), s1) <span class="comment">// 3 3 [1 2 3 ]</span></span><br><span class="line"></span><br><span class="line"> s2 := s1[<span class="number">1</span>:]</span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(s2), <span class="built_in">cap</span>(s2), s2) <span class="comment">// 2 2 [2 3]</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> s2 {</span><br><span class="line"> s2[i] += <span class="number">20</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 此时的 s1 与 s2 是指向同一个底层数组的</span></span><br><span class="line"> fmt.Println(s1) <span class="comment">// [1 22 23]</span></span><br><span class="line"> fmt.Println(s2) <span class="comment">// [22 23]</span></span><br><span class="line"></span><br><span class="line"> s2 = <span class="built_in">append</span>(s2, <span class="number">4</span>) <span class="comment">// 向容量为 2 的 s2 中再追加元素,此时将分配新数组来存</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> s2 {</span><br><span class="line"> s2[i] += <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(s1) <span class="comment">// [1 22 23] // 此时的 s1 不再更新,为旧数据</span></span><br><span class="line"> fmt.Println(s2) <span class="comment">// [32 33 14]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="for-语句中的迭代变量与闭包函数"><a href="#for-语句中的迭代变量与闭包函数" class="headerlink" title="for 语句中的迭代变量与闭包函数"></a>for 语句中的迭代变量与闭包函数</h1><p>for 语句中的迭代变量在每次迭代中都会重用,即 <code>for</code> 中创建的闭包函数接收到的参数始终是<strong>同一个变量</strong>,在 <code>goroutine</code> 开始执行时都会得到同一个迭代值:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">string</span>{<span class="string">"one"</span>, <span class="string">"two"</span>, <span class="string">"three"</span>}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(v)</span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line"> <span class="comment">// 输出 three three three</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>最简单的解决方法:无需修改 <code>goroutine</code> 函数,在 <code>for</code> 内部使用局部变量保存迭代值,再传参:</p><figure class="highlight golang"><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"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">string</span>{<span class="string">"one"</span>, <span class="string">"two"</span>, <span class="string">"three"</span>}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> vCopy := v</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(vCopy)</span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line"> <span class="comment">// 输出 one two three</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>另一个解决方法:直接将当前的迭代值以参数形式传递给匿名函数:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> data := []<span class="type">string</span>{<span class="string">"one"</span>, <span class="string">"two"</span>, <span class="string">"three"</span>}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> data {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(in <span class="type">string</span>)</span></span> {</span><br><span class="line"> fmt.Println(in)</span><br><span class="line"> }(v)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line"> <span class="comment">// 输出 one two three</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="defer-函数的参数值"><a href="#defer-函数的参数值" class="headerlink" title="defer 函数的参数值"></a>defer 函数的参数值</h1><p>对 defer 延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值:</p><figure class="highlight golang"><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="comment">// 在 defer 函数中参数会提前求值</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i = <span class="number">1</span></span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(<span class="string">"result: "</span>, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span> { <span class="keyword">return</span> i * <span class="number">2</span> }())</span><br><span class="line"> i++</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>result: 2</p></blockquote><h1 id="defer-函数的执行时机"><a href="#defer-函数的执行时机" class="headerlink" title="defer 函数的执行时机"></a>defer 函数的执行时机</h1><p>对 defer 延迟执行的函数,会在调用它的函数结束时执行,而不是在调用它的语句块结束时执行,注意区分开。</p><p>比如在一个长时间执行的函数里,内部 for 循环中使用 defer 来清理每次迭代产生的资源调用,就会出现问题:</p><figure class="highlight golang"><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="comment">// 命令行参数指定目录名</span></span><br><span class="line"><span class="comment">// 遍历读取目录下的文件</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">len</span>(os.Args) != <span class="number">2</span> {</span><br><span class="line"> os.Exit(<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> dir := os.Args[<span class="number">1</span>]</span><br><span class="line"> start, err := os.Stat(dir)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> || !start.IsDir() {</span><br><span class="line"> os.Exit(<span class="number">2</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> targets []<span class="type">string</span></span><br><span class="line"> filepath.Walk(dir, <span class="function"><span class="keyword">func</span><span class="params">(fPath <span class="type">string</span>, fInfo os.FileInfo, err <span class="type">error</span>)</span></span> <span class="type">error</span> {</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> err</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> !fInfo.Mode().IsRegular() {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> targets = <span class="built_in">append</span>(targets, fPath)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, target := <span class="keyword">range</span> targets {</span><br><span class="line"> f, err := os.Open(target)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"bad target:"</span>, target, <span class="string">"error:"</span>, err) <span class="comment">//error:too many open files</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">defer</span> f.Close() <span class="comment">// 在每次 for 语句块结束时,不会关闭文件资源</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用 f 资源</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>解决办法:defer 延迟执行的函数写入匿名函数中:</p><figure class="highlight golang"><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="comment">// 目录遍历正常</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> _, target := <span class="keyword">range</span> targets {</span><br><span class="line"> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> f, err := os.Open(target)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"bad target:"</span>, target, <span class="string">"error:"</span>, err)</span><br><span class="line"> <span class="keyword">return</span> <span class="comment">// 在匿名函数内使用 return 代替 break 即可</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">defer</span> f.Close() <span class="comment">// 匿名函数执行结束,调用关闭文件资源</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用 f 资源</span></span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当然你也可以去掉 <code>defer</code>,在文件资源使用完毕后,直接调用 <code>f.Close()</code> 来关闭。</p><h1 id="失败的类型断言"><a href="#失败的类型断言" class="headerlink" title="失败的类型断言"></a>失败的类型断言</h1><p>在类型断言语句中,断言失败则会返回目标类型的“零值”,断言变量与原来变量混用可能出现异常情况:</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> data <span class="keyword">interface</span>{} = <span class="string">"great"</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// data 混用</span></span><br><span class="line"> <span class="keyword">if</span> data, ok := data.(<span class="type">int</span>); ok {</span><br><span class="line"> fmt.Println(<span class="string">"[is an int], data: "</span>, data)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"[not an int], data: "</span>, data) <span class="comment">// [not an int], data: 0</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> data <span class="keyword">interface</span>{} = <span class="string">"great"</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> res, ok := data.(<span class="type">int</span>); ok {</span><br><span class="line"> fmt.Println(<span class="string">"[is an int], data: "</span>, res)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Println(<span class="string">"[not an int], data: "</span>, data) <span class="comment">// [not an int], data: great</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="堆栈变量"><a href="#堆栈变量" class="headerlink" title="堆栈变量"></a>堆栈变量</h1><p>你并不总是清楚你的变量是分配到了堆还是栈。</p><p>在 <code>C++</code> 中使用 <code>new</code> 创建的变量总是分配到堆内存上的,但在 Go 中即使使用 <code>new()</code>、<code>make()</code> 来创建变量,变量为内存分配位置依旧归 <code>Go</code> 编译器管。</p><p>Go 编译器会根据变量的大小及其 <code>"escape analysis"</code> 的结果来决定变量的存储位置,故能准确返回本地变量的地址,这在 C/C++ 中是不行的。</p><p>在 <code>go build</code> 或 <code>go run</code> 时,加入 <code>-m</code> 参数,能准确分析程序的变量分配位置:</p>]]></content>
<summary type="html">
<blockquote>
<p>Golang 新手可能会踩的 50 个坑 下</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 再深入 I</title>
<link href="http://blog.caoxl.com/2022/09/28/Golang-Learn-More-I/"/>
<id>http://blog.caoxl.com/2022/09/28/Golang-Learn-More-I/</id>
<published>2022-09-28T10:10:16.000Z</published>
<updated>2022-09-29T04:13:59.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>Golang 新手可能会踩的 50 个坑 上</p></blockquote><span id="more"></span><h1 id="左大括号-不能单独放一行"><a href="#左大括号-不能单独放一行" class="headerlink" title="左大括号 { 不能单独放一行"></a>左大括号 <code>{</code> 不能单独放一行</h1><p>在其他大多数语言中,<code>{ </code>的位置你自行决定。Go 比较特别,遵守分号注入规则(automatic semicolon injection):编译器会在每行代码尾部特定分隔符后加 <code>;</code> 来分隔多条语句,比如会在 <code>)</code> 后加分号:</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line">{</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"hello world"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等效于</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>; <span class="comment">// 无函数体</span></span><br><span class="line">{</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"hello world"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"hello world"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="未使用的变量"><a href="#未使用的变量" class="headerlink" title="未使用的变量"></a>未使用的变量</h1><p>如果在函数体代码中有未使用的变量,则无法通过编译,不过全局变量声明但不使用是可以的。</p><p>即使变量声明后为变量赋值,依旧无法通过编译,需在某处使用它:</p><figure class="highlight golang"><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">// 错误示例</span></span><br><span class="line"><span class="keyword">var</span> gvar <span class="type">int</span> <span class="comment">// 全局变量,声明不使用也可以</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> one <span class="type">int</span> <span class="comment">// error: one declared and not used</span></span><br><span class="line"> two := <span class="number">2</span> <span class="comment">// error: two declared and not used</span></span><br><span class="line"> <span class="keyword">var</span> three <span class="type">int</span> <span class="comment">// error: three declared and not used</span></span><br><span class="line"> three = <span class="number">3</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="comment">// 可以直接注释或移除未使用的变量</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> one <span class="type">int</span></span><br><span class="line"> _ = one</span><br><span class="line"></span><br><span class="line"> two := <span class="number">2</span></span><br><span class="line"> <span class="built_in">println</span>(two)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> three <span class="type">int</span></span><br><span class="line"> one = three</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> four <span class="type">int</span></span><br><span class="line"> four = four</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="未使用的-import"><a href="#未使用的-import" class="headerlink" title="未使用的 import"></a>未使用的 import</h1><p>如果你 <code>import</code> 一个包,但包中的变量、函数、接口和结构体一个都没有用到的话,将编译失败。</p><p>可以使用 <code>_</code> 下划线符号作为别名来忽略导入的包,从而避免编译错误,这只会执行 <code>package</code> 的 <code>init()</code></p><h1 id="简短声明的变量只能在函数内部使用"><a href="#简短声明的变量只能在函数内部使用" class="headerlink" title="简短声明的变量只能在函数内部使用"></a>简短声明的变量只能在函数内部使用</h1><figure class="highlight golang"><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">myvar := <span class="number">1</span> <span class="comment">// syntax error: non-declaration statement outside function body</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="keyword">var</span> myvar = <span class="number">1</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="使用简短声明来重复声明变量"><a href="#使用简短声明来重复声明变量" class="headerlink" title="使用简短声明来重复声明变量"></a>使用简短声明来重复声明变量</h1><p>不能用简短声明方式来单独为一个变量重复声明, := 左侧至少有一个新变量,才允许多变量的重复声明:</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> one := <span class="number">0</span></span><br><span class="line"> one := <span class="number">1</span> <span class="comment">// error: no new variables on left side of :=</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> one := <span class="number">0</span></span><br><span class="line"> one, two := <span class="number">1</span>, <span class="number">2</span> <span class="comment">// two 是新变量,允许 one 的重复声明。比如 error 处理经常用同名变量 err</span></span><br><span class="line"> one, two = two, one <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><code>struct</code> 的变量字段不能使用 <code>:=</code> 来赋值以使用预定义的变量来避免解决:</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="keyword">type</span> info <span class="keyword">struct</span> {</span><br><span class="line"> result <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">work</span><span class="params">()</span></span> (<span class="type">int</span>, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">3</span>, <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> data info</span><br><span class="line"> data.result, err := work() <span class="comment">// error: non-name data.result on left side of :=</span></span><br><span class="line"> fmt.Printf(<span class="string">"info: %+v\n"</span>, data)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> data info</span><br><span class="line"> <span class="keyword">var</span> err <span class="type">error</span> <span class="comment">// err 需要预声明</span></span><br><span class="line"></span><br><span class="line"> data.result, err = work()</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(err)</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"> fmt.Printf(<span class="string">"info: %+v\n"</span>, data)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="不小心覆盖了变量"><a href="#不小心覆盖了变量" class="headerlink" title="不小心覆盖了变量"></a>不小心覆盖了变量</h1><p>对从动态语言转过来的开发者来说,简短声明很好用,这可能会让人误会 <code>:=</code> 是一个赋值操作符。</p><p>如果你在新的代码块中像下边这样误用了 <code>:=</code>,编译不会报错,但是变量不会按你的预期工作:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="number">1</span></span><br><span class="line"> <span class="built_in">println</span>(x) <span class="comment">// 1</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">println</span>(x) <span class="comment">// 1</span></span><br><span class="line"> x := <span class="number">2</span></span><br><span class="line"> <span class="built_in">println</span>(x) <span class="comment">// 2 // 新的 x 变量的作用域只在代码块内部</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">println</span>(x) <span class="comment">// 1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这是 Go 开发者常犯的错,而且不易被发现。</p><p>可使用 <code>vet</code> 工具来诊断这种变量覆盖,Go 默认不做覆盖检查,添加 <code>-shadow</code> 选项来启用:</p><figure class="highlight golang"><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">go</span> tool vet -shadow main.<span class="keyword">go</span></span><br><span class="line">main.<span class="keyword">go</span>:<span class="number">9</span>: declaration of <span class="string">"x"</span> shadows declaration at main.<span class="keyword">go</span>:<span class="number">5</span></span><br></pre></td></tr></table></figure><p>注意 <code>vet</code> 不会报告全部被覆盖的变量,可以使用 <code>go-nyet</code> 来做进一步的检测:</p><figure class="highlight golang"><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">> $GOPATH/bin/<span class="keyword">go</span>-nyet main.<span class="keyword">go</span></span><br><span class="line">main.<span class="keyword">go</span>:<span class="number">10</span>:<span class="number">3</span>:Shadowing variable <span class="string">`x`</span></span><br></pre></td></tr></table></figure><h1 id="显式类型的变量无法使用-nil-来初始化"><a href="#显式类型的变量无法使用-nil-来初始化" class="headerlink" title="显式类型的变量无法使用 nil 来初始化"></a>显式类型的变量无法使用 nil 来初始化</h1><p><code>nil</code> 是 <code>interface</code>、<code>function</code>、<code>pointer</code>、<code>map</code>、<code>slice</code> 和 channel 类型变量的默认初始值。但声明时不指定类型,编译器也无法推断出变量的具体类型。</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x = <span class="literal">nil</span> <span class="comment">// error: use of untyped nil</span></span><br><span class="line"> _ = x</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x <span class="keyword">interface</span>{} = <span class="literal">nil</span></span><br><span class="line"> _ = x</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="直接使用值为-nil-的-slice、map"><a href="#直接使用值为-nil-的-slice、map" class="headerlink" title="直接使用值为 nil 的 slice、map"></a>直接使用值为 nil 的 slice、map</h1><figure class="highlight golang"><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="comment">// map 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span></span><br><span class="line"> m[<span class="string">"one"</span>] = <span class="number">1</span> <span class="comment">// error: panic: assignment to entry in nil map</span></span><br><span class="line"> <span class="comment">// m := make(map[string]int) // map 的正确声明,分配了实际的内存</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// slice 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> s []<span class="type">int</span></span><br><span class="line"> s = <span class="built_in">append</span>(s, <span class="number">1</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="map-容量"><a href="#map-容量" class="headerlink" title="map 容量"></a>map 容量</h1><p>在创建 map 类型的变量时可以指定容量,但不能像 <code>slice</code> 一样使用 <code>cap()</code> 来检测分配空间的大小:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>, <span class="number">99</span>)</span><br><span class="line"> <span class="built_in">println</span>(<span class="built_in">cap</span>(m)) <span class="comment">// error: invalid argument m1 (type map[string]int) for cap</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="string-类型的变量值不能为-nil"><a href="#string-类型的变量值不能为-nil" class="headerlink" title="string 类型的变量值不能为 nil"></a>string 类型的变量值不能为 nil</h1><p>对那些喜欢用 <code>nil</code> 初始化字符串的人来说,这就是坑:</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> s <span class="type">string</span> = <span class="literal">nil</span> <span class="comment">// cannot use nil as type string in assignment</span></span><br><span class="line"> <span class="keyword">if</span> s == <span class="literal">nil</span> { <span class="comment">// invalid operation: s == nil (mismatched types string and nil)</span></span><br><span class="line"> s = <span class="string">"default"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> s <span class="type">string</span> <span class="comment">// 字符串类型的零值是空串 ""</span></span><br><span class="line"> <span class="keyword">if</span> s == <span class="string">""</span> {</span><br><span class="line"> s = <span class="string">"default"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="Array-类型的值作为函数参数"><a href="#Array-类型的值作为函数参数" class="headerlink" title="Array 类型的值作为函数参数"></a>Array 类型的值作为函数参数</h1><p>在 C/C++ 中,数组(名)是指针。将数组作为参数传进函数时,相当于传递了数组内存地址的引用,在函数内部会改变该数组的值。</p><p>在 Go 中,数组是值。作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的:</p><figure class="highlight golang"><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="comment">// 数组使用值拷贝传参</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := [<span class="number">3</span>]<span class="type">int</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 class="function"><span class="keyword">func</span><span class="params">(arr [3]<span class="type">int</span>)</span></span> {</span><br><span class="line"> arr[<span class="number">0</span>] = <span class="number">7</span></span><br><span class="line"> fmt.Println(arr) <span class="comment">// [7 2 3]</span></span><br><span class="line"> }(x)</span><br><span class="line"> fmt.Println(x) <span class="comment">// [1 2 3] // 并不是你以为的 [7 2 3]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果想修改参数数组:</p><ul><li>接传递指向这个数组的指针类型:</li></ul><figure class="highlight golang"><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="comment">// 传址会修改原数据</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := [<span class="number">3</span>]<span class="type">int</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 class="function"><span class="keyword">func</span><span class="params">(arr *[3]<span class="type">int</span>)</span></span> {</span><br><span class="line"> (*arr)[<span class="number">0</span>] = <span class="number">7</span></span><br><span class="line"> fmt.Println(arr) <span class="comment">// &[7 2 3]</span></span><br><span class="line"> }(&x)</span><br><span class="line"> fmt.Println(x) <span class="comment">// [7 2 3]</span></span><br><span class="line">} </span><br></pre></td></tr></table></figure><ul><li>直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array)</li></ul><figure class="highlight golang"><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">// 会修改 slice 的底层 array,从而修改 slice</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> <span class="function"><span class="keyword">func</span><span class="params">(arr []<span class="type">int</span>)</span></span> {</span><br><span class="line"> arr[<span class="number">0</span>] = <span class="number">7</span></span><br><span class="line"> fmt.Println(x) <span class="comment">// [7 2 3]</span></span><br><span class="line"> }(x)</span><br><span class="line"> fmt.Println(x) <span class="comment">// [7 2 3]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="range-遍历-slice-和-array-时混淆了返回值"><a href="#range-遍历-slice-和-array-时混淆了返回值" class="headerlink" title="range 遍历 slice 和 array 时混淆了返回值"></a>range 遍历 slice 和 array 时混淆了返回值</h1><p>与其他编程语言中的 <code>for-in</code> 、<code>foreach</code> 遍历语句不同,Go 中的 <code>range</code> 在遍历时会生成 2 个值,第一个是元素索引,第二个是元素的值:</p><figure class="highlight golang"><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="comment">// 错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := []<span class="type">string</span>{<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>}</span><br><span class="line"> <span class="keyword">for</span> v := <span class="keyword">range</span> x {</span><br><span class="line"> fmt.Println(v) <span class="comment">// 0 1 2</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := []<span class="type">string</span>{<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>}</span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> x { <span class="comment">// 使用 _ 丢弃索引</span></span><br><span class="line"> fmt.Println(v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="slice-和-array-其实是一维数据"><a href="#slice-和-array-其实是一维数据" class="headerlink" title="slice 和 array 其实是一维数据"></a>slice 和 array 其实是一维数据</h1><p>看起来 Go 支持多维的 array 和 slice,可以创建数组的数组、切片的切片,但其实并不是。</p><p>对依赖动态计算多维数组值的应用来说,就性能和复杂度而言,用 Go 实现的效果并不理想。</p><p>可以使用原始的一维数组、“独立“ 的切片、“共享底层数组”的切片来创建动态的多维数组。</p><h1 id="访问-map-中不存在的-key"><a href="#访问-map-中不存在的-key" class="headerlink" title="访问 map 中不存在的 key"></a>访问 map 中不存在的 key</h1><p>和其他编程语言类似,如果访问了 map 中不存在的 key 则希望能返回 nil,比如在 PHP 中:</p><p>Go 则会返回元素对应数据类型的零值,比如 <code>nil</code>、<code>''</code> 、<code>false</code> 和 <code>0</code>,取值操作总有值返回,故不能通过取出来的值来判断 key 是不是在 map 中。</p><p>检查 key 是否存在可以用 map 直接访问,检查返回的第二个参数即可:</p><figure class="highlight golang"><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="comment">// 错误的 key 检测方式</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"one"</span>: <span class="string">"2"</span>, <span class="string">"two"</span>: <span class="string">""</span>, <span class="string">"three"</span>: <span class="string">"3"</span>}</span><br><span class="line"> <span class="keyword">if</span> v := x[<span class="string">"two"</span>]; v == <span class="string">""</span> {</span><br><span class="line"> fmt.Println(<span class="string">"key two is no entry"</span>) <span class="comment">// 键 two 存不存在都会返回的空字符串</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>{<span class="string">"one"</span>: <span class="string">"2"</span>, <span class="string">"two"</span>: <span class="string">""</span>, <span class="string">"three"</span>: <span class="string">"3"</span>}</span><br><span class="line"> <span class="keyword">if</span> _, ok := x[<span class="string">"two"</span>]; !ok {</span><br><span class="line"> fmt.Println(<span class="string">"key two is no entry"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="string-类型的值是常量,不可更改"><a href="#string-类型的值是常量,不可更改" class="headerlink" title="string 类型的值是常量,不可更改"></a>string 类型的值是常量,不可更改</h1><p>尝试使用索引遍历字符串,来更新字符串中的个别字符,是不允许的。</p><p><code>string</code> 类型的值是只读的二进制 <code>byte slice</code>,如果真要修改字符串中的字符,将 <code>string</code> 转为 <code>[]byte</code> 修改后,再转为 <code>string</code> 即可:</p><figure class="highlight golang"><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="comment">// 修改字符串的错误示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="string">"text"</span></span><br><span class="line"> x[<span class="number">0</span>] = <span class="string">"T"</span> <span class="comment">// error: cannot assign to x[0]</span></span><br><span class="line"> fmt.Println(x)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改示例</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="string">"text"</span></span><br><span class="line"> xBytes := []<span class="type">byte</span>(x)</span><br><span class="line"> xBytes[<span class="number">0</span>] = <span class="string">'T'</span> <span class="comment">// 注意此时的 T 是 rune 类型</span></span><br><span class="line"> x = <span class="type">string</span>(xBytes)</span><br><span class="line"> fmt.Println(x) <span class="comment">// Text</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意: 上边的示例并不是更新字符串的正确姿势,<strong>因为一个 UTF8 编码的字符可能会占多个字节,比如汉字就需要 3~4 个字节来存储,此时更新其中的一个字节是错误的</strong>。</p><p>更新字串的正确姿势:将 <code>string</code> 转为 <code>rune slice</code>(此时 1 个 rune 可能占多个 byte),直接更新 <code>rune</code> 中的字符</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="string">"text"</span></span><br><span class="line"> xRunes := []<span class="type">rune</span>(x)</span><br><span class="line"> xRunes[<span class="number">0</span>] = <span class="string">'我'</span></span><br><span class="line"> x = <span class="type">string</span>(xRunes)</span><br><span class="line"> fmt.Println(x) <span class="comment">// 我ext</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="string-与-byte-slice-之间的转换"><a href="#string-与-byte-slice-之间的转换" class="headerlink" title="string 与 byte slice 之间的转换"></a>string 与 byte slice 之间的转换</h1><p>当进行 string 和 byte slice 相互转换时,参与转换的是拷贝的原始值。这种转换的过程,与其他编程语的强制类型转换操作不同,也和新 slice 与旧 slice 共享底层数组不同。</p><p>Go 在 string 与 byte slice 相互转换上优化了两点,避免了额外的内存分配:</p><ul><li>在 <code>map[string]</code> 中查找 <code>key</code> 时,使用了对应的 <code>[]byte</code>,避免做 <code>m[string(key)]</code> 的内存分配</li><li>使用 <code>for range</code> 迭代 <code>string</code> 转换为 <code>[]byte</code> 的迭代:<code>for i,v := range []byte(str) {...}</code></li></ul><h1 id="string-与索引操作符"><a href="#string-与索引操作符" class="headerlink" title="string 与索引操作符"></a>string 与索引操作符</h1><p>对字符串用索引访问返回的不是字符,而是一个 <code>byte</code> 值。</p><p>这种处理方式和其他语言一样,比如 PHP 中:</p><figure class="highlight plaintext"><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">> php -r '$name="中文"; var_dump($name);' # "中文" 占用 6 个字节</span><br><span class="line">string(6) "中文"</span><br><span class="line"></span><br><span class="line">> php -r '$name="中文"; var_dump($name[0]);' # 把第一个字节当做 Unicode 字符读取,显示 U+FFFD</span><br><span class="line">string(1) "�"</span><br><span class="line"></span><br><span class="line">> php -r '$name="中文"; var_dump($name[0].$name[1].$name[2]);'</span><br><span class="line">string(3) "中"</span><br></pre></td></tr></table></figure><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> x := <span class="string">"ascii"</span></span><br><span class="line"> fmt.Println(x[<span class="number">0</span>]) <span class="comment">// 97</span></span><br><span class="line"> fmt.Printf(<span class="string">"%T\n"</span>, x[<span class="number">0</span>]) <span class="comment">// uint8</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果需要使用 <code>for range</code> 迭代访问字符串中的字符(<code>unicode code point</code> / <code>rune</code>),标准库中有 <code>"unicode/utf8"</code> 包来做 UTF8 的相关解码编码。另外 <code>utf8string</code> 也有像 <code>func (s *String) At(i int) rune</code> 等很方便的库函数。</p><h1 id="字符串并不都是-UTF8-文本"><a href="#字符串并不都是-UTF8-文本" class="headerlink" title="字符串并不都是 UTF8 文本"></a>字符串并不都是 UTF8 文本</h1><p>string 的值不必是 UTF8 文本,可以包含任意的值。只有字符串是文字字面值时才是 UTF8 文本,字串可以通过转义来包含其他数据。</p><p>判断字符串是否是 UTF8 文本,可使用 <code>"unicode/utf8"</code> 包中的 <code>ValidString()</code> 函数:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> str1 := <span class="string">"ABC"</span></span><br><span class="line"> fmt.Println(utf8.ValidString(str1)) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"> str2 := <span class="string">"A\xfeC"</span></span><br><span class="line"> fmt.Println(utf8.ValidString(str2)) <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"> str3 := <span class="string">"A\\xfeC"</span></span><br><span class="line"> fmt.Println(utf8.ValidString(str3)) <span class="comment">// true // 把转义字符转义成字面值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="字符串的长度"><a href="#字符串的长度" class="headerlink" title="字符串的长度"></a>字符串的长度</h1><p>在 Go 中:</p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> char := <span class="string">"♥"</span></span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(char)) <span class="comment">// 3</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Go 的内建函数 <code>len()</code> 返回的是字符串的 <code>byte</code> 数量,而不是像 Python 中那样是计算 <code>Unicode</code> 字符数。</p><p>如果要得到字符串的字符数,可使用 <code>"unicode/utf8"</code> 包中的 <code>RuneCountInString(str string) (n int)</code></p><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> char := <span class="string">"♥"</span></span><br><span class="line"> fmt.Println(utf8.RuneCountInString(char)) <span class="comment">// 1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意:<code>RuneCountInString</code> 并不总是返回我们看到的字符数,因为有的字符会占用 2 个 <code>rune</code>:</p><figure class="highlight golang"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> char := <span class="string">"é"</span></span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(char)) <span class="comment">// 3</span></span><br><span class="line"> fmt.Println(utf8.RuneCountInString(char)) <span class="comment">// 2</span></span><br><span class="line"> fmt.Println(<span class="string">"cafe\u0301"</span>) <span class="comment">// 法文的 cafe,实际上是两个 rune 的组合</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>Golang 新手可能会踩的 50 个坑 上</p>
</blockquote>
</summary>
<category term="Golang" scheme="http://blog.caoxl.com/categories/Golang/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>Golang 面试题 I</title>
<link href="http://blog.caoxl.com/2022/09/28/Golang-Interview-I/"/>
<id>http://blog.caoxl.com/2022/09/28/Golang-Interview-I/</id>
<published>2022-09-28T02:42:21.000Z</published>
<updated>2022-10-26T08:58:44.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>成为Go开发工作者已经2个多月了, 是时候通过一些面试题笔试题提升自己了</p></blockquote><span id="more"></span><h1 id="结构体是否可以直接寻地址"><a href="#结构体是否可以直接寻地址" class="headerlink" title="结构体是否可以直接寻地址"></a>结构体是否可以直接寻地址</h1><h2 id="什么叫可寻址"><a href="#什么叫可寻址" class="headerlink" title="什么叫可寻址"></a>什么叫可寻址</h2><blockquote><p>可直接使用<code>&</code>操作符取地址的对象,就是可寻址</p></blockquote><h2 id="哪些是可以寻址的"><a href="#哪些是可以寻址的" class="headerlink" title="哪些是可以寻址的"></a>哪些是可以寻址的</h2><h3 id="变量-amp-x"><a href="#变量-amp-x" class="headerlink" title="变量 &x"></a>变量 <code>&x</code></h3><figure class="highlight golang"><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">name := <span class="string">"caoxl"</span></span><br><span class="line">fmt.Println(&name)</span><br></pre></td></tr></table></figure><h3 id="指针-amp-x"><a href="#指针-amp-x" class="headerlink" title="指针 &*x"></a>指针 <code>&*x</code></h3><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"unsafe"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(unsafe.Pointer(&person{<span class="string">"caoxl"</span>}))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> person <span class="keyword">struct</span> {</span><br><span class="line"> name <span class="type">string</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="数组元素索引-amp-a-0"><a href="#数组元素索引-amp-a-0" class="headerlink" title="数组元素索引 &a[0]"></a>数组元素索引 <code>&a[0]</code></h3><figure class="highlight golang"><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">a := [<span class="number">3</span>]<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line">fmt.Printf(<span class="string">"%p\n"</span>, &a[<span class="number">0</span>])</span><br></pre></td></tr></table></figure><h3 id="切片"><a href="#切片" class="headerlink" title="切片"></a>切片</h3><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fmt.Println([]<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}[<span class="number">1</span>:])</span><br></pre></td></tr></table></figure><h3 id="切片元素索引-amp-s-0"><a href="#切片元素索引-amp-s-0" class="headerlink" title="切片元素索引 &s[0]"></a>切片元素索引 <code>&s[0]</code></h3><figure class="highlight golang"><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">s := []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line">fmt.Println(&s[<span class="number">0</span>])</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><h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><h3 id="函数或方法"><a href="#函数或方法" class="headerlink" title="函数或方法"></a>函数或方法</h3><ul><li><a href="https://juejin.cn/post/7077800061417029640">Golang的寻址与不可寻址</a></li></ul><h1 id="有方向的channel是否可以关闭"><a href="#有方向的channel是否可以关闭" class="headerlink" title="有方向的channel是否可以关闭"></a>有方向的channel是否可以关闭</h1><blockquote><p>因为关闭操作只用于断言不再向channel发送新的数据,所以只有在发送者所在的goroutine才会调用close函数,因此对一个只接收的channel调用close将是一个编译错误。</p></blockquote><h1 id="for-range和for对channel遍历的区别"><a href="#for-range和for对channel遍历的区别" class="headerlink" title="for range和for对channel遍历的区别"></a>for range和for对channel遍历的区别</h1><blockquote><p>for range 中,参与循环表达式的只是对象的副本</p></blockquote><h1 id="context超时控制"><a href="#context超时控制" class="headerlink" title="context超时控制"></a>context超时控制</h1><p>利用 <code>context.WithTimeout()</code> 方法会返回一个具有超时功能的上下文。</p><h1 id="cron定时"><a href="#cron定时" class="headerlink" title="cron定时"></a>cron定时</h1><figure class="highlight golang"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"github.com/robfig/cron/v3"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := cron.New(cron.WithSeconds())</span><br><span class="line"> <span class="comment">// 含义查看下文表达式示例</span></span><br><span class="line"> c.AddFunc(<span class="string">"0/7 * * * * *"</span>, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(time.Now().Format(<span class="string">"2006-01-02 15:04:05"</span>))</span><br><span class="line"> })</span><br><span class="line"> c.Start()</span><br><span class="line"> time.Sleep(<span class="number">300</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><a href="https://segmentfault.com/a/1190000039647260#item-3">Cron表达式</a></li><li><a href="https://segmentfault.com/a/1190000039647260#item-3">Golang cron 定时使用指南</a></li></ul><h1 id="embed包的作用"><a href="#embed包的作用" class="headerlink" title="embed包的作用"></a>embed包的作用</h1><p>embed是在Go 1.16中新加入的包。它通过<code>//go:embed</code>指令,可以在编译阶段将静态资源文件打包进编译好的程序中,并提供访问这些文件的能力。</p><h2 id="为什么需要-embed-包"><a href="#为什么需要-embed-包" class="headerlink" title="为什么需要 embed 包"></a>为什么需要 embed 包</h2><p>在以前,很多从其他语言转过来Go语言的同学会问到,或者踩到一个坑。就是以为Go语言所打包的二进制文件中会包含配置文件的联同编译和打包。</p><p>结果往往一把二进制文件挪来挪去,就无法把应用程序运行起来了。因为无法读取到静态文件的资源。</p><ul><li><a href="https://blog.51cto.com/niuben/3028670">Golang 1.16新特性-embed包及其使用</a></li></ul><h1 id="哪些类型可以和nil比较"><a href="#哪些类型可以和nil比较" class="headerlink" title="哪些类型可以和nil比较"></a>哪些类型可以和nil比较</h1><p>nil是一个预先声明的标识符,代表指针(pointer)、通道(channel)、函数(func)、接口(interface)、map、切片(slice)。也可以这么理解:指针、通道、函数、接口、map、切片的零值就是nil,就像布尔类型的零值是false、整型的零值是0。</p><h2 id="nil标识符的比较"><a href="#nil标识符的比较" class="headerlink" title="nil标识符的比较"></a>nil标识符的比较</h2><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fmt.Println(<span class="literal">nil</span>== <span class="literal">nil</span>)</span><br></pre></td></tr></table></figure><p>==符号对于nil来说是一种未定义的操作,所以是不可以比较两个nil的</p><h2 id="nil的值比较"><a href="#nil的值比较" class="headerlink" title="nil的值比较"></a>nil的值比较</h2><figure class="highlight golang"><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">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// 指针类型的nil比较</span></span><br><span class="line"> fmt.Println((*<span class="type">int64</span>)(<span class="literal">nil</span>) == (*<span class="type">int64</span>)(<span class="literal">nil</span>))</span><br><span class="line"> <span class="comment">// channel 类型的nil比较</span></span><br><span class="line"> fmt.Println((<span class="keyword">chan</span> <span class="type">int</span>)(<span class="literal">nil</span>) == (<span class="keyword">chan</span> <span class="type">int</span>)(<span class="literal">nil</span>))</span><br><span class="line"> <span class="comment">// func类型的nil比较</span></span><br><span class="line"> fmt.Println((<span class="function"><span class="keyword">func</span><span class="params">()</span></span>)(<span class="literal">nil</span>) == (<span class="function"><span class="keyword">func</span><span class="params">()</span></span>)(<span class="literal">nil</span>)) <span class="comment">// func() 只能与nil进行比较</span></span><br><span class="line"> <span class="comment">// interface类型的nil比较</span></span><br><span class="line"> fmt.Println((<span class="keyword">interface</span>{})(<span class="literal">nil</span>) == (<span class="keyword">interface</span>{})(<span class="literal">nil</span>))</span><br><span class="line"> <span class="comment">// map类型的nil比较</span></span><br><span class="line"> fmt.Println((<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)(<span class="literal">nil</span>) == (<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)(<span class="literal">nil</span>)) <span class="comment">// map 只能与nil进行比较</span></span><br><span class="line"> <span class="comment">// slice类型的nil比较</span></span><br><span class="line"> fmt.Println(([]<span class="type">int</span>)(<span class="literal">nil</span>) == ([]<span class="type">int</span>)(<span class="literal">nil</span>)) <span class="comment">// slice 只能与nil进行比较</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>运行结果</li></ul><figure class="highlight golang"><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"># command-line-arguments</span><br><span class="line">./<span class="literal">nil</span>.<span class="keyword">go</span>:<span class="number">13</span>:<span class="number">28</span>: invalid operation: (<span class="function"><span class="keyword">func</span><span class="params">()</span></span>)(<span class="literal">nil</span>) == (<span class="function"><span class="keyword">func</span><span class="params">()</span></span>)(<span class="literal">nil</span>) (<span class="function"><span class="keyword">func</span> <span class="title">can</span> <span class="title">only</span> <span class="title">be</span> <span class="title">compared</span> <span class="title">to</span> <span class="title">nil</span>)</span></span><br><span class="line">./<span class="literal">nil</span>.<span class="keyword">go</span>:<span class="number">17</span>:<span class="number">36</span>: invalid operation: (<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)(<span class="literal">nil</span>) == (<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)(<span class="literal">nil</span>) (<span class="keyword">map</span> can only be compared to <span class="literal">nil</span>)</span><br><span class="line">./<span class="literal">nil</span>.<span class="keyword">go</span>:<span class="number">19</span>:<span class="number">27</span>: invalid operation: ([]<span class="type">int</span>)(<span class="literal">nil</span>) == ([]<span class="type">int</span>)(<span class="literal">nil</span>) (slice can only be compared to <span class="literal">nil</span>)</span><br></pre></td></tr></table></figure><p>从运行结果我们可以看出,指<code>针类型nil</code>、<code>channel类型的nil</code>、<code>interface类型</code>可以相互比较,而<code>func类型</code>、<code>map类型</code>、<code>slice类型</code>只能与<code>nil标识符</code>比较,两个类型相互比较是不合法的。</p><ul><li><a href="https://segmentfault.com/a/1190000039894167">有趣的面试题:Go语言中nil的比较结果</a></li></ul><h1 id="fallthrouth是否可以用在断言"><a href="#fallthrouth是否可以用在断言" class="headerlink" title="fallthrouth是否可以用在断言"></a>fallthrouth是否可以用在断言</h1><h2 id="fallthrouth"><a href="#fallthrouth" class="headerlink" title="fallthrouth"></a>fallthrouth</h2><blockquote><p>fallthrough:Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。</p></blockquote><h2 id="断言"><a href="#断言" class="headerlink" title="断言"></a>断言</h2><p>类型断言(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。</p><p>断言还有一种形式,就是使用「switch语句」判断接口的类型</p>]]></content>
<summary type="html">
<blockquote>
<p>成为Go开发工作者已经2个多月了, 是时候通过一些面试题笔试题提升自己了</p>
</blockquote>
</summary>
<category term="Go面试题" scheme="http://blog.caoxl.com/categories/Go%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="http://blog.caoxl.com/tags/golang/"/>
<category term="go" scheme="http://blog.caoxl.com/tags/go/"/>
</entry>
<entry>
<title>MySQL8窗口函数详解-结合案例</title>
<link href="http://blog.caoxl.com/2022/06/15/MySQL8-Window-Function-II/"/>
<id>http://blog.caoxl.com/2022/06/15/MySQL8-Window-Function-II/</id>
<published>2022-06-15T09:18:25.000Z</published>
<updated>2022-06-16T07:54:22.000Z</updated>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><blockquote><p>窗口函数再深入,结合案例详解</p></blockquote><span id="more"></span><h2 id="MySQL窗口函数列表"><a href="#MySQL窗口函数列表" class="headerlink" title="MySQL窗口函数列表"></a>MySQL窗口函数列表</h2><table><thead><tr><th align="left">名称</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><strong>序号函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>ROW_NUMBER</code></td><td align="left">为其分区中的每一行分配一个连续整数</td></tr><tr><td align="left"><code>DENSE_RANK</code></td><td align="left">根据<code>ORDER BY</code>子句为其分区中的每一行分配一个排名。 它为具有相同值的行分配相同的排名。 如果两行或更多行具有相同的等级,则排序值序列中将没有间隙。</td></tr><tr><td align="left"><code>RANK</code></td><td align="left">与<code>DENSE_RANK()</code>函数类似,只是当两行或更多行具有相同的排名时,排序值序列中存在间隙。</td></tr><tr><td align="left"><strong>分布函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>PERCENT_RANK</code></td><td align="left">计算分区或结果集中行的百分位数</td></tr><tr><td align="left"><code>CUME_DIST</code></td><td align="left">计算一组值中值的累计分布</td></tr><tr><td align="left"><strong>前后函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>LAG</code></td><td align="left">返回分区中当前行之前的第N行的值。 如果不存在前一行,则返回NULL。</td></tr><tr><td align="left"><code>LEAD</code></td><td align="left">返回分区中当前行之后的第N行的值。 如果不存在后续行,则返回NULL。</td></tr><tr><td align="left"><strong>头尾函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>FIRST_VALUE</code></td><td align="left">返回指定表达式相对于窗口框架中第一行的值。</td></tr><tr><td align="left"><code>LAST_VALUE</code></td><td align="left">返回指定表达式相对于窗口框架中最后一行的值。</td></tr><tr><td align="left"><strong>聚合函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>SUM,AVG,COUNT,MAX</code></td><td align="left"></td></tr><tr><td align="left"><code>MEDIAN</code></td><td align="left">中位数</td></tr><tr><td align="left"><code>STDDEV</code></td><td align="left">总体标准差</td></tr><tr><td align="left"><code>STDDEV_SAMP</code></td><td align="left">样本标准差</td></tr><tr><td align="left"><strong>其他函数</strong></td><td align="left"></td></tr><tr><td align="left"><code>NTH_VALUE</code></td><td align="left">返回窗口框架第N行的参数值</td></tr><tr><td align="left"><code>NTILE</code></td><td align="left">将每个窗口分区的行分配到指定数量的已排名组中。</td></tr></tbody></table><h2 id="窗口函数详解-结合案例"><a href="#窗口函数详解-结合案例" class="headerlink" title="窗口函数详解-结合案例"></a>窗口函数详解-结合案例</h2><p>以订单表<code>orders</code>为例,来介绍每个函数的使用方法。表中各字段含义按顺序分别为<code>order_id订单号</code>、<code>user_no用户id</code>、<code>amount订单金额</code>、<code>create_date订单创建日期</code>。</p><figure class="highlight sql"><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="keyword">CREATE</span> <span class="keyword">TABLE</span> `orders` (</span><br><span class="line"> `order_id` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span>,</span><br><span class="line"> `user_no` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span>,</span><br><span class="line"> `amount` <span class="type">decimal</span>(<span class="number">14</span>,<span class="number">2</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span>,</span><br><span class="line"> `create_date` datetime <span class="keyword">NOT</span> <span class="keyword">NULL</span>,</span><br><span class="line"> <span class="keyword">PRIMARY</span> KEY (`order_id`)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8mb4_unicode_ci;</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> orders ( order_id, user_no, amount, create_date )</span><br><span class="line"><span class="keyword">VALUES</span></span><br><span class="line"> ( <span class="number">1</span>, <span class="number">1</span>, <span class="number">100</span>, <span class="string">'2020-01-01 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">2</span>, <span class="number">1</span>, <span class="number">300</span>, <span class="string">'2020-01-02 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">3</span>, <span class="number">1</span>, <span class="number">500</span>, <span class="string">'2020-01-02 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">4</span>, <span class="number">1</span>, <span class="number">800</span>, <span class="string">'2020-01-03 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">5</span>, <span class="number">1</span>, <span class="number">900</span>, <span class="string">'2020-01-04 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">6</span>, <span class="number">2</span>, <span class="number">500</span>, <span class="string">'2020-01-03 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">7</span>, <span class="number">2</span>, <span class="number">600</span>, <span class="string">'2020-01-04 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">8</span>, <span class="number">2</span>, <span class="number">300</span>, <span class="string">'2020-01-10 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">9</span>, <span class="number">2</span>, <span class="number">800</span>, <span class="string">'2020-01-16 00:00:00'</span> ),</span><br><span class="line"> ( <span class="number">10</span>, <span class="number">2</span>, <span class="number">800</span>, <span class="string">'2020-01-22 00:00:00'</span> );</span><br></pre></td></tr></table></figure><h3 id="序号函数"><a href="#序号函数" class="headerlink" title="序号函数"></a>序号函数</h3><ul><li>序号函数: <code>row_number()</code>, <code>rank()</code>, <code>dense_rank()</code><ul><li>用途: <code>显示分区中的当前行号</code></li><li>使用场景: 查询每个用户订单金额最高的前三个订单</li></ul></li></ul><figure class="highlight sql"><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">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> ( </span><br><span class="line"> <span class="keyword">SELECT</span> </span><br><span class="line"> <span class="built_in">ROW_NUMBER</span>() <span class="keyword">over</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount <span class="keyword">DESC</span> ) <span class="keyword">AS</span> row_num,</span><br><span class="line"> order_id, user_no, amount, create_date </span><br><span class="line"> <span class="keyword">FROM</span> orders </span><br><span class="line">) t <span class="keyword">WHERE</span> row_num <span class="operator"><=</span> <span class="number">3</span></span><br></pre></td></tr></table></figure><p>此时可以使用<code>ROW_NUMBER()</code>函数<strong>按照用户进行分组并按照订单金额进行由大到小排序</strong>,最后查找每组中序号<=3的记录。</p><figure class="highlight markdown"><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">row<span class="emphasis">_num order_</span>id user<span class="emphasis">_no amount create_</span>date</span><br><span class="line"> 1 5 1 900 2020-01-04 00:00:00</span><br><span class="line"> 2 4 1 800 2020-01-03 00:00:00</span><br><span class="line"> 3 3 1 500 2020-01-02 00:00:00</span><br><span class="line"> 1 9 2 800 2020-01-16 00:00:00</span><br><span class="line"> 2 10 2 800 2020-01-22 00:00:00</span><br><span class="line"> 3 7 2 600 2020-01-04 00:00:00</span><br></pre></td></tr></table></figure><p>对于用户<code>2</code>的订单,大家发现订单金额为800的有两条,<strong>序号随机排了1和2</strong>,但很多情况下二者应该是并列第一,而订单为600的序号则可能是第二名,也可能为第三名,这时候,<code>row_number</code>就不能满足需求,需要<code>rank</code>和<code>dense_rank</code>出场。</p><figure class="highlight sql"><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="keyword">SELECT</span><span class="operator">*</span> <span class="keyword">FROM</span></span><br><span class="line"> (</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> <span class="built_in">ROW_NUMBER</span>() <span class="keyword">over</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount <span class="keyword">DESC</span> ) <span class="keyword">AS</span> row_num1,</span><br><span class="line"> <span class="built_in">RANK</span>() <span class="keyword">over</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount <span class="keyword">DESC</span> ) <span class="keyword">AS</span> row_num2,</span><br><span class="line"> <span class="built_in">DENSE_RANK</span>() <span class="keyword">over</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount <span class="keyword">DESC</span> ) <span class="keyword">AS</span> row_num3, </span><br><span class="line"> order_id,</span><br><span class="line"> user_no,</span><br><span class="line"> amount,</span><br><span class="line"> create_date </span><br><span class="line"> <span class="keyword">FROM</span></span><br><span class="line"> orders </span><br><span class="line"> ) t </span><br><span class="line"><span class="keyword">WHERE</span>row_num1 <span class="operator"><=</span> <span class="number">3</span></span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">row<span class="emphasis">_num1 row_</span>num2 row<span class="emphasis">_num3 order_</span>id user<span class="emphasis">_no amount create_</span>date</span><br><span class="line"> 1 1 1 5 1 900 2020-01-04 00:00:00</span><br><span class="line"> 2 2 2 4 1 800 2020-01-03 00:00:00</span><br><span class="line"> 3 3 3 3 1 500 2020-01-02 00:00:00</span><br><span class="line"> 1 1 1 9 2 800 2020-01-16 00:00:00</span><br><span class="line"> 2 1 1 10 2 800 2020-01-22 00:00:00</span><br><span class="line"> 3 3 2 7 2 600 2020-01-04 00:00:00</span><br></pre></td></tr></table></figure><ul><li><code>row_number()</code>在amount都是800的两条记录上随机排序,但序号按照1、2递增,后面amount为600的的序号继续递增为3,中间不会产生序号间隙;</li><li><code>rank()</code>/<code>dense_rank()</code>则把amount为800的两条记录序号都设置为1,但后续amount为600的需要则分别设置为3(rank)和2(dense_rank)。<blockquote><p>即rank()会产生序号相同的记录,同时可能产生序号间隙;而dense_rank()也会产生序号相同的记录,但不会产生序号间隙。</p></blockquote></li></ul><h3 id="分布函数"><a href="#分布函数" class="headerlink" title="分布函数"></a>分布函数</h3><ul><li>分布函数: <code>percent_rank()</code>, <code>cume_dist()</code><ul><li>用途: 和之前的RANK()函数相关,每行按照如下公式进行计算:<code>(rank - 1) / (rows - 1)</code></li><li>应用场景: 大于等于当前订单金额的订单比例有多少。</li></ul></li></ul><figure class="highlight sql"><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">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span></span><br><span class="line">(</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> <span class="built_in">RANK</span>() <span class="keyword">over</span> w <span class="keyword">AS</span> row_num,</span><br><span class="line"> <span class="built_in">CUME_DIST</span>() <span class="keyword">over</span> w <span class="keyword">AS</span> <span class="keyword">percent</span>,</span><br><span class="line"> order_id,</span><br><span class="line"> user_no,</span><br><span class="line"> amount </span><br><span class="line"> <span class="keyword">FROM</span></span><br><span class="line"> orders <span class="keyword">window</span> w <span class="keyword">AS</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount <span class="keyword">DESC</span> ) </span><br><span class="line">) t;</span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">row<span class="emphasis">_num percent order_</span>id user<span class="emphasis">_no amount</span></span><br><span class="line"><span class="emphasis"> 1 0.2 5 1 900</span></span><br><span class="line"><span class="emphasis"> 2 0.4 4 1 800</span></span><br><span class="line"><span class="emphasis"> 3 0.6 3 1 500</span></span><br><span class="line"><span class="emphasis"> 4 0.8 2 1 300</span></span><br><span class="line"><span class="emphasis"> 5 1 1 1 100</span></span><br><span class="line"><span class="emphasis"> 1 0.4 9 2 800</span></span><br><span class="line"><span class="emphasis"> 1 0.4 10 2 800</span></span><br><span class="line"><span class="emphasis"> 3 0.6 7 2 600</span></span><br><span class="line"><span class="emphasis"> 4 0.8 6 2 500</span></span><br><span class="line"><span class="emphasis"> 5 1 8 2 300</span></span><br></pre></td></tr></table></figure><h3 id="前后函数"><a href="#前后函数" class="headerlink" title="前后函数"></a>前后函数</h3><ul><li>前后函数: <code>lead()</code>, <code>lag()</code><ul><li>用途: 分区中位于当前n行(lead) / 后n行(lag)的记录值</li><li>使用场景: 查询上一个订单距离当前订单的时间间隔</li></ul></li></ul><figure class="highlight sql"><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="keyword">SELECT</span></span><br><span class="line"> order_id,</span><br><span class="line"> user_no,</span><br><span class="line"> amount,</span><br><span class="line"> create_date,</span><br><span class="line"> last_date,</span><br><span class="line"> DATEDIFF( create_date, last_date ) <span class="keyword">AS</span> diff </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line"> (</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> order_id,</span><br><span class="line"> user_no,</span><br><span class="line"> amount,</span><br><span class="line"> create_date,</span><br><span class="line"> <span class="built_in">lag</span>( create_date, <span class="number">1</span> ) <span class="keyword">over</span> w <span class="keyword">AS</span> last_date </span><br><span class="line"> <span class="keyword">FROM</span></span><br><span class="line"> orders <span class="keyword">window</span> w <span class="keyword">AS</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> create_date ) </span><br><span class="line"> ) t;</span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">order<span class="emphasis">_id user_</span>no amount create<span class="emphasis">_date last_</span>date diff</span><br><span class="line"> 1 1 100 2020-01-01 00:00:00 </span><br><span class="line"> 2 1 300 2020-01-02 00:00:00 2020-01-01 00:00:00 1</span><br><span class="line"> 3 1 500 2020-01-02 00:00:00 2020-01-02 00:00:00 0</span><br><span class="line"> 4 1 800 2020-01-03 00:00:00 2020-01-02 00:00:00 1</span><br><span class="line"> 5 1 900 2020-01-04 00:00:00 2020-01-03 00:00:00 1</span><br><span class="line"> 6 2 500 2020-01-03 00:00:00 </span><br><span class="line"> 7 2 600 2020-01-04 00:00:00 2020-01-03 00:00:00 1</span><br><span class="line"> 8 2 300 2020-01-10 00:00:00 2020-01-04 00:00:00 6</span><br><span class="line"> 9 2 800 2020-01-16 00:00:00 2020-01-10 00:00:00 6</span><br><span class="line"> 10 2 800 2020-01-22 00:00:00 2020-01-16 00:00:00 6</span><br></pre></td></tr></table></figure><p>内层SQL先通过lag函数得到上一次订单的日期,外层SQL再将本次订单和上次订单日期做差得到时间间隔diff。</p><h3 id="头尾函数"><a href="#头尾函数" class="headerlink" title="头尾函数"></a>头尾函数</h3><ul><li>头尾函数: <code>first_val(expr)</code>, <code>last_val(expr)</code><ul><li>用途: 得到分区中的第一个/最后一个指定参数的值</li><li>使用场景: 查询截止到当前订单, 按照日期顺序第一个订单和最后一个订单的订单金额</li></ul></li></ul><figure class="highlight sql"><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">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span></span><br><span class="line"> (</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> order_id,user_no,amount,create_date,</span><br><span class="line"> <span class="built_in">FIRST_VALUE</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> first_amount,</span><br><span class="line"> <span class="built_in">LAST_VALUE</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> last_amount</span><br><span class="line"> <span class="keyword">FROM</span> orders </span><br><span class="line"> <span class="keyword">window</span> w <span class="keyword">AS</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> create_date ) </span><br><span class="line">) t;</span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">order<span class="emphasis">_id user_</span>no amount create<span class="emphasis">_date first_</span>amount last<span class="emphasis">_amount</span></span><br><span class="line"><span class="emphasis"> 1 1 100 2020-01-01 00:00:00 100 100</span></span><br><span class="line"><span class="emphasis"> 2 1 300 2020-01-02 00:00:00 100 500</span></span><br><span class="line"><span class="emphasis"> 3 1 500 2020-01-02 00:00:00 100 500</span></span><br><span class="line"><span class="emphasis"> 4 1 800 2020-01-03 00:00:00 100 800</span></span><br><span class="line"><span class="emphasis"> 5 1 900 2020-01-04 00:00:00 100 900</span></span><br><span class="line"><span class="emphasis"> 6 2 500 2020-01-03 00:00:00 500 500</span></span><br><span class="line"><span class="emphasis"> 7 2 600 2020-01-04 00:00:00 500 600</span></span><br><span class="line"><span class="emphasis"> 8 2 300 2020-01-10 00:00:00 500 300</span></span><br><span class="line"><span class="emphasis"> 9 2 800 2020-01-16 00:00:00 500 800</span></span><br><span class="line"><span class="emphasis"> 10 2 800 2020-01-22 00:00:00 500 800</span></span><br></pre></td></tr></table></figure><p>结果和预期一致,比如order_id为4的记录,<code>first_amount</code>和<code>last_amount</code>分别记录了用户‘1’截止到时间2018-01-03 00:00:00为止,第一条订单金额100和最后一条订单金额800,<strong>注意这里是按时间排序的最早订单和最晚订单</strong>,并不是最小金额和最大金额订单。</p><h3 id="其他函数"><a href="#其他函数" class="headerlink" title="其他函数"></a>其他函数</h3><ul><li>其他函数: <code>nth_value(expr,n)</code>, <code>nfile(n)</code></li></ul><blockquote><p><code>nth_value(expr,n)</code></p></blockquote><ul><li>用途: 返回窗口中第N个expr的值,expr可以是表达式,也可以是列名</li><li>应用场景: 每个用户订单显示本用户金额第二和第三的订单金额</li></ul><figure class="highlight sql"><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">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span></span><br><span class="line"> (</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> order_id,user_no,amount,create_date,</span><br><span class="line"> <span class="built_in">NTH_VALUE</span>(amount,<span class="number">2</span>) <span class="keyword">over</span> w <span class="keyword">as</span> second_amount,</span><br><span class="line"> <span class="built_in">NTH_VALUE</span>(amount,<span class="number">3</span>) <span class="keyword">over</span> w <span class="keyword">as</span> third_amount</span><br><span class="line"> <span class="keyword">FROM</span> orders </span><br><span class="line"> <span class="keyword">window</span> w <span class="keyword">AS</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> amount ) </span><br><span class="line">) t;</span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">order<span class="emphasis">_id user_</span>no amount create<span class="emphasis">_date second_</span>amount third<span class="emphasis">_amount</span></span><br><span class="line"><span class="emphasis"> 1 1 100 2020-01-01 00:00:00 </span></span><br><span class="line"><span class="emphasis"> 2 1 300 2020-01-02 00:00:00 300 </span></span><br><span class="line"><span class="emphasis"> 3 1 500 2020-01-02 00:00:00 300 500</span></span><br><span class="line"><span class="emphasis"> 4 1 800 2020-01-03 00:00:00 300 500</span></span><br><span class="line"><span class="emphasis"> 5 1 900 2020-01-04 00:00:00 300 500</span></span><br><span class="line"><span class="emphasis"> 8 2 300 2020-01-10 00:00:00 </span></span><br><span class="line"><span class="emphasis"> 6 2 500 2020-01-03 00:00:00 500 </span></span><br><span class="line"><span class="emphasis"> 7 2 600 2020-01-04 00:00:00 500 600</span></span><br><span class="line"><span class="emphasis"> 9 2 800 2020-01-16 00:00:00 500 600</span></span><br><span class="line"><span class="emphasis"> 10 2 800 2020-01-22 00:00:00 500 600</span></span><br></pre></td></tr></table></figure><h3 id="聚合函数"><a href="#聚合函数" class="headerlink" title="聚合函数"></a>聚合函数</h3><ul><li>用途: 在窗口中每条记录动态应用聚合函数(sum/avg/max/min/count),可以动态计算在指定的窗口内的各种聚合函数值</li><li>使用场景: 每个用户按照订单id,截止到当前的累计订单金额/平均订单金额/最大订单金额/最小订单金额/订单数是多少?</li></ul><figure class="highlight sql"><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="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span></span><br><span class="line"> (</span><br><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line"> order_id,user_no,amount,create_date,</span><br><span class="line"> <span class="built_in">sum</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> sum1,</span><br><span class="line"> <span class="built_in">avg</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> avg1,</span><br><span class="line"> <span class="built_in">max</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> max1,</span><br><span class="line"> <span class="built_in">min</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> min1,</span><br><span class="line"> <span class="built_in">count</span>(amount) <span class="keyword">over</span> w <span class="keyword">as</span> count1</span><br><span class="line"> <span class="keyword">FROM</span> orders </span><br><span class="line"> <span class="keyword">window</span> w <span class="keyword">AS</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> user_no <span class="keyword">ORDER</span> <span class="keyword">BY</span> order_id ) </span><br><span class="line">) t;</span><br></pre></td></tr></table></figure><figure class="highlight markdown"><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">order<span class="emphasis">_id user_</span>no amount create<span class="emphasis">_date sum1 avg1 max1 min1 count1</span></span><br><span class="line"><span class="emphasis"> 1 1 100 2020-01-01 00:00:00 100 100 100 100 1</span></span><br><span class="line"><span class="emphasis"> 2 1 300 2020-01-02 00:00:00 400 200 300 100 2</span></span><br><span class="line"><span class="emphasis"> 3 1 500 2020-01-02 00:00:00 900 300 500 100 3</span></span><br><span class="line"><span class="emphasis"> 4 1 800 2020-01-03 00:00:00 1700 425 800 100 4</span></span><br><span class="line"><span class="emphasis"> 5 1 900 2020-01-04 00:00:00 2600 520 900 100 5</span></span><br><span class="line"><span class="emphasis"> 6 2 500 2020-01-03 00:00:00 500 500 500 500 1</span></span><br><span class="line"><span class="emphasis"> 7 2 600 2020-01-04 00:00:00 1100 550 600 500 2</span></span><br><span class="line"><span class="emphasis"> 8 2 300 2020-01-10 00:00:00 1400 466 600 300 3</span></span><br><span class="line"><span class="emphasis"> 9 2 800 2020-01-16 00:00:00 2200 550 800 300 4</span></span><br><span class="line"><span class="emphasis"> 10 2 800 2020-01-22 00:00:00 3000 600 800 300 5</span></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><blockquote><p><窗口函数> over (partition by <用于分组的列名> order by <用于排序的列名>)</p></blockquote><ul><li><窗口函数>的位置,可以放以下两种函数:<ol><li>专用的窗口函数, 比如 <code>rank()</code>, <code>dense_rank()</code>, <code>row_number()</code>等</li><li>聚合函数, 如 <code>sum</code>,<code>avg</code>,<code>count</code>,<code>max</code>,<code>min</code>等</li></ol></li></ul><h3 id="窗口函数的功能"><a href="#窗口函数的功能" class="headerlink" title="窗口函数的功能"></a>窗口函数的功能</h3><ul><li><ol><li>同时具有 <code>分组(partition by)</code> 和 <code>排序(order by)</code> 的功能</li></ol></li><li><ol start="2"><li>不减少原表的行数, 所以经常用来每组内排名</li></ol></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://help.aliyun.com/document_detail/34994.html">阿里云窗口函数</a></li><li><a href="https://www.begtut.com/mysql/mysql-window-functions.html">MySQL 窗口函数</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>窗口函数再深入,结合案例详解</p>
</blockquote>
</summary>
<category term="MySQL" scheme="http://blog.caoxl.com/categories/MySQL/"/>
<category term="MySQL8" scheme="http://blog.caoxl.com/tags/MySQL8/"/>
<category term="窗口函数" scheme="http://blog.caoxl.com/tags/%E7%AA%97%E5%8F%A3%E5%87%BD%E6%95%B0/"/>
</entry>
</feed>