6-数据类型内存结构-interface

2019-05-27  本文已影响0人  浩玥当空

1. 值接收者和指针接收者

2. iface 与 eface

都是两个成员,第二个成员都是接口存放的数据。不同的是第一个成员。
eface._type接口保存的实体类型,没有包含任何接口方法。
iface.tab是一个itab结构,itab.inter是一个interfacetype指针,描述接口类型,包括:类型、包名、方法列表。
iface.tab._type定义实体类型。同eface._type。

接口结构实现

2.1 _type

_type是Go描述所有数据结构的基础类型,各种数据类型都在该类型上添加特有的信息。
_type.kind按位设置了不同的标志,如位kindDirectIface=1<<5 用来标识iface,efacedata保存的是指针,还是值本身。

go类型定义

const (
   KindBool = 1 + iota
   KindInt
   KindInt8
   KindInt16
   KindInt32
   KindInt64
   KindUint
   KindUint8
   KindUint16
   KindUint32
   KindUint64
   KindUintptr
   KindFloat32
   KindFloat64
   KindComplex64
   KindComplex128
   KindArray
   KindChan
   KindFunc
   KindInterface
   KindMap
   KindPtr
   KindSlice
   KindString
   KindStruct
   KindUnsafePointer
   KindDirectIface = 1 << 5
   KindGCProg      = 1 << 6
   KindNoPointers  = 1 << 7
   KindMask        = (1 << 5) - 1
)

2.2 全局itab表

itab一般静态分配,或者持久分配内存,从不释放。运行时申请的itab被缓存在itabTable中。指向全局对象itabTableInit

libgo\go\runtime\iface.go

全局接口表大小为512.

const itabInitSize = 512

var (
    itabLock      mutex                               // lock for accessing itab table
    itabTable     = &itabTableInit                    // pointer to current table
    itabTableInit = itabTableType{size: itabInitSize} // starter table
)

// Note: change the formula in the mallocgc call in itabAdd if you change these fields.
type itabTableType struct {
    size    uintptr             // length of entries array. Always a power of 2.
    count   uintptr             // current number of filled entries.
    entries [itabInitSize]*itab // really [size] large
}
//src\runtime\runtime2.go
type itab struct {
    inter *interfacetype    //8 字节
    _type *_type            //8 字节
    hash  uint32   //4 字节 copy of _type.hash. Used for type switches.
    _     [4]byte   //4 字节
    fun   [1]uintptr // 8 字节variable sized. fun[0]==0 means _type does not implement inter.
}

2.3 interface变量

2.3.1 interface变量内存结构

      1 package main
      2 import ( "fmt" )
      3 
      4 type person interface {
      5         Name() string
      6 }
      7 
      8 type student struct {
      9         name string
     10         age  int32
     11 }
     12 
     13 func (stu student) Name() string{
     14         return stu.name
     15 }
     16 
     17 var var_struct student = student{"aaaabbbbc",0x123456}
     18 
     19 var stu1 person = student{"ccc",18}
     20 func main() {
     21         var stu2 person = stu1 
     22         fmt.Print(stu2);
     23 }       

对应汇编如下。
stu1是一个person类型的interface,根据汇编可以看出stu1有两部分组成,共16字节。
前8字节指向go.itab."".student,"".person+0;
后8字节指向 "".statictmp_0+0,是结构体student。
即:interface变量分为两部分 itab指针,data指针。对于一般指直接放在data中,如数值,如stu2。对于复杂数据,data存放的实际数据的指针,如:stu1。

//src\runtime\runtime2.go
type iface struct {
    tab  *itab
    data unsafe.Pointer
}
"".stu1 SDATA size=16
        0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
        rel 0+8 t=1 go.itab."".student,"".person+0
        rel 8+8 t=1 "".statictmp_0+0
"".statictmp_0 SDATA size=24
        0x0000 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00  ................
        0x0010 12 00 00 00                                      ....
        rel 0+8 t=1 go.string."ccc"+0
"".stu2 SNOPTRDATA size=16 
        0x0000 34 12 00 00 00 00 00 00 18 90 78 56 34 12 00 00  4.........xV4...

结构体student在内存中有三部分:
typ 8 字节,指向

type structtype struct {
    typ     _type
    pkgPath name
    fields  []structfield
}
package teachertest

