go语言设计与实现-语言基础-阅读笔记

函数调用

  • 传值:函数调用时会对参数进行拷贝,被调用方和调用方两者持有不相关的两份数据;
  • 传引用:函数调用时会传递参数的指针,被调用方和调用方两者持有相同的数据,任意一方做出的修改都会影响另一方。

Go 语言选择了传值的方式,无论是传递基本类型、结构体还是指针,都会对传递的参数进行拷贝

  • 通过堆栈传递参数,入栈的顺序是从右到左;
  • 函数返回值通过堆栈传递并由调用者预先分配内存空间;
  • 调用函数时都是传值,接收方会对入参进行复制再计算;

 

接口

接口也是 Go 语言中的一种类型,它能够出现在变量的定义、函数的入参和返回值中并对它们进行约束,不过 Go 语言中有两种略微不同的接口,一种是带有一组方法的接口,另一种是不带任何方法的 interface{}

Go 语言使用 iface 结构体表示第一种接口,使用 eface 结构体表示第二种空接口,两种接口虽然都使用 interface 声明,但是由于后者在 Go 语言中非常常见,所以在实现时使用了特殊的类型。

type eface struct { // 16 bytes
    _type *_type
    data  unsafe.Pointer
}

由于 interface{} 类型不包含任何方法,所以它的结构也相对来说比较简单,只包含指向底层数据和类型的两个指针。从上述结构我们也能推断出 — Go 语言中的任意类型都可以转换成 interface{} 类型。

另一个用于表示接口的结构体就是 iface,这个结构体中有指向原始数据的指针 data,不过更重要的是 itab 类型中的 tab 字段。

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

_type 是 Go 语言类型的运行时表示。下面是运行时包中的结构体,结构体包含了很多元信息,例如:类型的大小、哈希、对齐以及种类等。

type _type struct {
    size       uintptr
    ptrdata    uintptr
    hash       uint32
    tflag      tflag
    align      uint8
    fieldAlign uint8
    kind       uint8
    equal      func(unsafe.Pointer, unsafe.Pointer) bool
    gcdata     *byte
    str        nameOff
    ptrToThis  typeOff
}
  • size 字段存储了类型占用的内存空间,为内存空间的分配提供信息;
  • hash 字段能够帮助我们快速确定类型是否相等;
  • equal 字段用于判断当前类型的多个对象是否相等,该字段是为了减少 Go 语言二进制包大小从 typeAlg 结构体中迁移过来

itab 结构体是接口类型的核心组成部分,每一个 itab 都占 32 字节的空间,我们可以将其看成接口类型和具体类型的组合,它们分别用 inter 和 _type 两个字段表示

type itab struct { // 32 bytes
    inter *interfacetype
    _type *_type
    hash  uint32
    _     [4]byte
    fun   [1]uintptr
}

除了 inter 和 _type 两个用于表示类型的字段之外,上述结构体中的另外两个字段也有自己的作用:

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

反射

三大法则
从 interface{} 变量可以反射出反射对象
从反射对象可以获取 interface{} 变量
要修改反射对象,其值必须可设置

由于 reflect.TypeOf、reflect.ValueOf 两个方法的入参都是 interface{} 类型,所以在方法执行的过程中发生了类型转换
v.Interface().(type)

从接口值到反射对象:
从基本类型到接口类型的类型转换;
从接口类型到反射对象的转换;
从反射对象到接口值:
反射对象转换成接口类型;
通过显式类型转换变成原始类型;


修改变量方式
调用 reflect.ValueOf 函数获取变量指针;
调用 reflect.Value.Elem 方法获取指针指向的变量;
调用 reflect.Value.SetInt 方法更新变量的值
emptyInterface
type
word
reflect.TypeOf 函数的实现原理其实并不复杂,它只是将一个 interface{} 变量转换成了内部的 emptyInterface 表示,然后从中获取相应的类型信息。
reflect.ValueOf 实现也非常简单,在该函数中我们先调用了 reflect.escapes 函数保证当前值逃逸到堆上,然后通过 reflect.unpackEface 方法从接口中获取 Value 结构体
reflect.unpackEface 函数会将传入的接口转换成 emptyInterface 结构体,然后将具体类型和指针包装成 Value 结构体并返回

posted @ 2021-02-28 15:58  菲菲菲菲菲常新的新手  阅读(335)  评论(0编辑  收藏  举报