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
因为sliceHeaderPtr
是reflect.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.Data
是uintptr
类型,其值应该是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}