type teacher struct {
    name string
    age  int64
}

type student struct {
    name string
    age  int64
}

type person interface {
    Name() string
    Age() int64
}

func (s teacher) Name() string {
    return s.name
}

func (s teacher) Age() int64 {
    return s.age
}

func (s student) Name() string {
    return s.name
}

func (s student) Age() int64 {
    return s.age
}

var var_1 person = teacher{"aaa", 38}
var var_2 person = teacher{"bbb", 28}
var var_3 person = student{"ccc", 18}

2.3.1 接口变量底层结构iface

定义变量var_1,var_2,var_3类型为接口person。具体对象类型分别为teacher、student.
编译器生成3个person变量,类型均为为iface。包含两部分。
即:具有方法接口变量的底层实现为iface,占用16个字节。

type iface struct {
    tab  *itab  // -->指向接口类型,go.itab."".teacher,"".person+0
    data unsafe.Pointer //-->指向数据 这是一个静态临时对象。
}

对应的汇编代码。可以看出,有3个iface对象,
var_1.data指向临时变量statictmp_0
var_2.data指向临时变量statictmp_1
var_3.data指向临时变量statictmp_2
var_1.tab=var_2.tab,均指向go.itab."".teacher,"".person+0
var_3.tab指向go.itab."".student,"".person+0

可以看出:
相同接口类型的不同变量(var_2,var_3):

  1. 数值部分不同。iface.data
  2. 接口类型相同。iface.tab.inter
  3. 接口具体类型不同。iface.tab._type
    即:一个接口变量其实有三部分组成。数值部分,接口静态类型,接口动态类型.
go.string."aaa" SRODATA dupok size=3
    0x0000 61 61 61                                         aaa
go.string."bbb" SRODATA dupok size=3
    0x0000 62 62 62                                         bbb
go.string."ccc" SRODATA dupok size=3
    0x0000 63 63 63                                         ccc
"".var_1 SDATA size=16
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    rel 0+8 t=1 go.itab."".teacher,"".person+0
    rel 8+8 t=1 "".statictmp_0+0
"".var_2 SDATA size=16
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    rel 0+8 t=1 go.itab."".teacher,"".person+0
    rel 8+8 t=1 "".statictmp_1+0
"".var_3 SDATA size=16
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    rel 0+8 t=1 go.itab."".student,"".person+0
    rel 8+8 t=1 "".statictmp_2+0
"".statictmp_0 SDATA size=24
    0x0000 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00  ................
    0x0010 84 01 00 00 00 00 00 00                          ........
    rel 0+8 t=1 go.string."aaa"+0
"".statictmp_1 SDATA size=24
    0x0000 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00  ................
    0x0010 20 01 00 00 00 00 00 00                           .......
    rel 0+8 t=1 go.string."bbb"+0
"".statictmp_2 SDATA size=24
    0x0000 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00  ................
    0x0010 bc 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 go.string."ccc"+0

2.3.2 接口类型底层结构itab

再看go.itab."".teacher,"".person
8+8+4+4=24字节。有两个方法,一个方法8字节因此size=24+8*2=40字节。
go.itab."".student,"".person也是40个字节。

type itab struct { //共32 + 8 = 40 字节
    inter *interfacetype    // 8 字节   -->指向 type."".person+0
    _type *_type            // 8 字节   -->指向 type."".teacher+0
    hash  uint32            // 4 字节 copy of _type.hash. Used for type switches.
    _     [4]byte           // 4字节
    fun   [1]uintptr        // 8 字节 -->指向 "".(*my).Age+0    variable sized. fun[0]==0 means _type does not implement inter.
}                           // 8 字节 -->指向 "".(*my).Name+0

go.itab."".teacher,"".persongo.itab."".student,"".person 汇编代码

go.itab."".teacher,"".person SRODATA dupok size=40
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x0010 59 b9 7d 36 00 00 00 00 00 00 00 00 00 00 00 00  Y.}6............
    0x0020 00 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 type."".person+0
    rel 8+8 t=1 type."".teacher+0
    rel 24+8 t=1 "".(*teacher).Age+0
    rel 32+8 t=1 "".(*teacher).Name+0
go.itablink."".teacher,"".person SRODATA dupok size=8
    0x0000 00 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 go.itab."".teacher,"".person+0
