13

08-go数据类型内存结构-interface

 3 years ago
source link: https://studygolang.com/articles/31010
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

自动生成
不会自动生成

2. iface 与 eface

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

BF3Inij.jpg!mobile

2.1 _type

_type是Go描述所有数据结构的基础类型,各种数据类型都在该类型上添加特有的信息。

_type.kind 按位设置了不同的标志,如位 kindDirectIface=1<<5 用来标识 iface , efacedata 保存的是指针,还是值本身。

zyIfye6.jpg!mobile

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"。

AV3yqeq.jpg!mobile

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
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK