关于go 函数传值问题
- 什么类型变量属于默认引用传值,什么类型变量默认属于复制传值?
- 如何修改默认的传参方式?
1、int ,int32,int64 类型传参
func toValueInt(val int){
val = 2
}
func main(){
a := 1
toValueInt(a)
fmt.Println(a)
}
复制代码
执行结果
1
复制代码
分析 main 函数创建一个变量并赋值 1,toValueInt(a) 函数修改为2,然后打印结果1,说明默认int 传参默认为 值传递。
下面我们实现int引用传值
func toValueInt(val *int){
*val = 2
}
func main(){
a := 1
toValueInt(&a)
fmt.Println(a)
}
复制代码
执行结果
2
复制代码
分析 toValueInt 函数为指向int的一个指针,传参的时候通过&符取地址,然后在函数中修改这个值,结果变为2
其他整型类型类似,不做过多解释
2、string 类型传参
func toValueString(val string){
val = "2"
}
func main(){
b := "1"
toValueString(b)
fmt.Println(b)
}
复制代码
执行结果
1
复制代码
和int类型一样,默认string 传参默认为 值传递
实现引用传值
func toValueString(val *string){
*val = "2"
}
func main(){
b := "1"
toValueString(&b)
fmt.Println(b)
}
复制代码
同理int类型传值
3、数组类型传参
func toValueList(val [3]int){
val[0]=4
val[1]=5
val[2]=5
}
func main(){
c := [3]int{1,2,3}
toValueList(c)
fmt.Println(c)
}
复制代码
输出结果
[1 2 3]
复制代码
分析 数组类型默认是值传递
实现引用传值
func toValueList(val *[3]int){
val[0]=4
val[1]=5
val[2]=5
}
func main(){
c := [3]int{1,2,3}
toValueList(&c)
fmt.Println(c)
}
复制代码
**输出结果 **
[4 5 5]
复制代码
分析 和上面的类型一致,通过传递指针来实现
4、slice 类型切片传值 ,这个类型比较特殊
首先看一种情况,通过下标赋值
func toValueSplice(val []int){
val[0]= 2
}
func main(){
d:= []int{1}
toValueSplice(d)
fmt.Println(d)
}
复制代码
输出结果
[2]
复制代码
分析下 ,定义数组[]int{1},调用函数 toValueSplice ,通过下标修改数组值,修改成功。
说明slice 在直接通过修改下标的时候是引用传值。
传递的切片长度默认是1,我们在数组里尝试给 1 的位置赋值
func toValueSplice(val []int){
val[1]= 2
}
func main(){
d:= []int{1}
toValueSplice(d)
fmt.Println(d)
}
复制代码
执行结果
panic: runtime error: index out of range [1] with length 1
复制代码
说明参数传递进去的数组长度是1,越位赋值。
接着我们通过append追加值
func toValueSplice(val []int){
val = append(val,2)
fmt.Println("函数中打印",val)
}
func main(){
d:= []int{1}
fmt.Println("main 函数打印",d)
fmt.Println(d)
}
复制代码
输出结果
函数中打印
[1 2]
复制代码
main 函数打印
[1]
复制代码
说明 main函数打印结果仍然是[1],toValueSplice函数中通过append 追加值的时候,打印出结果是[1 2] 说明slice在函数中通过append追加值这是值传递。
总结下
通过下标赋值是引用传值,但是不能改变原参数slice长度
通过append追加值,可以改变参数slice长度,但是不能修改原参数slice 值,属于值传递。
实现引用传值,且修改slice 长度
func toValueSplice(val *[]int){
*val = append(*val,2)
fmt.Println("函数中打印",*val)
}
func main(){
d:= []int{1}
toValueSplice(&d)
fmt.Println("main 函数打印",d)
}
复制代码
执行结果
函数中打印
[1 2]
复制代码
main 函数打印
[1 2]
复制代码
分析 通过传递地址来修改slice值
5、map传值
func toValueMap(val map[string]int){
val["1"]=2
}
func main(){
e:= map[string]int{"1":1}
toValueMap(e)
fmt.Println(e)
}
复制代码
输出结果
map[1:2]
复制代码
说明,我们在函数里面修改了原值,map类型默认是引用传值
如何打破引用传值
方法一
func toValueMap(val map[string]int){
val1 := map[string]int{}
for k,v := range val{
val1[k] = v
}
val = val1
val["1"]=2
}
fun main(){
e:= map[string]int{"1":1}
toValueMap(e)
fmt.Println(e)
}
复制代码
执行结果
map[1:1]
复制代码
分析 在函数toValueMap里面 创建 val1 变量,然后通过for 循环 将 val 的值赋值给 val1,然后在将 val1 赋值给 val。有些小伙伴看到这里就很疑惑,为啥这么麻烦呢?直接 val1 = val ,然后修改 val1 的值,不就可以了吗?这里咱们尝试一下
func toValueMap(val map[string]int){
val1 := val
val1["1"]=2
}
func main(){
e:= map[string]int{"1":1}
toValueMap(e)
fmt.Println(e)
}
复制代码
输出结果
map[1:2]
复制代码
说明 原参数值被修改了,map 是引用传值,在函数里面通过等号赋值仍然是引用赋值,恐怖吗?如果在实际中这么用,就回出现混乱,莫名其妙变量值就被意外一个函数修改了。
还有一种打破引用传值的方式是通过,json 序列化来修改
func toValueMap(val map[string]int){
var val1 map[string]int
str,_ := json.Marshal(val)
json.Unmarshal(str,&val1)
val1["1"]=2
}
复制代码
代替了 for range
5、struct 类型传值
type ss struct {
id int
}
func toValueStruct(s ss){
s.id = 2
}
func main(){
s := ss{1}
toValueStruct(s)
fmt.Println(s)
}
复制代码
执行结果
{1}
复制代码
说明 struct传值默认是值传递
实现引用传递
type ss struct {
id int
}
func toValueStruct(s *ss){
s.id = 2
}
func main(){
s := ss{1}
toValueStruct(&s)
fmt.Println(s)
}
复制代码
执行结果
{2}
复制代码
6、chan 类型传值
var ch chan int
func toValueChan(ch chan int){
ch <-2
}
func main(){
ch := make(chan int,2)
ch <- 1
toValueChan(ch)
fmt.Println( <-ch,<-ch)
}
复制代码
输出结果
1 2
复制代码
说明 main 函数里面创建了 ch,并且长度为2,然后写入1 ,接着调用 toValueChan(ch),函数里面写入2,通过 fmt.Println( <-ch,<-ch),连续读取两次,得到1,2 ,说明 toValueChan 里面对 ch 变量修改是生效了的 ,chan 类型是引用传值。
chan 打破引用传值,这个人建议觉得没有多大用,现实使用中更多是用引用传值。
总结
int ,int32,int64,string,list,struct 这几种类型是值传递
map,chan 是引用传值
splice 在通过下标赋值的时候是引用传值,通过append 追加值的时候是值传递
如果觉得文章对您有帮助,请下赞,您的认可是我更新的动力,谢谢大家