在Go数组的内存布局?



我的代码如下:当我检查的地址值存储为280 288 290 298。为什么以这种模式存储?

package main

import "fmt"
func main() {
const a = 1111111111111111111
test := [7]int{a, 1, 33333,4,6,7,7}
fmt.Println(&test[0])
fmt.Println(&test[1])
fmt.Println(&test[2])
fmt.Println(&test[3])
fmt.Println(&test[4])
fmt.Println(&test[5])
fmt.Println(&test[6])
}

输出:

0xc00000e280
0xc00000e288
0xc00000e290
0xc00000e298
0xc00000e2a0
0xc00000e2a8
0xc00000e2b0

不出所料,Go数组在内存中连续布局。然后,由于Go类型是静态大小的,因此第n项的地址等于第0项的地址加上一个字节偏移量,该字节偏移量等于该项类型的大小。

可以大致形式化为(伪代码):

addr_n = addr_0 + (n * size_of(item))

在Go代码中,使用unsafe.Add(从Go 1.17开始):

func main() {
const a = 1111111111111111111
x := [7]int{a, 1, 33333, 4, 6, 7, 7}
unsafePointer := unsafe.Pointer(&x[0])
for i := range x {
step := unsafe.Sizeof(int(0))
addr_n := unsafe.Add(unsafePointer, int(step)*i)
fmt.Printf("addr: %p, val: %dn", addr_n, *(*int)(addr_n))
}
}

打印:

addr: 0xc000102000, val: 1111111111111111111
addr: 0xc000102008, val: 1
addr: 0xc000102010, val: 33333
addr: 0xc000102018, val: 4
addr: 0xc000102020, val: 6
addr: 0xc000102028, val: 7
addr: 0xc000102030, val: 7

如果不是很清楚,十六进制数字是内存地址。这就是指针通常是如何被fmt包格式化的

但是请注意,int的大小特别依赖于平台,因此在上面的代码片段中不能只添加8。要使其具有确定性,可以使用unsafe.Sizeof(int(0))

游乐场:https://go.dev/play/p/4hu8efVed96

最新更新