• 数组在内存中是连续分配的,数组的大小保存在数组类型元数据中的。

[3]int 的内存布局

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 示例[3]int 数组布局 内存中连续分配
// 地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
//     |<---a[0]---->| |<-------a[1]------>|  |<--------a[2]------>|
a := [3]int{0, 1, 2}    // a的地址&a也就是当前示例的0即内存的首地址
    
// 查看a的内存占用大小 int 在64位系统下占8字节 3*8 = 24字节
fmt.Println(unsafe.Sizeof(a))   // 24

// 数组的长度保存在类型元数据中
type arrayType struct {
    _type        // 数组类型结构
    elem *_type  // 数组元素类型结构
    slice *_type // 切片类型结构
    len uintptr  // 数组长度
}

[3]string 的内存布局

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 示例[5]string 字符串数组内存布局
// 字符串结构我们是如下 64位操作系统下
//  string struct
//    字段      类型        大小        示例
//    data    uintptr    8byte  指向底层数组 如x[0] -> ['h', 'e', 'l', 'l', 'o', '2', '1']
//    len      int       8byte  记录字符的长度 如x[0] 7
//
// |
// 0 1 ... 6 7 8 9 ... 14 15 16 17 .. 22 23 24 25 ... 30 31 32 33 .. 39 40 41 42 ... 内存地址byte
// |         | |          |  |           |  |            |  |              | ...
// |x[0].data| | x[0].len |  | x[1].data |  |  x[1].len  |  |  x2[2].data  | ...
// |   8B    | |    8B    |  |    8B     |  |     8B     |  |      8B      | ...
// 
// 数组的长度记录在数组的类型元数据中
x := [5]string{"hello21", "word", "! 1", "! 2", "! 3"}

// 数组x占用内存大小(字节) 5*16
fmt.Println(unsafe.Sizeof(x))	// 80    
  1. 验证[2]string的内存布局。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
    "fmt"
    "unsafe"
)

// array2Str 类 [2]string 内存结构布局
// 因此 数组 直接看成相应的结构体
type array2Str struct {
    a0Data unsafe.Pointer
    a0Len  int
    a1Data unsafe.Pointer
    a1Len  int
}

func main() {
    a := [2]string{"hello", "world!"} // 16 * 2 = 32

    // 把 a 看成 array2Str 结构体
    s := **(**[5]byte)(unsafe.Pointer(&a)) // a[0]; array2Str.p1

    fmt.Println(string(s[:]), unsafe.Sizeof(a)) // hello 32

    // &array2Str.l1
    // 把 a 看成 array2Str 这里比较好理解
    a0l := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&a)) + unsafe.Sizeof(uintptr(0)))) // len(a[0])
    //a0l := (*(*[2]int)(unsafe.Pointer(&a)))[1] // 5

    fmt.Println(*a0l) // 5

    ss := (*array2Str)(unsafe.Pointer(&a))

    fmt.Println(*ss) // {4807271 5 4807756 6}
}

数组的按值传参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
    "fmt"
    "unsafe"
)

func main() {
    a := [2]int{10, 20}

    // 数组a占用内存大小:16
    fmt.Printf("数组a占用内存大小:%d\n", unsafe.Sizeof(a))

    fmt.Printf("数组a地址:%p\n", &a)

    x(a)

    // Output:
    // 数组a占用内存大小:16
    // 数组a地址:0xc00000e0b0
    // 数组a占用内存大小:16
    // 数组a地址:0xc00000e0d0
    
    // 从数组a的地址可以看出,main函数的栈分配是经挨着的
}

func x(a [2]int) {
    // 数组a占用内存大小:16
    fmt.Printf("数组a占用内存大小:%d\n", unsafe.Sizeof(a))

    fmt.Printf("数组a地址:%p", &a)
}