我更好奇如果需要,我将如何从类型 []byte 到 [4]byte?
从 Go 1.17 开始,您可以直接将切片转换为数组指针。使用 Go 的类型转换语法T(x)
,您可以这样做:
slice := make([]byte, 4)
arrptr := (*[4]byte)(slice)
记住,数组的长度不能大于切片的长度,否则转换会panic。
bad := (*[5]byte)(slice) // panics: slice len < array len
这种转换的优点是不进行任何复制,因为它只是产生了一个指向底层数组的指针。
当然,您可以取消引用数组指针以获取非指针数组变量,因此以下方法也有效:
slice := make([]byte, 4)
var arr [4]byte = *(*[4]byte)(slice)
但是,取消引用和分配会巧妙地复制,因为arr
变量现在已初始化为转换表达式产生的值。要清楚(为简单起见使用整数):
v := []int{10,20}
a := (*[2]int)(v)
a[0] = 500
fmt.Println(v) // [500 20] (changed, both point to the same backing array)
w := []int{10,20}
b := *(*[2]int)(w)
b[0] = 500
fmt.Println(w) // [10 20] (unchanged, b holds a copy)
有人可能想知道为什么转换检查切片长度而不是容量(我做过)。考虑以下程序:
func main() {
a := []int{1,2,3,4,5,6}
fmt.Println(cap(a)) // 6
b := a[:3]
fmt.Println(cap(a)) // still 6
c := (*[3]int)(b)
ptr := uintptr(unsafe.Pointer(&c[0]))
ptr += 3 * unsafe.Sizeof(int(0))
i := (*int)(unsafe.Pointer(ptr))
fmt.Println(*i) // 4
}
该程序显示转换可能在重新切片后发生。(*[6]int)(b)
包含六个元素的原始后备数组仍然存在,所以人们可能想知道为什么where会发生运行时恐慌cap(b) == 6
。
这其实已经提出来了。值得记住的是,与切片不同,数组具有固定大小,因此它不需要容量的概念,只需要长度:
a := [4]int{1,2,3,4}
fmt.Println(len(a) == cap(a)) // true