Go xmas2020 学习笔记 14、Reference & Value Semantics

课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)

主讲老师 Matt Holiday

image-20220401081031592

14-Reference & Value Semantics

Pointers vs Values

image-20220423174451039

image-20220423174951051

如果要共享变量并修改,建议统一用指针传递的方式,否则 \(f3\) 返回的是原来的副本,\(f4\) 作出的修改将无法反映到 \(f1\)\(f2\) 修改的对象上。即针对一个对象的修改却产生了两个对象。


image-20220423175515747

Loop Gotcha

image-20220423175628859

循环内第二个参数拿到的是副本,要在循环内修改原切片字段的值不能直接修改副本,需要通过索引进行修改。


image-20220423175938721

在函数内修改切片最好将切片返回,因为修改切片很可能会导致切片描述符指向的底层数组地址发生改变,比如 \(grow\) 扩容。


image-20220423180254232

将指针指向切片内的元素是极其危险的。当切片描述符指向的底层数组扩容时,会导致指针指向已经过时的底层数组。再通过指针修改元素会导致修改无效。


package main

import "fmt"

func main() {
	items := [][2]byte{{1, 2}, {3, 4}, {5, 6}}
	a := [][]byte{}
	for _, item := range items {
		a = append(a, item[:])
	}
	fmt.Println(items)
	fmt.Println(a)
}
[[1 2] [3 4] [5 6]]
[[5 6] [5 6] [5 6]]

因为 \(item\) 是切片元素的副本,所以是两字节数组,在内存中有特定位置,每次循环获得到的副本都在内存的同一个地方。当循环结束后,最后两个字节数组是 \(5、6\) ,而向 \(a\) 添加的是三个相同的 \(item\) 引用,所以都将引用 \(item\) 的最终值。修复这种方法的方法是在每次循环内部声明一个新变量。

func main() {
	items := [][2]byte{{1, 2}, {3, 4}, {5, 6}}
	a := [][]byte{}
	for _, item := range items {
		i := make([]byte, len(item))
		copy(i, item[:])
		a = append(a, i)
	}
	fmt.Println(items)
	fmt.Println(a)
}
[[1 2] [3 4] [5 6]]
[[1 2] [3 4] [5 6]]

如果给定 \(i\)\(length\)\(0\),会导致 \(copy\) 无法工作。所以必须给定长度。


image-20220423181837165

不要引用用于循环的变量。在循环内部声明新变量将其特殊化。

image-20220423182027032

posted @ 2022-04-23 18:25  小能日记  阅读(4)  评论(0编辑  收藏  举报