go值拷贝
概念
值类型
值类型的数据,默认全部都是深复制,Array、Int、String、Struct、Float,Bool。
引用类型
引用类型的数据,返回的是值的地址,指针,切片,channel,interface,map,函数等
深拷贝
拷贝的是数据本身,创建一个新的对象,在内存中开辟一块新的地址。
package main
import "fmt"
type User struct {
Name string
}
func changeValue(m User) {
m.Name = "kang"
fmt.Printf("m=%s\taddr=%p \n", m, &m)
}
func main() {
// struct
var structfa User
structfa.Name = "liu"
fmt.Printf("a=%s\taddr=%p \n", structfa, &structfa)
changeValue(structfa)
fmt.Printf("a=%s\taddr=%p \n", structfa, &structfa)
}
a={liu} addr=0xc000010230
m={kang} addr=0xc000010250
a={liu} addr=0xc000010230
浅拷贝
拷贝的是数据地址,只复制指向的对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化。释放内存地址时,同时释放内存地址。
package main
import "fmt"
func changeValue(m []int) {
fmt.Printf("m=%v\taddr=%p \n", m, &m)
m[2] = 8
}
func main() {
arr := [4]int{10, 20, 30, 40}
slice := arr[0:2]
testSlice1 := slice // slice和testSlice1指向同一个底层数组
testSlice2 := append(slice, 1) // 没有超过容量,修改的还是底层数组
fmt.Printf("arr=%v\taddr=%p \n", arr, &arr) // 此时底层数组变为[10 20 1 40]
testSlice2 = append(testSlice2, 2, 3) // 超过底层数组,重新开辟空间
testSlice2[2] = 5 // 此时再修改不会改变arr
fmt.Printf("testSlice1=%v\taddr=%p \n", testSlice1, &testSlice1)
fmt.Printf("testSlice2=%v\taddr=%p \n", testSlice2, &testSlice2)
fmt.Printf("arr=%v\taddr=%p \n", arr, &arr)
changeValue(testSlice2) // 浅拷贝,值传递,传递的是切片的地址
fmt.Printf("testSlice2=%v\taddr=%p \n", testSlice2, &testSlice2) // 修改的是同一块内存
}
arr=[10 20 1 40] addr=0xc000136000
testSlice1=[10 20] addr=0xc000126018
testSlice2=[10 20 5 2 3] addr=0xc000126030
arr=[10 20 1 40] addr=0xc000136000
m=[10 20 5 2 3] addr=0xc000126078
testSlice2=[10 20 8 2 3] addr=0xc000126030
值传递
概括
如果函数传参是值类型,会复制值的副本。如果函数传参是引用类型,就会复制这个引用指针的值。
结构体 struct
看下结构体的例子:
package main
import "fmt"
type Person struct {
Name string
Age int64
}
func main() {
per := Person{
Name: "liu",
Age: int64(8),
}
fmt.Printf("原始struct地址是:%p\n", &per)
modifiedAge(per)
fmt.Println(per)
}
func modifiedAge(per Person) {
fmt.Printf("函数里接收到struct的内存地址是:%p\n", &per)
per.Age = 10
}
看下结果,实际没有改变原结构体的值
原始struct地址是:0xc00000c030
函数里接收到struct的内存地址是:0xc00000c048
{liu 8}
管道 channel
func main() {
p := make(chan bool)
fmt.Printf("原始chan的内存地址是:%p\n", &p)
go func(p chan bool) {
fmt.Printf("函数里接收到chan的内存地址是:%p\n", &p)
//模拟耗时
time.Sleep(2 * time.Second)
p <- true
}(p)
select {
case l := <-p:
fmt.Println(l)
}
}
程序正常执行,说明使用的是同一个管道,只是传参的时候传递的是指针的值。具体可以看下源码 makechan
,实际创建的是一个引用
原始chan的内存地址是:0xc0000ac018
函数里接收到chan的内存地址是:0xc0000ac028
true