go.itab."".student,"".person SRODATA dupok size=40
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x0010 3a 3a 10 19 00 00 00 00 00 00 00 00 00 00 00 00  ::..............
    0x0020 00 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 type."".person+0
    rel 8+8 t=1 type."".student+0
    rel 24+8 t=1 "".(*student).Age+0
    rel 32+8 t=1 "".(*student).Name+0
go.itablink."".student,"".person SRODATA dupok size=8
    0x0000 00 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 go.itab."".student,"".person+0

根据汇编可以看出go.itab."".teacher,"".persongo.itab."".student,"".person都是itab结构。
itab.inter相同,都指向 type."".person+0
itab._type不同,分别指向 type."".teacher+0,type."".student+0
itab.hash 不同,分别为 0x0010 3a 3a 10 19,0x0010 59 b9 7d 36.
itab.fun 不同,
var_1=var_2,都指向"".(*teacher).Age+0,"".(*teacher).Name+0.
var_3指向"".(*student).Age+0,"".(*student).Name+0

2.3.3 interfacetype

再看 type."".person size=112
person是一个interface,底层对象结构为 interfacetype

type imethod struct {
    name nameOff
    ityp typeOff
}

type interfacetype struct {
    typ     _type
    pkgpath name
    mhdr    []imethod
}
type."".person SRODATA size=112
    0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
    0x0010 4c 01 88 4f 07 08 08 14 00 00 00 00 00 00 00 00  L..O............
    0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
    0x0050 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00  ........ .......
    0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    rel 24+8 t=1 runtime.algarray+128
    rel 32+8 t=1 runtime.gcbits.02+0
    rel 40+4 t=5 type..namedata.*teachertest.person-+0
    rel 44+4 t=5 type.*"".person+0
    rel 48+8 t=1 type..importpath."".+0
    rel 56+8 t=1 type."".person+96
    rel 80+4 t=5 type..importpath."".+0
    rel 96+4 t=5 type..namedata.Age.+0
    rel 100+4 t=5 type.func() int64+0
    rel 104+4 t=5 type..namedata.Name.+0
    rel 108+4 t=5 type.func() string+0
type tflag uint8
type nameOff int32
type typeOff int32
type textOff int32

type _type struct {
    size       uintptr  //8 字节
    ptrdata    uintptr  //8 字节 size of memory prefix holding all pointers
    hash       uint32   //4 字节
    tflag      tflag    //1 字节
    align      uint8    //1 字节  0x08 8字节对象
    fieldalign uint8    //1 字节  0x08 8字节对象 
    kind       uint8    //1 字节   0x14=20=RUNTIME_TYPE_KIND_INTERFACE
    alg        *typeAlg //8 字节
    // gcdata stores the GC type data for the garbage collector.
    // If the KindGCProg bit is set in kind, gcdata is a GC program.
    // Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
    gcdata    *byte     //8
    str       nameOff   //4
    ptrToThis typeOff   //4
}

3. 类型转换

类型转换分为3类。
1、具体类型转空接口;
2、具体类型转接口;
3、接口转接口;

3.1 具体类型转为空接口 convT2E

src\runtime\iface.go
1、eface._type设置为具体对象类型。
2、eface.data新申请一块内存,保存传入数据。

3.1.1 convT2E实现

type person interface {
    Name() string
}
var ef3 interface{} = student{"abc"}
通过汇编可以看到,编译器操作过程
1、创建临时变量autotmp_36,置空。
2、给临时变量autotmp_36赋值为"abc"。
3、将student地址放到寄存器CX,将CX压进栈 0(SP)。
4、将字符串临时变量autotmp_36地址放进寄存器CX,将CX压进栈8(SP)。
5、调用runtime.convT2E(t *_type, elem unsafe.Pointer) (e eface) 
    第一个参数t,即student地址,student类型是一个structtype,structtype的第一个元素是type _type.
    第二个参数elem,即字符串临时变量autotmp_36地址,值为"abc"。
