slice底层struct的数据结构

我们知道slice底层的数据结构是SliceHeader,位于value.go如下:

1type SliceHeader struct {
2	Data uintptr
3	Len  int
4	Cap  int
5}

现在,创建一个slice然后打印每个元素的地址。

1arr := [5]int{1, 2, 3, 4, 5}
2s := arr[1:4]
3fmt.Printf("%p, %p, %p \n", &s[0], &s[1], &s[2])

上述代码的打印结果类似:

10xc42001a068, 0xc42001a070, 0xc42001a078 

转换成十进制后三个地址相差8,因为Go中的int在64位CPU的机器占8字节。既然slice底层数据结构是SliceHeader,那么其元素地址和SliceHeader的地址有什么关系呢?要解决这个问题,首先需要获取到SliceHeader的地址,unsafe.Pointer能做到这点。

1sliceHeaderPtr := (*reflect.SliceHeader)(unsafe.Pointer(&s))

&s表示对s进行取址,它的类型是*[]int,即指向s的指针。然后unsafe.Pointer(&s)*[]int转换成unsafe.Pointer类型,之所以能完成这种转换,因为unsafa.Pointer有以下特性:

1- A pointer value of any type can be converted to a Pointer.
2- A Pointer can be converted to a pointer value of any type.
3- A uintptr can be converted to a Pointer.
4- A Pointer can be converted to a uintptr.

参考

最后将unsafe.Pointer类型转换成*reflect.SliceHeader类型。现在可以查看SliceHeader的地址:

1fmt.Printf("sliceHeaderPtr保存的地址:%p\n", sliceHeaderPtr) // 0xc42000a080

因为sliceHeaderPtrreflect.SliceHeader的指针类型,所以其值是reflect.SliceHeader的地址,可以判断,这个地址和sliceHeaderPtr.Data的地址是相同的,即struct的地址就是第一个成员的地址。

1fmt.Printf("sliceHeaderPtr.Data: 0x%x\n", &(sliceHeaderPtr.Data)) // 0xc42000a080
2fmt.Printf("sliceHeaderPtr.Len: 0x%x\n", &(sliceHeaderPtr.Len)) // 0xc42000a088
3fmt.Printf("sliceHeaderPtr.Cap: 0x%x\n", &(sliceHeaderPtr.Cap)) // 0xc42000a090

因为SliceHeader三个成员均占8字节,所以其地址对应的十进制相差8。 sliceHeaderPtr.Datauintptr类型,其值应该是s[0]的地址,同时也arr[1]的地址。注意sliceHeaderPtr.Data的地址和sliceHeaderPtr.Data所保存的地址间的区别。

1if sliceHeaderPtr.Data == (uintptr)(unsafe.Pointer(&s[0])) &&
2    sliceHeaderPtr.Data == (uintptr)(unsafe.Pointer(&arr[1])) {
3    fmt.Println("true")
4}

完整示例:

 1package main
 2
 3import (
 4	"fmt"
 5	"reflect"
 6	"unsafe"
 7)
 8
 9func main() {
10	arr := [5]int{1, 2, 3, 4, 5}
11	s := arr[1:4]
12	sliceHeaderPtr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
13	fmt.Printf("%p, %p, %p \n", &s[0], &s[1], &s[2])
14	fmt.Printf("sliceHeaderPtr保存的地址:%p\n", sliceHeaderPtr)
15	fmt.Printf("sliceHeaderPtr.Data保存的地址:0x%x\n", sliceHeaderPtr.Data)
16	fmt.Printf("sliceHeaderPtr.Data: 0x%x\n", &(sliceHeaderPtr.Data))
17	fmt.Printf("sliceHeaderPtr.Len: 0x%x\n", &(sliceHeaderPtr.Len))
18	fmt.Printf("sliceHeaderPtr.Cap: 0x%x\n", &(sliceHeaderPtr.Cap))
19	fmt.Printf("%d, %d, %d\n", 0xc42000a080, 0xc42000a088, 0xc42000a090)
20	if sliceHeaderPtr.Data == (uintptr)(unsafe.Pointer(&s[0])) &&
21		sliceHeaderPtr.Data == (uintptr)(unsafe.Pointer(&arr[1])) {
22		fmt.Println("true")
23	}
24}