package main func a() []int { a1 := []int{3} a2 := a1[1:] return a2 } func main() { a() }
看到這個題, 你的第一反應是啥?git
(A) 編譯失敗 (B) panic: runtime error: index out of range [1] with length 1 (C) [] (D) 其餘
第一感受: 確定能編譯過, 可是運行時必定會panic的. 但事與願違居然可以正常運行, 結果是:[]github
a1 := []int{3} a2 := a1[1:] fmt.Println("a[1:]", a2)
a1 和 a2 共享一樣的底層數組, len(a1) = 1, a1[1]絕對會panic, 可是a[1:]卻能正常輸出, 這是爲什麼?golang
總體上看下總體的狀況express
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:])
結果:c#
len:1, cap:1 a[0:]: [1] a[1:] [] panic: runtime error: slice bounds out of range [2:1]
從表面來看, 從a[2:]纔開始panic, 究竟是誰一手形成這樣的結果呢?數組
"".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的數據首地址加載到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 // 省略....
其實主要關心這兩行便可.ide
0x003e 00062 (main.go:8) XORPS X0, X0 // 初始化 X0 寄存器 0x0041 00065 (main.go:8) MOVUPS X0, "".~r0+40(SP) // 將X0放入返回值
是否是很神奇, a[1:] 沒有調用runtime.panicSliceB(SB)
, 而是返回的是一個空的slice. 這是爲什麼呢? ui
持着懷疑態度, 去 github 提上一枚 issue. https://github.com/golang/go/... spa
結論: 這是故意的, 單純爲了保持reslice對稱而已. 這也就解釋了返回一個空的slice的緣由.指針
上面的問題已經解釋清楚了, 回過頭來看正常 reslice 的例子
func a() []int { a1 := []int{3, 4, 5, 6, 7, 8} a2 := a1[2:] return a2 }
用簡單的圖來描述這段代碼裏, a1 和 a2 之間的reslice 關係. 能夠看到 a1 和 a2 是共享底層數組的.
若是你知道這些, 那麼 slice 的使用基本上不會出現問題.
下面這些問題你考慮過嗎 ?
繼續來看這段代碼的彙編:
"".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 // 省略....
下圖是 slice 的 棧圖, 能夠配合着上面的彙編一塊看.
看到這裏是否是一目瞭然了. 因而有了下面的這些結論:
至此, golang reslice的原理基本已經闡述清楚了.