eface_struct.jpg
    0x0073 00115 (interface.go:29)  PCDATA  $0, $3
    0x0073 00115 (interface.go:29)  XORPS   X0, X0
    0x0076 00118 (interface.go:29)  MOVUPS  X0, ""..autotmp_36+144(SP) //将临时变量清空
    0x007e 00126 (interface.go:29)  PCDATA  $2, $2
    0x007e 00126 (interface.go:29)  LEAQ    go.string."abc"(SB), CX //将字符串"abc"地址放到寄存器 CX
    0x0085 00133 (interface.go:29)  PCDATA  $2, $0
    0x0085 00133 (interface.go:29)  MOVQ    CX, ""..autotmp_36+144(SP) //将字符串"abc"赋值给临时变量
    0x008d 00141 (interface.go:29)  MOVQ    $3, ""..autotmp_36+152(SP)
    0x0099 00153 (interface.go:29)  PCDATA  $2, $2
    0x0099 00153 (interface.go:29)  LEAQ    type."".student(SB), CX //将student地址放到寄存器CX
    0x00a0 00160 (interface.go:29)  PCDATA  $2, $0
    0x00a0 00160 (interface.go:29)  MOVQ    CX, (SP)//将寄存器CX(即student)放到栈中
    0x00a4 00164 (interface.go:29)  PCDATA  $2, $2
    0x00a4 00164 (interface.go:29)  PCDATA  $0, $2
    0x00a4 00164 (interface.go:29)  LEAQ    ""..autotmp_36+144(SP), CX //将字符串"abc"临时对象地址 放到寄存器CX
    0x00ac 00172 (interface.go:29)  PCDATA  $2, $0
    0x00ac 00172 (interface.go:29)  MOVQ    CX, 8(SP) //将字符串放入栈中
    0x00b1 00177 (interface.go:29)  CALL    runtime.convT2E(SB) //调用convT2E函数
    0x00b6 00182 (interface.go:29)  PCDATA  $2, $1
    0x00b6 00182 (interface.go:29)  MOVQ    24(SP), AX
    0x00bb 00187 (interface.go:29)  MOVQ    16(SP), CX
func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
    if raceenabled {
        raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E))
    }
    if msanenabled {
        msanread(elem, t.size)
    }
    x := mallocgc(t.size, t, true) //申请内存用于保存数据
    // TODO: We allocate a zeroed object only to overwrite it with actual data.
    // Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice.
    typedmemmove(t, x, elem)
    e._type = t // 空接口数据类型设置为具体对象类型
    e.data = x  // 空接口数据指向新申请的内存
    return
}

3.1.2 convT64

对于具体类型转为eface有一些特例,进行了优化。比如:convT64
    i := 200
    var ef interface{} = i
    str := "abc"
    var ef2 interface{} = str

对应汇编,可以看出整数使用了convT264,字符串使用了 convTstring

go tool compile -S interface.go

    0x0036 00054 (interface.go:26)  PCDATA  $2, $0
    0x0036 00054 (interface.go:26)  PCDATA  $0, $0
    0x0036 00054 (interface.go:26)  MOVQ    $200, (SP)
    0x003e 00062 (interface.go:26)  CALL    runtime.convT64(SB)
    0x0043 00067 (interface.go:26)  PCDATA  $2, $1
    0x0043 00067 (interface.go:26)  MOVQ    8(SP), AX
    0x0048 00072 (interface.go:26)  PCDATA  $2, $0
    0x0048 00072 (interface.go:26)  PCDATA  $0, $1
    0x0048 00072 (interface.go:26)  MOVQ    AX, ""..autotmp_85+104(SP)
    0x004d 00077 (interface.go:28)  PCDATA  $2, $2
    0x004d 00077 (interface.go:28)  LEAQ    go.string."abc"(SB), CX
    0x0054 00084 (interface.go:28)  PCDATA  $2, $0
    0x0054 00084 (interface.go:28)  MOVQ    CX, (SP)
    0x0058 00088 (interface.go:28)  MOVQ    $3, 8(SP)
    0x0061 00097 (interface.go:28)  CALL    runtime.convTstring(SB)
3.1 convT64,convTstring
func convT64(val uint64) (x unsafe.Pointer) {
    if val == 0 {
        x = unsafe.Pointer(&zeroVal[0])
    } else {
        x = mallocgc(8, uint64Type, false)
        *(*uint64)(x) = val
    }
    return
}


