• Golang 的元类型在interface章节才会介绍,如果不熟悉略过。

数组元类型结构

  1. 数组元类型:构成数组类型的结构。(数组类型和自定义数组类型)
 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
// 数组类型
type arrayType struct {
    _type           // 数组元类型,也就是数组类型相关信息
    elem *_type     // 数组元素元类型,比如[2]string中的string类型相关信息
    slice *_type    // 切片元类型,为什么数组类型结构中有一个切片的元类型。在反射的方法中被用到用于快速找到切片
    len uintptr     // 数组长度,数组的长度是保存在元类型中的
}

// 自定义数组类型
type u struct {
    arrayType
    u uncommonType
}

// uncommonType 是自定义方法集
type uncommonType struct {
    pkgPath int32   // 4B   偏移到包名称路径
    mcount uint16   // 2B   方法总数量
    xcount uint16   // 2B   可导出方法数量
    moff uint32     // 4B   偏移到首方法的偏移量,方法是按照方法名正序排序的,因此导出方法在最前面
    _ uint32        // 4B   占位内存补齐,补齐4B,该uncommonType正好是16B无论是32位下还是64位下都兼容
}

// 方法类型
type method struct {
    name nameOff    // 偏移量 方法名
    mtyp typeOff    // 偏移量 方法类型
    ifn  textOff    // 偏移量 方法地址 用于接口 编译器生成的包装方法
    tfn  textOff    // 偏移量 方法地址
}

查看数组的_type信息

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
    "fmt"
    "unsafe"
)

// sType 不论在32位下还是64位下刚好是8的倍数都兼容
type sType struct {
    size uintptr        // 当前类型占用字节大小,字节B
    ptrData uintptr     // 类型中也可包含指针的字节数B,在当前大小范围后都是标量数据
    hash uint32         // 类型唯一哈希值,用于快速区分元素类型
    tFlag uint8         // 记录着当前类型的额外信息,比如名称、方法集、名称*前缀、包路径、tag标签等
    align uint8         // 类型的对齐量
    fieldAlign uint8    // 结构体字段的对齐量
    kind uint8          // 类型枚举,也就是当前类型值
    equal func(uintptr, uintptr) // 类型比较函数,不为nil表示可比较
    gcData *byte        // 垃圾回收相关,记录当前类型引用的回收状态等
    str int32           // 当前类型name的偏移量,到当前类型名称路径的偏移量,也就是tFlag信息对应的内容处
    ptrToThis int32     // 当前类型的指针类型的偏移量,也就是[2]int偏移到*[2]int的偏移量
}

//type AA [2]string

func main() {
    var s = [2]string{"hello", "world!"}
    //var s AA = [2]string{"hello", "world!"}

    // type eface interface {typ *_type, data uintptr}
    var ss interface{} = s

    at := **(**sType)(unsafe.Pointer(&ss))

    // main.sType{
    //  size:0x20,              // 2 * 16 = 32
    //  ptrData:0x18,           // 16 + 8 = 3 * 8 = 24,在24字节后全部都是标量数据
    //  hash:0xe9e55850,
    //  tFlag:0x2,              // 0000 0010
    //  align:0x8,
    //  fieldAlign:0x8,
    //  kind:0x11,              // 16 + 1 = 17
    //  equal:(func(uintptr, uintptr))(0x45dee0), // 可比较
    //  gcData:(*uint8)(0x49d780),
    //  str:5888,
    //  ptrToThis:0             // 数组该值都为0
    // }
    fmt.Printf("%#v\n", at)
}

查看数组全部信息

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main

import (
    "fmt"
    "unsafe"
)

type sType struct {
    size uintptr            // 当前类型占用字节大小  B
    ptrData uintptr         // 类型中也可包含指针的字节数 B 在当前大小范围后都是标量数据
    hash uint32             // 类型唯一哈希值
    tFlag uint8             // 记录着当前类型的额外信息,比如名称、方法集、名称*前缀、包路径、tag标签等
    align uint8             // 类型的对齐量
    fieldAlign uint8        // 结构体字段的对齐量
    kind uint8              // 类型枚举
    equal func(uintptr, uintptr) // 类型比较函数
    gcData *byte            // 垃圾回收相关
    str int32               // 当前类型name的偏移量
    ptrToThis int32         // 当前类型的指针类型的偏移量
}

type arrayType struct {
    sType
    elem *sType
    slice *sType
    len uintptr
}

