6-数据类型内存结构-interface
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
,eface
中data
保存的是指针,还是值本身。
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):
- 数值部分不同。
iface.data
- 接口类型相同。
iface.tab.inter
- 接口具体类型不同。
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,"".person
与 go.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,"".person
与go.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
}