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