Go切片是值传递还是引用传递?

发布时间 2023-12-03 21:59:51作者: _Eternity味道

Go没有引用传递和引用类型!!!

很多人有个误区,认为涉及Go切片的参数是引用传递,或者经常听到Go切片是引用类型这种说法,今天我们就来说一下方面的问题。

什么是值传递?
将实参的值传递给形参,形参是实参的一份拷贝,实参和形参的内存地址不同。函数内对形参值内容的修改,是否会影响实参的值内容,取决于参数的本身

什么是引用传递?
将实参的地址传递给形参,函数内对形参值内容的修改,将会影响实参的值内容。Go语言是没有引用传递的,在C++中,函数参数的传递方式有引用传递。
————————————————
版权声明:本文为CSDN博主「走,我们去吹风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_51991615/article/details/130345352

首先明确几个点:

  • 1、官方明确表明:Go中没有引用类型,链接在这
    image
  • 2、官方明确表明:Go中function所有参数的传递都是值传递,链接在这
    image
  • 3、Golang默认是值传递,即拷贝传递;
  • 4、有些值头部head中含有地址,如(切片,map类型),所以导致看起来像是引用传递

那么问题来了,既然Go没有引用类型的说法,且function的参数都是值传递,为什么当我使用切片slice或者map等类型作为参数时,有传引用的错觉呢?

/**
 * 测试切片不是引用传递
 */
func TestPassByValue(t *testing.T) {
	sli := []int{0, 1, 2, 3, 4}
	fmt.Printf("sli的地址:%p\n", unsafe.Pointer(&sli))
	fmt.Printf("sli[0]的地址:%p\n", unsafe.Pointer(&sli[0])) 
	//unsafe.Pointer(&sli[0])是一个指针不假,但它也仅仅是切片结构内包含的一个值
	t.Log(sli)
	modifySlice(sli)
	t.Log(sli)
}
func modifySlice(s []int) {
	//slice的底层大概如下:
	type slice struct {
		array unsafe.Pointer   // 就是这里的unsafe.Pointer(&s[0]),因为是值传递,所以这个指针是跟外面一样的
		len int
		cap int
	}
	fmt.Printf("参数的地址:%p\n", unsafe.Pointer(&s))
	fmt.Printf("参数[0]的地址: %p \n", unsafe.Pointer(&s[0]))
	s[0] = 111 //此时直接修改参数的值,外面的值也跟着同步修改
	s[1] = 222 //但当你进行append操作或者二倍扩容时,&s[0]的地址就会改变,数外的切片就不会跟着改变了。
}

总结
之所以有传引用的错觉是因为slice本质是一个结构体,结构体内有个指针值,而这个值指向切片的第一个元素,因为是值传递,所以slice内的这个指针跟函数外边是一样的,此时,你在函数内修改切片的某个值,函数外会跟着改变。 但当你进行append操作或二倍扩容时,这个指针值就会改变,此时,你再次在函数内对切片进行修改,函数外不会跟着改变。