func convTstring(val string) (x unsafe.Pointer) {
    if val == "" {
        x = unsafe.Pointer(&zeroVal[0])
    } else {
        x = mallocgc(unsafe.Sizeof(val), stringType, true)
        *(*string)(x) = val
    }
    return
}

3.2 具体类型转接口 convT2I

src\runtime\iface.go
1.将iface.tab设置为传入的itab。itab在哪里创建?
2.将iface.data指向新申请的内存。
3.2.1 源码

将struct赋值为非空接口。

type person interface {
    Name() string
}

type student struct {
    name string
    age  int64
}
func main() {
    var ef3 person = student{"abc", 18}
}
3.2.1 汇编
go tool compile -N -S interface.go

汇编可以看出主要过程如下:
1、创建临时对象student。
2、编译器将person接口的成员person.tab压入栈。person的底层类型iface.tab为* itab,itab._type指向student。
编译器明确知道转换后的接口类型,直接已经构造好接口,以及接口类型,缺少的就是设置接口值。
3、将student的值压入栈。
4、调用runtime.convT2I。

    0x00c4 00196 (interface.go:37)  PCDATA  $0, $3
    0x00c4 00196 (interface.go:37)  MOVQ    $0, ""..autotmp_36+920(SP) 
    0x00d0 00208 (interface.go:37)  XORPS   X0, X0
    0x00d3 00211 (interface.go:37)  MOVUPS  X0, ""..autotmp_36+928(SP) //清空临时变量 autotmp_36
    0x00db 00219 (interface.go:37)  PCDATA  $2, $1
    0x00db 00219 (interface.go:37)  LEAQ    go.string."abc"(SB), AX //将字符串"abc"地址放到寄存器AX
    0x00e2 00226 (interface.go:37)  PCDATA  $2, $0
    0x00e2 00226 (interface.go:37)  MOVQ    AX, ""..autotmp_36+920(SP)  //将字符串"abc"地址赋值给临时变量 autotmp_36,即name="abc"
    0x00ea 00234 (interface.go:37)  MOVQ    $3, ""..autotmp_36+928(SP)
    0x00f6 00246 (interface.go:37)  MOVQ    $18, ""..autotmp_36+936(SP) //将临时变量autotmp_36,第16字节开始设置为18,即age=18
    0x0102 00258 (interface.go:37)  PCDATA  $2, $1
    0x0102 00258 (interface.go:37)  LEAQ    go.itab."".student,"".person(SB), AX    //将student.itab放进寄存器AX
    0x0109 00265 (interface.go:37)  PCDATA  $2, $0
    0x0109 00265 (interface.go:37)  MOVQ    AX, (SP)    //将AX压进栈,即将student.itab压进展,对应convT2I第一个参数t *itab
    0x010d 00269 (interface.go:37)  PCDATA  $2, $1
    0x010d 00269 (interface.go:37)  PCDATA  $0, $2
    0x010d 00269 (interface.go:37)  LEAQ    ""..autotmp_36+920(SP), AX  
    0x0115 00277 (interface.go:37)  PCDATA  $2, $0
    0x0115 00277 (interface.go:37)  MOVQ    AX, 8(SP)   //将临时变量autotmp_36,即student的值压进栈
    0x011a 00282 (interface.go:37)  CALL    runtime.convT2I(SB) //调用runtime.convT2I.
    0x011f 00287 (interface.go:37)  PCDATA  $2, $1
    0x011f 00287 (interface.go:37)  MOVQ    24(SP), AX
    0x0124 00292 (interface.go:37)  MOVQ    16(SP), CX
    0x0129 00297 (interface.go:37)  PCDATA  $0, $4
    0x0129 00297 (interface.go:37)  MOVQ    CX, "".ef3+528(SP)
    0x0131 00305 (interface.go:37)  PCDATA  $2, $0
    0x0131 00305 (interface.go:37)  MOVQ    AX, "".ef3+536(SP)
3.2.2 convT2I实现
1、获取将具体对象类型,即iface.itab._type指向student结构体。
2、申请内存用于存放接口值。
3、设置接口类型。
4、设置接口值。
func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
    t := tab._type
    if raceenabled {
        raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I))
    }
    if msanenabled {
        msanread(elem, t.size)
    }
    x := mallocgc(t.size, t, true)  // 申请内存保存数据
    typedmemmove(t, x, elem)    
    i.tab = tab //将iface.tab设置为传入的itab
    i.data = x  //将iface.data指向新申请的内存
    return
}

