Go xmas2020 学习笔记 14、Reference & Value Semantics
作者:互联网
课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)
主讲老师 Matt Holiday
14-Reference & Value Semantics
Pointers vs Values
如果要共享变量并修改,建议统一用指针传递的方式,否则 \(f3\) 返回的是原来的副本,\(f4\) 作出的修改将无法反映到 \(f1\)、\(f2\) 修改的对象上。即针对一个对象的修改却产生了两个对象。
Loop Gotcha
循环内第二个参数拿到的是副本,要在循环内修改原切片字段的值不能直接修改副本,需要通过索引进行修改。
在函数内修改切片最好将切片返回,因为修改切片很可能会导致切片描述符指向的底层数组地址发生改变,比如 \(grow\) 扩容。
将指针指向切片内的元素是极其危险的。当切片描述符指向的底层数组扩容时,会导致指针指向已经过时的底层数组。再通过指针修改元素会导致修改无效。
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\) 无法工作。所以必须给定长度。
不要引用用于循环的变量。在循环内部声明新变量将其特殊化。
标签:14,Reference,修改,items,fmt,切片,xmas2020,item,byte 来源: https://www.cnblogs.com/linxiaoxu/p/16183201.html