Tips:go里面的参数传递都是值传递
一般情况下的值传递
- 在Go里面的参数传递都是值传递,这意味着当你将一个参数传递给一个函数时,实际上是传递了该参数值的一个副本。因此,函数内部对改参数值的修改不会影响到函数外部的原始值
- 这里的一般情是指传递的参数类型是(如 int、float、bool 等)和结构体(struct)
type tempData struct {
name string
age int
other []string
}
func chgValue(aValue int) {
fmt.Println("chgValue() 中修改前的 aValue =", aValue, " &aValue =", &aValue)
aValue = aValue + 10
fmt.Println("chgValue() 中修改后的 aValue =", aValue, " &aValue =", &aValue)
}
func chgStruct1(aValue tempData) {
fmt.Printf("chgStruct1() 中修改前的 aValue =%+v &aValue =%p\n", aValue, &aValue)
aValue.name = "xiaoyin_03"
aValue.age = 5
aValue.other = append(aValue.other, "这是新增")
aValue.other[0] = "修改啦"
fmt.Printf("chgStruct1() 中修改后的 aValue =%+v &aValue =%p\n", aValue, &aValue)
}
func TestDemo(t *testing.T) {
mValue := 5
fmt.Println("原始数据 mValue =", mValue, " &mValue =", &mValue)
chgValue(mValue)
fmt.Println("经过 chgValue() 后 mValue =", mValue, " &mValue =", &mValue)
fmt.Println("================================================")
fmt.Println("================================================")
mTempData := tempData{
name: "xiaoyin",
age: 26,
other: []string{"xiaoyin1", "xiaoyin2"},
}
fmt.Printf("原始 mTempData =%+v &mTempData =%p\n", mTempData, &mTempData)
chgStruct1(mTempData)
fmt.Printf("经过 chgStruct1() 后 mTempData =%+v &mTempData =%p\n", mTempData, &mTempData)
}
执行结果:
从上面的执行结果来看很容易知道,当一般情况是值传递的时候方法里面使用传递进来的参数完全是另一个,里面对这个值进行修改并不会对外面的数据有影响
在看特殊的情况,感觉不是值传递的情况-引用类型参数
- 对于引用类型,虽然也是值传递,但传递的是指针的副本,这个副本指向的是同一个底层数据。因此,在函数内部通过指针可以修改原始数据
- 在Go里面引用类型常见的有【切片(slice)、map、通道(channel)、接口(interface)和函数(function)】
type tempData struct {
name string
age int
other []string
}
func chgSlice(aSlice []int) {
fmt.Printf("chgSlice() 中修改前的 aSlice 第一个元素地址: %p\n", unsafe.Pointer(&aSlice[0]))
fmt.Printf("chgSlice() 中修改前的 aSlice =%+v &aSlice =%p\n", aSlice, &aSlice)
aSlice = append(aSlice, 666)
aSlice[0] = 999
fmt.Printf("chgSlice() 中修改后的 aSlice =%+v &aSlice =%p\n", aSlice, &aSlice)
fmt.Printf("chgSlice() 中修改后的 aSlice 第一个元素地址: %p\n", unsafe.Pointer(&aSlice[0]))
}
func chgStruct2(aValue *tempData) {
fmt.Printf("chgStruct2() 中修改前的 aValue =%+v 地址 =%p\n", aValue, aValue)
aValue.name = "xiaoyin_03"
aValue.age = 5
aValue.other = append(aValue.other, "这是新增")
aValue.other[0] = "修改啦"
fmt.Printf("chgStruct2() 中修改后的 aValue =%+v 地址 =%p\n", aValue, aValue)
}
func TestDemo2(t *testing.T) {
mSlice := make([]int, 0)
mSlice = append(mSlice, 1)
mSlice = append(mSlice, 2)
mSlice = append(mSlice, 3)
fmt.Printf("原始 mSlice =%+v &mSlice =%p\n", mSlice, &mSlice)
fmt.Printf("原始 mSlice 第一个元素地址: %p\n", unsafe.Pointer(&mSlice[0]))
chgSlice(mSlice)
fmt.Printf("经过 chgSlice() 后 mSlice =%+v &mSlice =%p\n", mSlice, &mSlice)
fmt.Printf("经过 chgSlice() 后 mSlice 第一个元素地址: %p\n", unsafe.Pointer(&mSlice[0]))
fmt.Println("================================================")
fmt.Println("================================================")
mTempData := tempData{
name: "xiaoyin",
age: 26,
other: []string{"xiaoyin1", "xiaoyin2"},
}
fmt.Printf("原始 mTempData =%+v &mTempData =%p\n", mTempData, &mTempData)
chgStruct2(&mTempData)
fmt.Printf("经过 chgStruct2() 后 mTempData =%+v &mTempData =%p\n", mTempData, &mTempData)
}
执行结果:
- 可以从上面切片的那部分看到,就算是切片在进行传值的时候,从明面上面看也是值传递,原始数据切片的地址和传到方法
chgSlice()
中的参数地址并不相同,所以这里就可以应征go里面所有的传参都是值传递。
但是切片也算是有点特殊的,切片底层是指向数组的一个指针,然后在进行值拷贝的时候底层指向数组的地址值给拷贝了,所以虽然看切片是值传递,但是其内部指向的数组是同一个,如果出现代码中的追加,改变了数组的长度和容量,这两个数据改变外面切片内部的这两个数据是不会跟着改变的。
如果是改变外部切片范围内的数据,方法里面改了或者方法外面改了,双边都是同步改动的,因为他们底层指向的数组是同一个,所以任意一方修改后另一方的数据都是会跟着改变的。- 在看后面部分,把一个接头体的指针作为值进行传递,这就很好理解,不管是方法内还是方法外面的他们双方所指向的都是同一块内存数据,所以修改会同步。
总结
- Go中所有的传参都是值传递
- 一般类型(如 int、float、bool 等)和结构体(struct)默认是按值传递的,函数接收到的是副本
- 指针类型可以用来实现按引用传递,函数接收到的是指向原始数据的指针。
- 引用类型(如指针、切片、map、通道、接口和函数)虽然按值传递,但它们的底层实现是引用类型,因此可以在函数内部修改原始数据。
一点点笔记,以便以后翻阅。