go 语言函数传参 引用传递&&值传递

关于go 函数传值问题

  • 什么类型变量属于默认引用传值,什么类型变量默认属于复制传值?
  • 如何修改默认的传参方式?

1、int ,int32,int64 类型传参

1
2
3
4
5
6
7
8
9
golang复制代码func toValueInt(val int){
val = 2
}

func main(){
a := 1
toValueInt(a)
fmt.Println(a)
}

执行结果

1
js复制代码1

分析 main 函数创建一个变量并赋值 1,toValueInt(a) 函数修改为2,然后打印结果1,说明默认int 传参默认为 值传递。

下面我们实现int引用传值

1
2
3
4
5
6
7
8
golang复制代码func toValueInt(val *int){
*val = 2
}
func main(){
a := 1
toValueInt(&a)
fmt.Println(a)
}

执行结果

1
js复制代码2

分析 toValueInt 函数为指向int的一个指针,传参的时候通过&符取地址,然后在函数中修改这个值,结果变为2

其他整型类型类似,不做过多解释

2、string 类型传参

1
2
3
4
5
6
7
8
9
gloang复制代码func toValueString(val string){
val = "2"
}

func main(){
b := "1"
toValueString(b)
fmt.Println(b)
}

执行结果

1
js复制代码1

和int类型一样,默认string 传参默认为 值传递

实现引用传值

1
2
3
4
5
6
7
8
9
js复制代码func toValueString(val *string){
*val = "2"
}

func main(){
b := "1"
toValueString(&b)
fmt.Println(b)
}

同理int类型传值

3、数组类型传参

1
2
3
4
5
6
7
8
9
10
11
js复制代码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
js复制代码[1 2 3]

分析 数组类型默认是值传递

实现引用传值

1
2
3
4
5
6
7
8
9
10
11
golang复制代码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
js复制代码[4 5 5]

分析 和上面的类型一致,通过传递指针来实现

4、slice 类型切片传值 ,这个类型比较特殊

首先看一种情况,通过下标赋值

1
2
3
4
5
6
7
8
9
golang复制代码func toValueSplice(val []int){
val[0]= 2
}

func main(){
d:= []int{1}
toValueSplice(d)
fmt.Println(d)
}

输出结果

1
js复制代码[2]

分析下 ,定义数组[]int{1},调用函数 toValueSplice ,通过下标修改数组值,修改成功。

说明slice 在直接通过修改下标的时候是引用传值。

传递的切片长度是1,我们在数组里尝试给 索引1的位置赋值

1
2
3
4
5
6
7
8
9
golang复制代码func toValueSplice(val []int){
val[1]= 2
}

func main(){
d:= []int{1}
toValueSplice(d)
fmt.Println(d)
}

执行结果

1
js复制代码panic: runtime error: index out of range [1] with length 1

说明参数传递进去的数组长度是1,越位赋值。

接着我们通过append追加值

1
2
3
4
5
6
7
8
9
10
golang复制代码func toValueSplice(val []int){
val = append(val,2)
fmt.Println("函数中打印",val)
}

func main(){
d:= []int{1}
fmt.Println("main 函数打印",d)
fmt.Println(d)
}

输出结果

函数中打印

1
js复制代码[1 2]

main 函数打印

1
js复制代码[1]

说明 main函数打印结果仍然是[1],toValueSplice函数中通过append 追加值的时候,打印出结果是[1 2] 说明slice在函数中通过append追加值这是值传递。

总结下

通过下标赋值是引用传值,但是不能改变原参数slice长度

通过append追加值,可以改变参数slice长度,但是不能修改原参数slice 值,属于值传递。

实现引用传值,且修改slice 长度

1
2
3
4
5
6
7
8
9
10
golang复制代码func toValueSplice(val *[]int){
*val = append(*val,2)
fmt.Println("函数中打印",*val)
}

func main(){
d:= []int{1}
toValueSplice(&d)
fmt.Println("main 函数打印",d)
}

执行结果

函数中打印

1
js复制代码[1 2]

main 函数打印

1
js复制代码[1 2]

分析 通过传递地址来修改slice值

5、map传值

1
2
3
4
5
6
7
8
9
golang复制代码func toValueMap(val map[string]int){
val["1"]=2
}

func main(){
e:= map[string]int{"1":1}
toValueMap(e)
fmt.Println(e)
}

输出结果

1
js复制代码map[1:2]

说明,我们在函数里面修改了原值,map类型默认是引用传值

如何打破引用传值

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
golang复制代码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)
}

执行结果

1
js复制代码map[1:1]

分析 在函数toValueMap里面 创建 val1 变量,然后通过for 循环 将 val 的值赋值给 val1,然后在将 val1 赋值给 val。有些小伙伴看到这里就很疑惑,为啥这么麻烦呢?直接 val1 = val ,然后修改 val1 的值,不就可以了吗?这里咱们尝试一下

1
2
3
4
5
6
7
8
9
10
golang复制代码func toValueMap(val map[string]int){
val1 := val
val1["1"]=2
}

func main(){
e:= map[string]int{"1":1}
toValueMap(e)
fmt.Println(e)
}

输出结果

1
js复制代码map[1:2]

说明 原参数值被修改了,map 是引用传值,在函数里面通过等号赋值仍然是引用赋值,恐怖吗?如果在实际中这么用,就回出现混乱,莫名其妙变量值就被意外一个函数修改了。

还有一种打破引用传值的方式是通过,json 序列化来修改

1
2
3
4
5
6
golang复制代码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 类型传值

1
2
3
4
5
6
7
8
9
10
11
12
13
golang复制代码type ss struct {
id int
}

func toValueStruct(s ss){
s.id = 2
}

func main(){
s := ss{1}
toValueStruct(s)
fmt.Println(s)
}

执行结果

1
js复制代码{1}

说明 struct传值默认是值传递

实现引用传递

1
2
3
4
5
6
7
8
9
10
11
12
13
golang复制代码type ss struct {
id int
}

func toValueStruct(s *ss){
s.id = 2
}

func main(){
s := ss{1}
toValueStruct(&s)
fmt.Println(s)
}

执行结果

1
js复制代码{2}

6、chan 类型传值

1
2
3
4
5
6
7
8
9
10
11
js复制代码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
js复制代码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 追加值的时候是值传递

如果觉得文章对您有帮助,请下赞,您的认可是我更新的动力,谢谢大家

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%