Skip to main content

params

参数传递:值、引用及指针之间的区别?

复习接口实现的知识,再解答关于指向接口的指针的思考题

我们知道值的接受者实现了某个接口,那么指针接受者也一定实现了该接口,来看下面的例子printString()传入值,指针都可以正常输出

package main

import "fmt"

func main() {
add:=address{
province: "广东省",
city: "深圳市",
}
printString(add)
printString(&add)
}

type address struct {
province string
city string
}

func (addr address) String() string {
return fmt.Sprintf("the addr is %s%s",addr.province,addr.city)
}

func printString(s fmt.Stringer) {
fmt.Println(s.String())
}

那么是否可以定义一个指向接口的指针?

来看下面的例子:

可以看到IDE已经红色警告了,我们来运行上述代码:

总结:虽然指向具体类型的指针可以实现一个接口,但指向接口的指针永远不可能实现该接口

修改参数

来看下面的示例:

运行发现,并没有修改

接下来我们把参数换成指针,因为我们知道可以通过操作指针来修改指针指向的数据

这些代码用于满足指针参数的修改,把接收的参数改为指针参数及在调用modifyPerson函数时,通过&取地址符传递一个指针

再次运行:

发现我们已经修改成功了

值类型

上述示例中,person是一个值类型;&p获取的指针是*person类型的,即指针类型

为什么值类型在参数传递中无法被修改呢

这就要从内存开始讲起

我们知道:变量的值存储在内存中,内存都有一个编号称为内存地址,想要修改内存中的数据,就要找到这个内存地址

还是以上述示例为例,我们来打印一下person在函数内外的内存地址(person在调用前后的地址):

运行:

发现main函数的person和modifyPerson中的person压根儿同一个东西,modifyPerson只是person的拷贝而已,虽数据一样但内存地址不一样所以无法修改

总结

go语言的函数传参都是值传递:值传递指的是传递原来数据的一份拷贝而不是原来的数据本身

指针类型

指针类型的变量保存值就是数据对应的内存地址

所以在函数参数传递是传值的原则下

拷贝的值也是内存地址

小提示:值传递的是指针也是内存地址

通过内存地址可找到原数据的那块内存,修改它就等于修改原数据

引用类型

  • map
  • chan

map

运行:

这里没有使用指针,只用了map类型的参数

按照go语言值传递的原则,modifyMap函数中的map是副本

怎么会修改成功了呢?

要解决这个问题就要从go语言内置的make函数说起

go语言中无论以任何形式创建的map代码,最终调用的都是runtime.makemap函数

小提示

用字面量或make方式创建的map,编译器会自动帮我们转化为makemap函数的调用

通过下面的代码可以看到makemap方法返回的是一个*hmap的指针,所以上述代码modifyMap函数其实接收的是一个*hmap的指针,所以可以修改

我们来打印一下地址:

运行:

内存地址一模一样,所以才可以修改原数据,得到年龄是20的结果

chan

通过下面的源代码可以发现,channel本质上也是指针

总结:go语言没有引用类型,但可以吧map、chan称为引用类型

除了map、chan外,go语言中的函数,接口,slice切片都可称为引用类型

小提示

指针类型也可理解为引用类型

类型的零值

go语言中,定义变量通过声明或通过make和new函数,make和new函数属于显示声明并初始化

如声明变量没有显式声明初始化那该变量的默认值就是对应类型的零值

总结

在go语言中,函数的参数传递只有值传递

且传递的实参都是原始数据的一份拷贝

如拷贝内容是值类型,那在函数中无法修改原数据

如拷贝的内容是指针类型,那就可在函数中修改原数据

所以在创建函数时,要根据真实的需求决定参数的类型,以便更好的服务业务