一、为什么说切片是引用类型,切片在函数之中传递会影响原有值
Go类型:基本类型(bool string int array)、引用类型(切片\map\interface\func\channel)、 结构类型、 自定义类型
引用类型,标头值里包含一个指 针,指向底层的数据结构,当我们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并没有被复制传递,这也是引用类型传递高效的原因
比如切片(引用类型)和数组(基本类型)
1、切片原理,可以知道切片其实内部是一个unsafearray ,非线程安全数组的指针,对切片操作其实是对这个内部的数组进行操作(容量变更会重新生成新的数组替换)
2、函数之间传递变量,如下面的p变量,他们的地址不是同一个,因为p变量的值会交给S函数,S函数内部会重新生成一个变量存储它,只是由于存储的是切片指向的同一个底层数组的指针,所以才会带来原有值的变化
也就是说:
func TestSliceTrans(t *testing.T) { var p = []string{"a"} fmt.Printf("TestSliceTrans %p %s \n", &p, p) S(p) fmt.Printf("TestSliceTrans %p %s \n", &p, p) } func S(l []string) { l[0] = "b" fmt.Printf("S %p %s \n", &l, l) }
输出
TestSliceTrans 0xc0000be180 [a] S 0xc0000be1b0 [b] TestSliceTrans 0xc0000be180 [b]
即使 S 函数里面的 l 的指针和 上层不一致,但是这个切片指向的内部的数组是同一个,所以 S 函数内更改了切片的值
func P(p Person) { p.Name = "rose" fmt.Printf("P %p\n", &p) } func TestSliceTrans(t *testing.T) { var p = []string{"a"} fmt.Printf("TestSliceTrans %p %s \n", &p, p) S(p) fmt.Printf("TestSliceTrans %p %s \n", &p, p) } func S(l []string) { l = append(l, "b") fmt.Printf("S %p %s \n", &l, l) }
输出
TestSliceTrans 0xc00000e198 [a] S 0xc00000e1c8 [a b] TestSliceTrans 0xc00000e198 [a]
二、为什么切片传递之中有时候不会影响原有的值
为什么这里的切片值没有被改变呢,这里就涉及切片的append的时候的扩容方式,在append的时候切片会将底层指向的数组更换为新的数组,也就是说 append之后对之前的底层指向的数组是没有做操作的,所以不会改变值
结论:函数操作之中,判定被调用者是否会变更原来的变量的值,本质上是判定两个函数之间的变量内部存储的是指针还是值,也就是说判定下游函数操作数据的时候操作的是指针还是值,而对于切片、chan(两个底层都是一个变量的指针)那么传递过去的话,变量的操作如果没有变更底层的指针的话操作的就是同一个变量
参考地址:
https://blog.csdn.net/weixin_52690231/article/details/123967274#11_5
https://www.jianshu.com/p/fbcaa36cff1c Map实现原理