3.3

func convI2I(inter *interfacetype, i iface) (r iface) {
    tab := i.tab
    if tab == nil {
        return
    }
    if tab.inter == inter {
        r.tab = tab
        r.data = i.data
        return
    }
    r.tab = getitab(inter, tab._type, false)
    r.data = i.data
    return
}

测试代码

package main

import "fmt"

type MyInter interface {
    func1()
    func2()
}

type MyStruct struct {
    var1 int
    var2 string
}

func (s MyStruct) func1() {
    fmt.Println("var1=", s.var1)
    return
}

func (s MyStruct) func2() {
    fmt.Println("var2=", s.var2)
    return
}

func main() {
    var var1 = MyInter(MyStruct{var1: 12345, var2: "this is var2"})

    fmt.Println(var1)
}

生成汇编

go tool compile -S interface.go > ddd

主要内容

    0x0099 00153 (interface2.go:38) PCDATA  $0, $3
    0x0099 00153 (interface2.go:38) XORPS   X0, X0
    0x009c 00156 (interface2.go:38) MOVUPS  X0, ""..autotmp_11+112(SP)
    0x00a1 00161 (interface2.go:38) MOVQ    $0, ""..autotmp_11+128(SP)
    0x00ad 00173 (interface2.go:38) MOVQ    $22222, ""..autotmp_11+112(SP)
    0x00b6 00182 (interface2.go:38) PCDATA  $2, $2
    0x00b6 00182 (interface2.go:38) LEAQ    go.string."bbb"(SB), DX
    0x00bd 00189 (interface2.go:38) PCDATA  $2, $0
    0x00bd 00189 (interface2.go:38) MOVQ    DX, ""..autotmp_11+120(SP)
    0x00c2 00194 (interface2.go:38) MOVQ    $3, ""..autotmp_11+128(SP)
    0x00ce 00206 (interface2.go:38) PCDATA  $2, $2
    0x00ce 00206 (interface2.go:38) LEAQ    go.itab."".MyStruct,"".MyInter2(SB), DX
    0x00d5 00213 (interface2.go:38) PCDATA  $2, $0
    0x00d5 00213 (interface2.go:38) MOVQ    DX, (SP)
    0x00d9 00217 (interface2.go:38) PCDATA  $2, $2
    0x00d9 00217 (interface2.go:38) PCDATA  $0, $2
    0x00d9 00217 (interface2.go:38) LEAQ    ""..autotmp_11+112(SP), DX
    0x00de 00222 (interface2.go:38) PCDATA  $2, $0
    0x00de 00222 (interface2.go:38) MOVQ    DX, 8(SP)
    0x00e3 00227 (interface2.go:38) CALL    runtime.convT2I(SB)
    0x00e8 00232 (interface2.go:38) MOVQ    16(SP), AX
    0x00ed 00237 (interface2.go:38) PCDATA  $2, $3
    0x00ed 00237 (interface2.go:38) MOVQ    24(SP), CX
    0x00f2 00242 (interface2.go:38) XCHGL   AX, AX
    0x00f3 00243 (interface2.go:31) PCDATA  $2, $4
    0x00f3 00243 (interface2.go:31) LEAQ    type."".MyInter(SB), DX

itab内存结构

go.itab."".MyStruct,"".MyInter SRODATA dupok size=40
    0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x0010 dc a7 2c a3 00 00 00 00 00 00 00 00 00 00 00 00  ..,.............
    0x0020 00 00 00 00 00 00 00 00                          ........
    rel 0+8 t=1 type."".MyInter+0
    rel 8+8 t=1 type."".MyStruct+0
    rel 24+8 t=1 "".(*MyStruct).func1+0
    rel 32+8 t=1 "".(*MyStruct).func2+0

实现接口转换函数,将具体类型转换为一个iface结构。

func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
    t := tab._type
    if raceenabled {
        raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I))
    }
    if msanenabled {
        msanread(elem, t.size)
    }
    x := mallocgc(t.size, t, true)
    typedmemmove(t, x, elem)
    i.tab = tab
    i.data = x
    return
}
上一篇下一篇

猜你喜欢

热点阅读