IT科技

Go语言之再谈空接口

时间:2010-12-5 17:23:32  作者:人工智能   来源:域名  查看:  评论:0
内容摘要:前言在 【Go】内存中的空接口 一文中,我们知道 interface{} 类型的对象的结构如下://emptyInterfaceistheheaderforaninterface{}value.typ

前言

在 【Go】内存中的谈空空接口 一文中,我们知道 interface{ } 类型的接口对象的结构如下:

// emptyInterface is the header for an interface{ } value. type emptyInterface struct {    typ  *rtype   word unsafe.Pointer } 

该结构体包含两个指针,占 16 个字节。谈空

该结构体包含类型信息和数据信息,接口Go编译器可以把任何类型的谈空数据封装成该结构的对象;结果是在语法层面, interface{ } 类型的接口变量可以引用任何类型的数据, interface{ } 类型的谈空形式参数可以接收任何类型的实际参数。

前文只是接口介绍了 interface{ } 的对象结构,但还没有介绍 interface{ } 的谈空类型信息。本文将详细分析空接口的接口类型信息。

继续阅读文本之前,谈空强烈推荐优先阅读以下两篇图文:

【Go】内存中的接口空接口 【Go】深入理解函数

环境

OS : Ubuntu 20.04.2 LTS; x86_64 Go : go version go1.16.2 linux/amd64 

声明

操作系统、处理器架构、谈空Go版本不同,接口均有可能造成相同的谈空源码编译后运行时的寄存器值、内存地址、数据结构等存在差异。源码库

本文仅包含 64 位系统架构下的 64 位可执行程序的研究分析。

本文仅保证学习过程中的分析数据在当前环境下的准确有效性。

代码清单

package main import "fmt" import "reflect" func main() {    Typeof(Typeof) } //go:noinline func Typeof(i interface{ }) {    t := reflect.TypeOf(i)   fmt.Println("函数类型", t.String())   fmt.Println("参数类型", t.In(0).String()) } 

运行结果

接口类型

接口类型信息的结构定义在reflect/type.go源文件中:

// interfaceType represents an interface type. type interfaceType struct {    rtype   pkgPath name      // import path   methods []imethod // sorted by hash } 

该结构体占 80 个字节。

结构分布图

将接口类型信息绘制成图表如下:

内存分析

再次强调,继续之前务必阅读 【Go】内存中的函数 ,方便了解本文分析过程。

定义Typeof函数的目的是方便定位 interface{ } 的类型信息,直接在该函数入口处设置断点。

在调试过程中,首先得到的是Typeof函数的类型信息。该函数只有一个参数,参数的类型信息位于内存地址 0x4a5200 处,这就是空接口的类型信息。

将空接口的类型信息做成图表如下:

rtype.size = 16 rtype.ptrdata = 16 rtype.hash = 0x18a057e7 rtype.tflag = 2 = reflect.tflagExtraStar rtype.align = 8 rtype.fieldAlign = 8 rtype.kind = 20 = reflect.Interface rtype.equal = 0x4c2ba8 = runtime.nilinterequal rtype.str = 0x000030b0 => *interface { } rtype.ptrToThis = 0x000067c0 interfaceType.pkgPath = 0 = nil interfaceType.methods.Data = 0x4a5220 interfaceType.methods.Len = 0 interfaceType.methods.Cap = 0

从以上数据我们可以知道,interface{ } 类型的对象 16 字节大小、云服务器提供商8 字节对齐、具有可比较性,没有任何方法。

ptrToThis = 0x000067c0 是空接口指针类型(*interface{ })信息的内存偏移量。

空接口是Go语言内置的数据类型,不属于任何包,所以包路径值为空(pkgPath)。

可比较性

空接口具有可比较性。

空接口类型的equal函数为runtime.nilinterequal,该函数定义在runtime/alg.go源文件中:

func nilinterequal(p, q unsafe.Pointer) bool {    x := *(*eface)(p)   y := *(*eface)(q)   return x._type == y._type && efaceeq(x._type, x.data, y.data) } func efaceeq(t *_type, x, y unsafe.Pointer) bool {    if t == nil {      return true   }   eq := t.equal   if eq == nil {      panic(errorString("comparing uncomparable type " + t.string()))   }   if isDirectIface(t) {      return x == y   }   return eq(x, y) } // isDirectIface reports whether t is stored directly in an interface value. func isDirectIface(t *_type) bool {    return t.kind&kindDirectIface != 0 } 

通过源码,看到空接口对象比较的逻辑如下:

如果两个对象的类型不一样,返回 false 如果两个对象的类型一样,并且值为nil,返回 true 如果两个对象的类型一样,但是不可比较,直接 panic 然后进行比较,并返回比较结果。

也就是说,空接口对象的比较,实际是其表示的香港云服务器类型和其指向的数据的比较。

通过内存中的空接口和本文,基本已经全面了解了 interface{ }。

本文转载自微信公众号「Golang In Memory」

最近更新
热门排行
copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap