go101 笔记

#Go 类型系统概述

  • 预声明类型:17 个内置基本类型
  • 从 Go 1.9 到 Go 1.17,Go 白皮书曾经把预声明类型视为定义类型。 但是从 Go 1.18 开始,Go 白皮书明确说明预声明类型不再属于定义类型。

在 Go 中,每个类型都有一个底层类型。规则:

  • 一个内置类型的底层类型为它自己。
  • unsafe 标准库包中定义的 Pointer 类型的底层类型是它自己。(至少我们可以认为是这样。事实上,关于 unsafe.Pointer 类型的底层类型,官方文档中并没有清晰的说明。我们也可以认为 unsafe.Pointer 类型的底层类型为*T,其中 T 表示一个任意类型。) unsafe.Pointer 也被视为一个内置类型。
  • 一个无名类型(必为一个组合类型)的底层类型为它自己。
  • 在一个类型声明中,新声明的类型和源类型共享底层类型。
  • 一般说来,一个可寻址的值是指被放置在内存中某固定位置处的一个值(但放置在某固定位置处的一个值并非一定是可寻址的)。 目前,我们只需知道所有变量都是可以寻址的;但是所有常量、函数返回值和强制转换结果都是不可寻址的。 当一个变量被声明的时候,Go 运行时将为此变量开辟一段内存。此内存的起始地址即为此变量的地址。
  • 解引用操作符*的优先级都高于自增++和自减–操作符。
  • 一个结构体类型中的字段标签和字段的声明顺序对此结构体类型的身份识别很重要。 如果两个无名结构体类型的各个对应字段声明都相同(按照它们的出现顺序),则此两个无名结构体类型是等同的。 两个字段声明只有在它们的名称、类型和标签都等同的情况下才相同。
  • 两个声明在不同的代码包中的非导出字段将总被认为是不同的字段。
  • 当一个(源)结构体值被赋值给另外一个(目标)结构体值时,其效果和逐个将源结构体值的各个字段赋值给目标结构体值的各个对应字段的效果是一样的。
1
2
3
4
5
6
7
8
9
10
func f() {
book1 := Book{pages: 300}
book2 := Book{"Go语言101", "老貘", 256}

book2 = book1
// 上面这行和下面这三行是等价的。
book2.title = book1.title
book2.author = book1.author
book2.pages = book1.pages
}
  • 选择器中的属性选择操作符.的优先级比取地址操作符&的优先级要高。
  • 组合字面量不可寻址但可被取地址。
1
2
3
4
5
6
7
8
9
10
package main

func main() {
type Book struct {
Pages int
}
// Book{100}是不可寻址的,但是它可以被取地址。
p := &Book{100} // <=> tmp := Book{100}; p := &tmp
p.Pages = 200
}
  • 在字段选择器中,属主结构体值可以是指针,它将被隐式解引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

func main() {
type Book struct {
pages int
}
book1 := &Book{100} // book1是一个指针
book2 := new(Book) // book2是另外一个指针
// 像使用结构值一样来使用结构体值的指针。
book2.pages = book1.pages
// 上一行等价于下一行。换句话说,上一行
// 两个选择器中的指针属主将被自动解引用。
(*book2).pages = (*book1).pages
}

一个非空接口类型的值的dynamicTypeInfo字段的methods字段引用着一个方法列表。 此列表中的每一项为此接口值的动态类型上定义的一个方法,此方法对应着此接口类型所指定的一个的同描述的方法。

1
2
3
4
5
6
7
type _interface struct {
dynamicTypeInfo *struct {
dynamicType *_type // 引用着接口值的动态类型
methods []*_function // 引用着动态类型的对应方法列表
}
dynamicValue unsafe.Pointer // 引用着动态值
}

NOTE: 所以是 interface 定义的方法列表还是类型的方法列表?

  • 因为一个间接值部可能并不专属于任何一个值,所以在使用unsafe.Sizeof函数计算一个值的尺寸的时候,此值的间接部分所占内存空间未被计算在内。
  • 在运行时刻,即使一个数组变量在声明的时候未指定初始值,它的元素所占的内存空间也已经被开辟出来。 但是一个 nil 切片或者映射值的元素的内存空间尚未被开辟出来。
  • []T{}表示类型[]T的一个空切片值,它和[]T(nil)是不等价的。 同样,map[K]T{}map[K]T(nil)也是不等价的。
  • 容器字面量是不可寻址的但可以被取地址
  • 任意两个映射值(或切片值)是不能相互比较的。
  • 大多数数组类型都是可比较类型,除了元素类型为不可比较类型的数组类型。