日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

GoLang中的iface?和?eface?的區別解析_Golang

作者:~龐貝 ? 更新時間: 2022-11-19 編程語言

GoLang之iface 和 eface 的區別是什么?

ifaceeface 都是 Go 中描述接口的底層結構體,區別在于 iface 描述的接口包含方法,而 eface 則是不包含任何方法的空接口:interface{}

從源碼層面看一下:

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

type itab struct {
	inter  *interfacetype
	_type  *_type
	link   *itab
	hash   uint32 // copy of _type.hash. Used for type switches.
	bad    bool   // type does not implement interface
	inhash bool   // has this itab been added to hash?
	unused [2]byte
	fun    [1]uintptr // variable sized
}

iface 內部維護兩個指針,tab 指向一個 itab 實體, 它表示接口的類型以及賦給這個接口的實體類型。data 則指向接口具體的值,一般而言是一個指向堆內存的指針。

再來仔細看一下 itab 結構體:_type 字段描述了實體的類型,包括內存對齊方式,大小等;inter 字段則描述了接口的類型。fun 字段放置和接口方法對應的具體數據類型的方法地址,實現接口調用方法的動態分派,一般在每次給接口賦值發生轉換時會更新此表,或者直接拿緩存的 itab。

這里只會列出實體類型和接口相關的方法,實體類型的其他方法并不會出現在這里。如果你學過 C++ 的話,這里可以類比虛函數的概念。

另外,你可能會覺得奇怪,為什么 fun 數組的大小為 1,要是接口定義了多個方法可怎么辦?實際上,這里存儲的是第一個方法的函數指針,如果有更多的方法,在它之后的內存空間里繼續存儲。從匯編角度來看,通過增加地址就能獲取到這些函數指針,沒什么影響。順便提一句,這些方法是按照函數名稱的字典序進行排列的。

再看一下 interfacetype 類型,它描述的是接口的類型:

type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}

可以看到,它包裝了 _type 類型,_type 實際上是描述 Go 語言中各種數據類型的結構體。我們注意到,這里還包含一個 mhdr 字段,表示接口所定義的函數列表, pkgpath 記錄定義了接口的包名。

這里通過一張圖來看下 iface 結構體的全貌:

iface 結構體全景

接著來看一下 eface 的源碼:

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

相比 ifaceeface 就比較簡單了。只維護了一個 _type 字段,表示空接口所承載的具體的實體類型。data 描述了具體的值。

eface 結構體全景

我們來看個例子:

package main

import "fmt"

func main() {
	x := 200
	var any interface{} = x
	fmt.Println(any)

	g := Gopher{"Go"}
	var c coder = g
	fmt.Println(c)
}

type coder interface {
	code()
	debug()
}

type Gopher struct {
	language string
}

func (p Gopher) code() {
	fmt.Printf("I am coding %s language\n", p.language)
}

func (p Gopher) debug() {
	fmt.Printf("I am debuging %s language\n", p.language)
}

執行命令,打印出匯編語言:

go tool compile -S ./src/main.go

可以看到,main 函數里調用了兩個函數:

func convT2E64(t *_type, elem unsafe.Pointer) (e eface)
func convT2I(tab *itab, elem unsafe.Pointer) (i iface)

上面兩個函數的參數和 ifaceeface 結構體的字段是可以聯系起來的:兩個函數都是將參數組裝一下,形成最終的接口。

作為補充,我們最后再來看下 _type 結構體:

type _type struct {
    // 類型大小
	size       uintptr
    ptrdata    uintptr
    // 類型的 hash 值
    hash       uint32
    // 類型的 flag,和反射相關
    tflag      tflag
    // 內存對齊相關
    align      uint8
    fieldalign uint8
    // 類型的編號,有bool, slice, struct 等等等等
	kind       uint8
	alg        *typeAlg
	// gc 相關
	gcdata    *byte
	str       nameOff
	ptrToThis typeOff
}

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 structtype struct {
	typ     _type
	pkgPath name
	fields  []structfield
}

這些數據類型的結構體定義,是反射實現的基礎。

原文鏈接:https://blog.csdn.net/qq_53267860/article/details/127101278

欄目分類
最近更新