golang slice 内存泄露_Golang 的传参方式探秘

在编程语言中有两种传参方式,1. 传值 2. 传引用。有的资料中也会把传指针单独列出来。不同的传参方式决定了我们对函数入参的使用方式, 比如是不是可以直接修改参数值,调用方可以感知到变量的修改。传参方式也会影响对内存的使用效率,如果参数是一个大对象,按值传递方式就会创建1个完全一样的拷贝,使内存占用直接提升了1倍。先用1个函数测试一下golang中的传参方式究竟是按值传递还是按引用传递。
package mainimport "fmt"type MyType struct {  Val int32}func main() {  val := MyType {    Val: 2,  }  fmt.Printf("before, val: %#v \n", val)  changeStructValue(val)  fmt.Printf("after: %#v \n", val)}func changeStructValue(t MyType) {  t.Val += 1  fmt.Printf("val in changeStructValue: %#v \n", t)}
运行结果:

352b44949784f6bab80395407b70a9fd.png

被调用函数内修改值后调用方的值没有改变。得出 结论: Golang的传参方式是按值传递。计算机的世界有时候很简单,又很复杂,程序运行真的像我们想的那样,按值传递就可以随意修改入参的值而对原值不产生任何影响吗?来看下面的代码。
package mainimport "fmt"type MyType struct {  Val int32}func main() {  slice := []int32{1,2,3}  fmt.Printf("before: %#v \n", slice)  changeSliceValue(slice)  fmt.Printf("after: %#v \n", slice)}func changeSliceValue(slice []int32) {  if len(slice) == 0 {    return  }  slice[0] += 1  fmt.Printf("val in changeSliceValue: %#v \n", slice)}
运行结果:

0dcd782608193e61acc09252cc883c97.png

传递到changeSliceValue中的切片内容竟然被修改了?!在Golang中数组、切片、map都存在于一个结构体中,具体的数据存储在结构体指向的一块内存区域中,只要不做内存的重新分配和空间扩容,函数参数和原变量修改的就是同一块内存空间。想进一步了解的朋友可以探索一下interface类型。 结论: 不要轻易修改任何传入的map、slice、array类型变量 。 Golang 可以在类型上创建方法, 方法的 receiver 既可以是 Type, 也可以是 *Type,  receiver是怎么传值的呢?官方文档中有介绍,应该把方法的 receiver 理解为一个特殊的参数,在方法运行时会为 receiver 创建1个拷贝, 所以它也是按值传递的。可以做一下验证。
package mainimport "fmt"type MyInt int32func (t MyInt) Print() {  fmt.Printf("MyInt Print, pointer to t: %p, val: %d \n", &t, t)  t = t + 1  fmt.Printf("MyInt Print, after change pointer to t: %p, val: %d \n", &t, t)}func methodPssparaType() {  var t MyInt  t = 99  fmt.Printf("before, addr: %p, val: %d \n", &t, t)  t.Print()  fmt.Printf("after, addr: %p, val: %d \n", &t, t)}func main() {  methodPssparaType()}
运行结果:

c3f1b578aa8e5da28b5881fab05918a2.png

如果想要在method中修改值就应该传递指针, 对于函数和方法都适用。下面列出几个 《FAQ》 中给出的在声明方法时的建议, 建议中使用 T 和 *T 区分 receiver 是值还是指针。(注意:T 和 *T 对应不同的方法集(method set), *T 包含在 T 上声明的方法)1. 是否要修改 receiver 的值。如果不需要修改receiver的值,应该在T上创建方法, 否则receive应该为 *T;2. 效率。如果 T 需要分配大量的内存空间,应该把方法建立在 *T 上。3. 一致性。如果存在 receiver 为 *T 的方法, 则应该改写 T 的方法到 *T 上。Golang传参方式的内容介绍完了。下面介绍Golang中很 tricky 的一个东西。猜想一下下面代码的运行结果是什么?
package mainimport "fmt"type GreetType struct{}func (t *GreetType) Greet() {  fmt.Println("hello")}func callOnNilReceiver() {  var t *GreetType = nil  t.Greet()}func main() {  callOnNilReceiver()}
运行结果:

44888dc764e8bf1d55061aed0e10035a.png

竟然没有panic?具体解答放在下次推送中,有想法的朋友可以在公众号后台给我留言。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值