func main() {
    var s = [2]string{"hello", "world!"}
    
    // type eface interface {typ *_type, data uintptr}
    var ss interface{} = s

    at := **(**arrayType)(unsafe.Pointer(&ss))

    // main.arrayType{
    //  sType:main.sType{
    //      size:0x20,
    //      ptrData:0x18,
    //      hash:0xe9e55850,
    //      tFlag:0x2,
    //      align:0x8,
    //      fieldAlign:0x8,
    //      kind:0x11,
    //      equal:(func(uintptr, uintptr))(0x45dee0),
    //      gcData:(*uint8)(0x49d880),
    //      str:5896,
    //      ptrToThis:0
    //  },
    //  elem:(*main.sType)(0x486fa0),
    //  slice:(*main.sType)(0x486120),
    //  len:0x2
    // }
    fmt.Printf("%#v\n", at)
    fmt.Println(*at.elem)   // {16 8 3774831796 7 8 8 24 0x402d20 0x4b4268 3135 21952}
    fmt.Println(*at.slice)  // {24 8 183740627 2 8 8 23 <nil> 0x4b4268 5068 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
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package main

import (
    "fmt"
    "unsafe"
)

type sType struct {
    size uintptr        // 当前类型占用字节大小  B
    ptrData uintptr     // 类型中也可包含指针的字节数 B 在当前大小范围后都是标量数据
    hash uint32         // 类型唯一哈希值
    tFlag uint8         // 记录着当前类型的额外信息,比如名称、方法集、名称*前缀、包路径、tag标签等
    align uint8         // 类型的对齐量
    fieldAlign uint8    // 结构体字段的对齐量
    kind uint8          // 类型枚举
    equal func(uintptr, uintptr) // 类型比较函数
    gcData *byte        // 垃圾回收相关
    str int32           // 当前类型name的偏移量
    ptrToThis int32     // 当前类型的指针类型的偏移量
}

// 数组元类型
type arrayType struct {
    sType
    elem *sType
    slice *sType
    len uintptr
}

// 自定义数组类型,包含自定义方法
type u struct {
    arrayType
    uncommonType
}

// 自定义方法集
type uncommonType struct {
    pkgPath int32   // 4B   偏移到表名称路径
    mCount uint16   // 2B   总方法数量
    xCount uint16   // 2B   可导出方法数量
    mOff uint32     // 4B   偏移到首方法的偏移量,方法是按照方法名正序排序的,因此导出方法在最前面
    _ uint32        // 4B   占位内存补齐
}

// AA 定义自定义类型
type AA [2]uint8

func (a AA) String() string {
    return "可导出方法"
}

func (a *AA) setName(i uint8) {
    a[0] = i // 这里是语法糖 (*a)[0] = 1
}

// ptrType represents a pointer type. 指针类型
type ptrType struct {
    sType
    elem *sType // pointer element (pointed at) type
}

type up struct {
    ptrType
    uncommonType
}

func main() {
    // 验证自定义类型
    var a AA
    a.setName(12)

    // AA类型
    var inter any = a   // 该类型只实现了String方法
    s := **(**u)(unsafe.Pointer(&inter))

    fmt.Printf("%#v\n", s)

    // *AA类型 是指针类型
    var inter1 any = &a // 该类型只实现了setName方法,但是还有编译包装的*AA String()方法
    s1 := **(**up)(unsafe.Pointer(&inter1))

    fmt.Printf("%#v\n", s1)
}

// kind: 0x11 是 17 array类型

// AA类型
// main.u{
//  arrayType:main.arrayType{
//      sType:main.sType{
//          size:0x2,
//          ptrData:0x0,
//          hash:0xeb895ab,
//          tFlag:0xf,
//          align:0x1,
//          fieldAlign:0x1,
//          kind:0x11,
//          equal:(func(uintptr, uintptr))(0xb02c40),
//          gcData:(*uint8)(0xbc8010),
//          str:4503,
//          ptrToThis:45344
//      },
//      elem:(*main.sType)(0xb94e20),
//      slice:(*main.sType)(0xb93f20),
//      len:0x2
//  },
//  uncommonType:main.uncommonType{
//      pkgPath:522,
//      mCount:0x1,     // 方法总数1
//      xCount:0x1,
//      mOff:0x10,
//      _:0x0
//  }
// }

// kind: 0x36 是 54 = 32(类型间接存在接口值中) + 22(指针类型)

// *AA类型
// main.up{
//  ptrType:main.ptrType{
//      sType:main.sType{
//          size:0x8,
//          ptrData:0x8,
//          hash:0xae85f2d0,
//          tFlag:0x9,      // 1001
//          align:0x8,
//          fieldAlign:0x8,
//          kind:0x36,      // 0x36 = 54 = 32 + 22
//          equal:(func(uintptr, uintptr))(0x9f2c80),
//          gcData:(*uint8)(0xab8330),
//          str:4512,
//          ptrToThis:0
//      },
//      elem:(*main.sType)(0xa88fc0)
//  },
//  uncommonType:main.uncommonType{
//      pkgPath:522,
//      mCount:0x2,     // 方法总数2
//      xCount:0x1,
//      mOff:0x10,
//      _:0x0
//  }
// }