普通变量都是值传递,通过新开辟一篇内存并让引用指向这片内存
a := 1
b := a
fmt.Printf("%p\n%p", &a, &b)
// 0xc0000160a8
// 0xc0000160c0
a := 1.0
b := a
fmt.Printf("%p\n%p", &a, &b)
// 0xc0000160a8
// 0xc0000160c0
a := "hello"
b := a
fmt.Printf("%p\n%p", &a, &b)
// 0xc0000160a8
// 0xc0000160c0
其他基本类型也是相同…
2. array数组赋值时地址不同,创建了一个新的副本,同样是值传递
a := [2]int{1, 2, 3}
b := a
fmt.Printf("%p\n%p", &a, &b)
// 0xc0000160a8
// 0xc0000160c0
3. slice
新的切片改动会对原来的造成影响
a := []int{}
b := a
fmt.Printf("%p\n%p\n", &a, &b)
// 0xc000004078
// 0xc000004090
注意:切片赋值底层指向的是同一个数组,只有通过 append 进行增加元素时发生了扩容,切片才会指向新的数组
4. map新的 map 中的元素实际地址与原来相同,说明会对原来的map造成影响
a := map[int]string{1: "hi"}
b := a
fmt.Printf("%p\n%p\n", &a, &b)
a[1] = "go"
fmt.Printf("%s\n%s\n", a[1], b[1])
// 0xc000006028
// 0xc000006030
// go
// go
5. 结构体
结构体是值传递,其中普通的字段重新创建一个副本,指针字段还指向原来的地址(解释了 slice 和 map ,slice 和 map 底层就是有一个指针分别指向一个普通数组和一个bucket数组的结构)
a := []Student{{1}, {2}}
b := a
fmt.Printf("%p\n%p\n", &a, &b)
// 0xc000004078
// 0xc000004090
6. 总结
- array 、结构体可以通过直接赋值并对新的结构体操作,不会产生安全问题
- slice,map 不推荐直接赋值
- slice 可以通过 copy 函数进行复制
- map 可以通过遍历来复制
- 实际上 go 使用的都是值传递,也就是复制一份新的副本,slice 和 map 特殊在它们本身就是一种结构体,这个结构体内部包含了一个指向实际数据的索引,在他们进行副本拷贝时这个索引还是指向原来的位置,所以拷贝后的影响还是能在原来的 slice 或 map 看到
其实 go 中赋值的过程与他们作为参数传递的过程都是一样的都是值传递(包括结构体),slice 和 map 的底层也是结构体。