Go 中数组的内存布局?

Fai*_*VİM -3 memory go low-level

我的代码如下。当我检查值的地址时,它存储为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])
}

Run Code Online (Sandbox Code Playgroud)

输出 :

0xc00000e280
0xc00000e288
0xc00000e290
0xc00000e298
0xc00000e2a0
0xc00000e2a8
0xc00000e2b0
Run Code Online (Sandbox Code Playgroud)

bla*_*een 5

毫不奇怪,Go 数组在内存中连续排列。然后,由于 Go 类型的大小是静态的,因此第 n 个元素的地址等于第 0 个元素的地址加上等于该元素类型大小的字节偏移量。

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

addr_n = addr_0 + (n * size_of(item))
Run Code Online (Sandbox Code Playgroud)

在 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: %d\n", addr_n, *(*int)(addr_n))
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个打印:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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