大佬教程收集整理的这篇文章主要介绍了面试题:让你捉摸不透的 Go reslice,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
package main
func a() []int {
a1 := []int{3}
a2 := a1[1:]
return a2
}
func main() {
a()
}
(A) 编译失败
(B) panic: runtime error: index out of range [1] with length 1
(C) []
(D) 其他
第一感觉: 肯定能编译过, 但是运行时一定会panic的. 但事与愿违竟然能够正常运行, 结果是:[]
a1 := []int{3}
a2 := a1[1:]
fmt.Println("a[1:]", a2)
a1 和 a2 共享同样的底层数组, len(a1) = 1, a1[1]绝对会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:])
结果:
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
// 省略....
其实主要关心这两行即可.
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. 这是为何呢?
持着怀疑态度, 去 github 提上一枚 issue. https://github.com/golang/go/issues/42069
结论: 这是故意的, 单纯为了保持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的原理基本已经阐述清楚了.
以上是大佬教程为你收集整理的面试题:让你捉摸不透的 Go reslice全部内容,希望文章能够帮你解决面试题:让你捉摸不透的 Go reslice所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。