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

ivenher · · 597 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

### 1. 值接收者和指针接收者 - 实现了接收者是值类型的方法, ```自动生成```了接收者是指针类型的方法; - 实现了接收者是指针类型的方法,```不会自动生成```对应接收者是值类型的方法。 ### 2. iface 与 eface 都是两个成员,第二个成员都是接口存放的数据。不同的是第一个成员。 ``` eface._type ```接口保存的实体类型,没有包含任何接口方法。 ```iface.tab```是一个itab结构,itab.inter是一个interfacetype指针,描述接口类型,包括:类型、包名、方法列表。 ```iface.tab._type```定义实体类型。同```eface._type```。 ![abc](https://img-blog.csdnimg.cn/20190623120005341.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N2YXN0aWNhaA==,size_16,color_FFFFFF,t_70) #### 2.1 _type _type是Go描述所有数据结构的基础类型,各种数据类型都在该类型上添加特有的信息。 ```_type.kind```按位设置了不同的标志,如位```kindDirectIface=1<<5```用来标识```iface```, ```eface```中```data```保存的是指针,还是值本身。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190623120059361.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N2YXN0aWNhaA==,size_16,color_FFFFFF,t_70) ```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. ```go 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 } ``` ```go //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变量内存结构 ```go 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。 ```go //src\runtime\runtime2.go type iface struct { tab *itab data unsafe.Pointer } ``` 对应汇编 ```go "".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 字节,指向 ```go type structtype struct { typ _type pkgPath name fields []structfield } ``` 代码示例 ```go 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个字节。 ```go 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 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 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``` ```go type imethod struct { name nameOff ityp typeOff } type interfacetype struct { typ _type pkgpath name mhdr []imethod } ``` 对应汇编 ```go 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 ``` ```go 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实现 ```go 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](https://img-blog.csdnimg.cn/2019062312021782.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N2YXN0aWNhaA==,size_16,color_FFFFFF,t_70) ```go 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 ``` ```go 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 ```go i := 200 var ef interface{} = i str := "abc" var ef2 interface{} = str ``` 对应汇编,可以看出整数使用了convT264,字符串使用了 convTstring > go tool compile -S interface.go ```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 ```go 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赋值为非空接口。 ```golang 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。 ```go 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、设置接口值。 ```go 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 ```go 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 } ``` ### 测试代码 ```go 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``` 主要内容 ```go 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 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结构。 ```go 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 } ```

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

597 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传