21《Go语言入门》interface底层浅分析

这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会,此处应有掌声!
文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中。在看文章的时候,对照仓库中代码学习效果更佳!

目录

  • interface的结构
  • 空接口eface
    • _type类型
  • 非空接口iface
    • itab结构体
  • 支持

interface的结构

前面文章空接口非空接口,它们都属于接口,空接口底层是由runtime.eface结构体实现,而非空接口底层是由runtime.iface结构体实现。

接口
空接口
非空接口
由runtime.eface结构体实现
由`runtime.iface`结构体实现

空接口eface

eface
_type *_type
data unsafe.Pointer
指向具体数据类型信息的指针
指向具体数据的指针

efaceempty interface(空接口)。

eface结构体位于runtime包runtime2.go文件中。

 
type eface struct {
	_type *_type
	data  unsafe.Pointer
}

因为空接口不包含任何方法,所以它的结构也很简单,只包含:

  • _type
    指向具体数据类型信息的指针
  • data
    指向具体数据的指针

这个结构体是golang中的变量类型的基础,所以空接口可以指定任意变量类型。
示例:

func TestEface(t *testing.T) {
	b := Binary(200)
	t.Log(b)

	any := (interface{})(b)
	t.Log(any)
}

都输出了200,下图为any的结构图:
21《Go语言入门》interface底层浅分析_第1张图片

下面具体讲下_type

_type类型

_type是Go类型的运行时表示。它包含了很多类型的元信息,例如:类型的大小、哈希、对齐以及种类等。
_type可以认为是Go语言中所有类型的公共描述,Go语言中几乎所有的数据结构都可以抽象成_type,是所有类型的表现,可以说是万能类型。

type _type struct {
	size       uintptr
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal func(unsafe.Pointer, unsafe.Pointer) bool
	// 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
	str       nameOff
	ptrToThis typeOff
}
  • size 字段存储了类型占用的内存空间,为内存空间的分配提供信息;
  • hash 字段能够帮助我们快速确定类型是否相等;
  • equal 字段用于判断当前类型的多个对象是否相等,该字段是为了减少 Go 语言二进制包大小从 typeAlg 结构体中迁移过来的;
  • gcdata 字段存储gc类型数据。

非空接口iface

iface
tab *itab
data unsafe.Pointer
inter *interfacetype
_type *_type
hash * uint32
fun [1]uintptr
指向具体数据的指针
type _type
pkgpath name
mhdr []imethod
指向具体数据类型信息的指针
函数地址表
name nameOff
ityp typeOff
接口的类型信息

iface结构体位于runtime包runtime2.go文件中。

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

和eface相同的是都有指向具体数据的指针data字段。
不同的是_type变成了tab。

itab结构体

// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptabs.
type itab struct {
	inter *interfacetype
	_type *_type
	hash  uint32 // copy of _type.hash. Used for type switches.
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

itab结构体也包含_type类型的字段。

  • 上面我们已经知道了_type是指向具体数据类型信息的指针。
  • 而inter字段的类型为interfacetype,顾名思义就是接口类型。
type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}

在go中除了interfacetpe 外,还有类似的一些类型,如 arraytype、maptype、chartype、chantype 和 slicetype 等,它们都可以在 src/runtime/type.go 文件里找到。

Go语言各种数据类型都是在 _type 字段的基础上,增加一些额外的字段来进行管理的,比如:

type arraytype struct {
	typ   _type
	elem  *_type
	slice *_type
	len   uintptr
}

type chantype struct {
	typ  _type
	elem *_type
	dir  uintptr
}

type slicetype struct {
	typ  _type
	elem *_type
}

type functype struct {
	typ      _type
	inCount  uint16
	outCount uint16
}
  • hash 是对 _type.hash 的拷贝,当我们想将 interface 类型转换成具体类型时,可以使用该字段快速判断目标类型和具体类型 runtime._type 是否一致;
  • fun 是一个动态大小的数组,它是一个用于动态派发的虚函数表,存储了一组函数指针。虽然该变量被声明成大小固定的数组,但是在使用时会通过原始指针获取其中的数据,

你可能会觉得奇怪,为什么 fun 数组的大小为1,要是接口定义了多个方法可怎么办?实际上,这里存储的是第一个方法的函数指针。如果 fun[0] 为 0 时,说明 _type 并没有实现该接口。否则表示已经实现,如果有更多的方法,在它之后的内存空间里继续存储。从汇编角度来看,通过增加地址就能获取到这些函数指针,没什么影响。顺便提一句,这些方法是按照函数名称的字典序进行排列的。

支持

  • 我会持续编写【软件开发相关】的文章,保持每周至少一篇文章。
  • 如果你也是【软件工程师】,【关注❤️我】,一定会对你有所帮助。
  • 如果这篇文章对你有所帮助,那就麻烦,【点赞】
  • 您的支持将给与我更大的动力,谢谢。

你可能感兴趣的:(#,Go语言入门,go,golang,go语言)