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

关于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 追加值的时候是值传递

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

猜你喜欢

转载自juejin.im/post/7019240935124893709
今日推荐