<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試題:這道 reslice 的題 簡(jiǎn)單,但原理你知曉嗎?

          共 3694字,需瀏覽 8分鐘

           ·

          2020-10-28 12:59

          package?main

          func?a()?[]int?{
          ?a1?:=?[]int{3}
          ?a2?:=?a1[1:]
          ?return?a2
          }

          func?main()?{
          ?a()
          }

          看到這個(gè)題, 你的第一反應(yīng)是啥?

          (A)?編譯失敗
          (B)?panic:?runtime?error:?index?out?of?range?[1]?with?length?1
          (C)?[]
          (D)?其他

          也許你的第一感覺是: 肯定能編譯過, 但是運(yùn)行時(shí)一定會(huì)panic的. 但事與愿違竟然能夠正常運(yùn)行, 結(jié)果是:[]

          疑問

          a1?:=?[]int{3}
          a2?:=?a1[1:]
          fmt.Println("a[1:]",?a2)

          a1 和 a2 共享同樣的底層數(shù)組, len(a1) = 1, a1[1]絕對(duì)會(huì)panic, 但是a[1:]卻能正常輸出, 這是為何?

          從表面入手

          整看下整體的情況

          a1?:=?[]int{3}
          fmt.Printf("len:%d,?cap:%d",?len(a1),?cap(a1))
          fmt.Println("a[0:]",?a1[0:])
          fmt.Println("a[1:]",?a1[1:])
          fmt.Println("a[2:]",?a1[2:])

          結(jié)果:

          len:1,?cap:1
          a[0:]:?[1]
          a[1:]?[]
          panic:?runtime?error:?slice?bounds?out?of?range?[2:1]

          從表面來看, 從a[2:]才開始panic, 到底是誰一手造成這樣的結(jié)果呢?

          匯編上看一目了然

          "".a STEXT size=87 args=0x18 locals=0x18
          // 省略...
          0x0028 00040 (main.go:6) CALL runtime.newobject(SB)
          0x002d 00045 (main.go:6) MOVQ 8(SP), AX // 將slice的數(shù)據(jù)首地址加載到AX寄存器
          0x0032 00050 (main.go:6) MOVQ $3, (AX) // 把3放入到AX寄存器中, 也就是a1[0]
          0x0039 00057 (main.go:8) MOVQ AX, "".~r0+32(SP)
          0x003e 00062 (main.go:8) XORPS X0, X0 // 初始化 X0 寄存器
          0x0041 00065 (main.go:8) MOVUPS X0, "".~r0+40(SP) // 將X0放入返回值
          0x0046 00070 (main.go:8) MOVQ 16(SP), BP
          0x004b 00075 (main.go:8) ADDQ $24, SP
          0x004f 00079 (main.go:8) RET
          // 省略....

          其實(shí)主要關(guān)心這兩行即可.

          0x003e?00062?(main.go:8)?XORPS?X0,?X0?????//?初始化?X0?寄存器
          0x0041?00065?(main.go:8)?MOVUPS?X0,?"".~r0+40(SP)?//?將X0放入返回值

          是不是很神奇, a[1:] 沒有調(diào)用runtime.panicSliceB(SB), 而是返回的是一個(gè)空的slice. 這是為何呢?

          持著懷疑態(tài)度, 去 github 提上一枚 issue. https://github.com/golang/go/issues/42069

          reslice

          結(jié)論: 這是故意的, 單純?yōu)榱吮3謗eslice對(duì)稱而已. 這也就解釋了返回一個(gè)空的slice的原因.

          reslice 原理

          上面的問題已經(jīng)解釋清楚了, 回過頭來看正常 reslice 的例子

          func?a()?[]int?{
          ?a1?:=?[]int{3,?4,?5,?6,?7,?8}
          ?a2?:=?a1[2:]
          ?return?a2
          }

          用簡(jiǎn)單的圖來描述這段代碼里, a1 和 a2 之間的reslice 關(guān)系. 可以看到 a1 和 a2 是共享底層數(shù)組的.

          reslice

          如果你知道這些, 那么 slice 的使用基本上不會(huì)出現(xiàn)問題.

          下面這些問題你考慮過嗎 ?

          1. a1, a2 是如何共享底層數(shù)組的?
          2. a1[low:high]是如何實(shí)現(xiàn)的?

          繼續(xù)來看這段代碼的匯編:

          "".a STEXT size=117 args=0x18 locals=0x18
          // 省略...
          0x0028 00040 (main.go:4) CALL runtime.newobject(SB)
          0x002d 00045 (main.go:4) MOVQ 8(SP), AX
          0x0032 00050 (main.go:4) MOVQ $3, (AX)
          0x0039 00057 (main.go:4) MOVQ $4, 8(AX)
          0x0041 00065 (main.go:4) MOVQ $5, 16(AX)
          0x0049 00073 (main.go:4) MOVQ $6, 24(AX)
          0x0051 00081 (main.go:4) MOVQ $7, 32(AX)
          0x0059 00089 (main.go:4) MOVQ $8, 40(AX)
          0x0061 00097 (main.go:5) ADDQ $16, AX
          0x0065 00101 (main.go:6) MOVQ AX, "".~r0+32(SP)
          0x006a 00106 (main.go:6) MOVQ $4, "".~r0+40(SP)
          0x0073 00115 (main.go:6) MOVQ $4, "".~r0+48(SP)
          0x007c 00124 (main.go:6) MOVQ 16(SP), BP
          0x0081 00129 (main.go:6) ADDQ $24, SP
          0x0085 00133 (main.go:6) RET
          // 省略....
          • 第4行: 將 AX 棧頂指針下移 8 字節(jié), 指向了 a1 的 data 指向的地址空間里
          • 第5-10行: 將 [3,4,5,6,7,8] 放入到 a1 的 data 指向的地址空間里
          • 第11行: AX 指針后移 16 個(gè)字節(jié). 也就是指向元素 5 的位置
          • 第12行: 將 SP 指針下移 32 字節(jié)指向即將返回的 slice (其實(shí)就是 a2 ), 同時(shí)將 AX 放入到 SP. 注意 AX 放入 SP 里的是一個(gè)指針, 也就造成了a1, a2是共享同一塊內(nèi)存空間的
          • 第13行: 將 SP 指針下移 40 字節(jié)指向了 a2 的 len, ?同時(shí) 把 4 放入到 SP, 也就是 len(a2) = 4
          • 第14行: 將 SP 指針下移 48 字節(jié)指向了 a2 的 cap, ?同時(shí) 把 4 放入到 SP, 也就是 cap(a2) = 4

          下圖是 slice 的 棧圖, 可以配合著上面的匯編一塊看.

          slice stack

          看到這里是不是一目了然了. 于是有了下面的這些結(jié)論:

          1. reslice 完全是利用匯編實(shí)現(xiàn)的
          2. reslice 時(shí), slice 的 data 通過指針的移動(dòng)完成, 造成了共享相同的底層數(shù)據(jù), 同時(shí)將新的 len, cap 放入對(duì)應(yīng)的位置

          至此, golang reslice的原理基本已經(jīng)闡述清楚了.

          參考資料

          [1]????????????深入Go的底層,帶你走近一群有追求的人:?https://qcrao.com/2019/03/20/dive-into-go-asm/
          [2]

          匯編角度看 Slice,一個(gè)新的世界:?https://qcrao.com/2019/04/02/dive-into-go-slice/

          [3]

          Why slice not painc:?https://github.com/golang/go/issues/42069

          [4]

          Slice expressions:?https://golang.org/ref/spec#Slice_expressions

          [5]

          A Quick Guide to Go's Assembler:?https://golang.org/doc/asm

          [6]

          plan9 assembly 完全解析:?https://github.com/cch123/golang-notes/blob/master/assembly.md



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。


          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久夜色精品国产 | 午夜无码视频 | 欧美人妻中文字幕久久久苍井空 | 考逼无码 | 国产精品伦子伦免费 |