Goland的值与引用类型

Goland的值与引用类型

在新的项目中,用到的Goland。以前用Python做项目,Python基本变量的赋值是引用重定向。Goland有些不一样,这里做个测试

直接利用打印变量值、地址的方式确认变量拷贝情况


package main
import "fmt"

func getInt() int {
	i := 5
	println("in getInt, i", i, &i)
	return i
}

func intTest(i int) {
	println("in intTest, i", i, &i)
	i = 99
}

func listTest(list []int) {
	println("in listTest, list", &list, list)
	list[0] = 99
}

func mapTest(mapInt map[int]int) {
	println("in mapTest, mapInt", mapInt, &mapInt)
	mapInt[99] = 100
}

func main() {
	t := 1
	fmt.Println("in main, t", t, &t)
	t = 2
	fmt.Println("in main, t", t, &t)
	t = getInt()
	fmt.Println("in main, t", t, &t)
	intTest(t)
	fmt.Println("in main, t", t, &t)

	fmt.Println("*************************")
	l := []int{1,2,3}
	println("in main, l", &l, l)
	l = []int{1,2,3,4,5}
	println("in main, l", &l, l)

	l2 := l
	println("in main, l2", &l2, l2)
	listTest(l)
	fmt.Println("in main, l", l)

	fmt.Println("*************************")
	mapInt := make(map[int]int)
	mapInt[1] = 2
	println("in main, mapInt", mapInt, &mapInt)
	mapTest(mapInt)
	println("in main, mapInt", mapInt, &mapInt)
	fmt.Println("in main, mapInt", mapInt)
}

输出结果
in main, t 1 0xc0000140a0
in main, t 2 0xc0000140a0
in getInt, i 5 0xc00006ade0
in main, t 5 0xc0000140a0
in intTest, i 5 0xc00006ade8
in main, t 5 0xc0000140a0
*************************
in main, l 0xc00006ae58 [3/3]0xc000012140
in main, l 0xc00006ae58 [5/5]0xc00001a060
in main, l2 0xc00006ae40 [5/5]0xc00001a060
in listTest, list 0xc00006ae28 [5/5]0xc00001a060
in main, l [99 2 3 4 5]
*************************
in main, mapInt 0xc00006adf0 0xc000062180
in mapTest, mapInt 0xc00006adf8 0xc000062180
in main, mapInt 0xc00006adf0 0xc000062180
in main, mapInt map[1:2 99:100]

分析

先看第一组结果,简单int变量t,重新赋值为2后,在内存中的地址不变。与Python不同,Python简单对象赋值是引用重定向,引用一个新的对象,会改变id(obj)的结果。Goland是在原对象的基础上修改,改变原对象中的内容。函数的传参、返回值都会拷贝一个新对象,修改拷贝的对象不影响原对象。

再看第二组结果,切片类型l,打印第一个数值是变量地址,后面接的是[len/cap]数组内存地址,切片独享保留着对数组内存地址的引用。第二次对切片赋值,同样的对象地址不变,其len、cap、数组地址发生变化。在函数listTest中,形参同样会生成一个新的对象,切片拷贝对象的len、cap、数组内存地址与元对象一致,如果改变拷贝切片中的元素,由于其引用同一个连续数组内存,原对象上也会得到体现,最后main中l[0]=99

再看第三组结果,map类型,打印的第一个值是map变量的地址,第二个参数是map对象引用的数据块地址。同样的,map对象作为形参,会拷贝出一个新对象,与list类似,新老对象引用的数据块地址一致,修改拷贝对象数据块中的数据,会影响老对象的数据。

总结

  • Goland对存在的对象重新赋值,会改变原对象内存中的数据,与Python引用重定向相区别
  • Goland中slices、map、channel为引用类型,其余简单类型为值类型,值类型对象中空间中包含了对象所需的全部内容,引用类型除了基本信息外还引用着一块可以共享的数据块
  • Goland对象作为函数参数、函数返回值、对象间赋值、copy等机制会从老对象拷贝出一个新的对象。这里是浅拷贝机制,值类型完全复制,共用引用数据块,复制引用地址。
  • 修改拷贝对象时,拷贝对象是引用类型,且修改的是引用数据块中的数据,会影响所有引用该数据块引用对象,这里容易出bug。
  • 深拷贝可考虑序列化、反序列化来实现,深拷贝都比较耗时,非必须不建议使用

你可能感兴趣的:(编程,Go,Golang)