《Go语言设计与实现》03-2 切片

a[low : high : max] capacity will be set to max - low, low <= high <= max https://stackoverflow.com/questions/27938177/golang-slice-slicing-a-slice-with-sliceabc OSLICEHEADER 操作会创建我们在上面介绍过的结构体 reflect.SliceHeader,其中包含数组指针、切片长度和容量,它是切片在运行时的表示: type SliceHeader struct { Data uintptr Len int Cap int } 正是因为大多数对切片类型的操作并不需要直接操作原来的 runtime.slice 结构体,所以 reflect.SliceHeader 的引入能够减少切片初始化时的少量开销,该改动不仅能够减少 约0.2% 的 Go 语言包大小,还能够减少 92 个 runtime.panicIndex 的调用,占 Go 语言二进制的 约3.5%。 3.2.4 追加扩容 分为append后的切片覆盖原切片和不覆盖原切片两种 3.2.5 切片拷贝

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》03-3 哈希表

数据结构 初始化 1 2 3 4 5 mp := map[string]int{ "111": 1, "222": 2, "333": 3, } 当哈希表中的元素数量少于或者等于 25 个时,编译器会将字面量初始化的结构体转换成以下的代码,将所有的键值对一次加入到哈希表中: 1 2 3 4 hash := make(map[string]int, 3) hash["1"] = 2 hash["3"] = 4 hash["5"] = 6 一旦哈希表中元素的数量超过了 25 个,编译器会创建两个数组分别存储键和值,这些键值对会通过如下所示的 for 循环加入哈希: 1 2 3 4 5 6 hash := make(map[string]int, 26) vstatk := []string{"1", "2", "3", ... , "26"} vstatv := []int{1, 2, 3, ....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》03-4 字符串

Go 语言的字符串可以作为哈希的键,所以如果哈希的键是可变的,不仅会增加哈希实现的复杂度,还可能会影响哈希的比较 字符串在 Go 语言中的接口其实非常简单,每一个字符串在运行时都会使用如下的 reflect.StringHeader 表示,其中包含指向字节数组的指针和数组的大小: type StringHeader struct { Data uintptr Len int }

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》04-1 函数调用

C 语言的方式能够极大地减少函数调用的额外开销,但是也增加了实现的复杂度; CPU 访问栈的开销比访问寄存器高几十倍; 需要单独处理函数参数过多的情况; Go 语言的方式能够降低实现的复杂度并支持多返回值,但是牺牲了函数调用的性能; 不需要考虑超过寄存器数量的参数应该如何传递; 不需要考虑不同架构上的寄存器差异; 函数入参和出参的内存空间需要在栈上进行分配; 通过堆栈传递参数,入栈的顺序是从右到左,而参数的计算是从左到右; 函数返回值通过堆栈传递并由调用者预先分配内存空间; 调用函数时都是传值,接收方会对入参进行复制再计算;

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》04-2 接口

接口不包含成员变量 Go 语言中的两种接口 Go 语言使用 runtime.iface 表示第一种接口,使用 runtime.eface 表示第二种不包含任何方法的接口 interface{},两种接口虽然都使用 interface 声明,但是由于后者在 Go 语言中很常见,所以在实现时使用了特殊的类型 结构体和指针实现接口 虽然两种类型不同,但是上图中的两种实现不可以同时存在,Go 语言的编译器会在结构体类型和指针类型都实现一个方法时报错 “method redeclared”。 type Duck interface { Quack() } type Cat struct{} func (c *Cat) Quack() { fmt.Println(“meow”) } func main() { var c Duck = Cat{} c.Quack() } $ go build interface.go ./interface.go:20:6: cannot use Cat literal (type Cat) as type Duck in assignment: Cat does not implement Duck (Quack method has pointer receiver)...

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》04-3 反射

三大原则 interface reflect.TypeOf interface reflect.ValueOf func main() { i := 1 v := reflect.ValueOf(&i) v.Elem().SetInt(10) fmt.Println(i) } 当我们想要更新 reflect.Value 时,就需要调用 reflect.Value.Set 更新反射对象,该方法会调用 reflect.flag.mustBeAssignable 和 reflect.flag.mustBeExported 分别检查当前反射对象是否是可以被设置的以及字段是否是对外公开的 实现协议 reflect 包还为我们提供了 reflect.rtype.Implements 方法可以用于判断某些类型是否遵循特定的接口 type CustomError struct{} func (*CustomError) Error() string { return "" } func main() { typeOfError := reflect.TypeOf((*error)(nil)).Elem() customErrorPtr := reflect.TypeOf(&CustomError{}) customError := reflect.TypeOf(CustomError{}) fmt.Println(customErrorPtr.Implements(typeOfError)) // #=> true fmt.Println(customError.Implements(typeOfError)) // #=> false }

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》05-1 for和range

分析遍历数组和切片清空元素的情况; 分析使用 for range a {} 遍历数组和切片,不关心索引和数据的情况; 分析使用 for i := range a {} 遍历数组和切片,只关心索引的情况; 分析使用 for i, elem := range a {} 遍历数组和切片,关心索引和数据的情况; 复制了一份,所以在循环中append没问题 引入range中的v,v的地址是固定的,只是值更新了 一个优化: cmd/compile/internal/gc.arrayClear 是一个非常有趣的优化,它会优化 Go 语言遍历数组或者切片并删除全部元素的逻辑: 1 2 3 4 5 6 7 8 9 10 11 12 // 原代码 for i := range a { a[i] = zero // 这是置为零值? } // 优化后 if len(a) != 0 { hp = &a[0] hn = len(a)*sizeof(elem(a)) memclrNoHeapPointers(hp, hn) i = len(a) - 1 } 遍历数组和切片时,发生拷贝后,再做循环。...

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Go语言设计与实现》05-2 select

select 能在 Channel 上进行非阻塞的收发操作; select 在遇到多个 Channel 同时响应时,会随机执行一种情况; default go channel从非阻塞收发演变到阻塞收发 数据结构 select 在 Go 语言的源代码中不存在对应的结构体,但是我们使用 runtime.scase 结构体表示 select 控制结构中的 case: 1 2 3 4 type scase struct { c *hchan // chan elem unsafe.Pointer // data element } select 不存在任何的 case; select 只存在一个 case; select 存在两个 case,其中一个 case 是 default; select 存在多个 case; 空的 select 语句会直接阻塞当前 Goroutine,导致 Goroutine 进入无法被唤醒的永久休眠状态。 单一管道改写: 1 2 3 4 5 6 7 8 9 10 11 select { case v, ok <-ch: // case ch <- v ....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