面试官:Go函数返回局部变量的指针是否安全?

大家好,我是木川

一般来说,局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

但这在 Go 中是安全的,Go 编译器将会对每个局部变量进行逃逸分析。如果发现局部变量的作用域超出该函数,则不会将内存分配在栈上,而是分配在堆上,因为他们不在栈区,即使释放函数,其内容也不会受影响。

一、什么是内存逃逸

内存逃逸(Memory Escape)是一种编程语言中的术语,特别是在像Go这样的语言中,它描述了在编译器分析中的一种情况。内存逃逸指的是一个局部变量(通常是分配在栈上的)的引用在其定义的作用域之外继续存在的情况。这意味着编译器将该变量的内存分配从栈上移到堆上,以确保它在作用域结束后仍然可以访问。

内存逃逸通常是为了确保程序的安全性和正确性,尤其是在多线程环境下,以防止悬挂指针或内存泄漏等问题。以下是一些常见的导致内存逃逸的情况:

  1. 返回局部变量的指针:如果一个函数返回一个局部变量的指针,但在函数外部仍然被引用,编译器可能会将该变量的内存分配到堆上,以确保它的生命周期足够长。

  2. 引用传递:当一个函数将一个局部变量通过引用传递给另一个函数,并且在被调用函数中该引用可能会继续存在,编译器也可能会将该变量分配到堆上。

  3. Goroutine 中的闭包:在Go中,当一个闭包引用了一个局部变量,并且这个闭包可能在其作用域之外执行(例如,在一个新的Goroutine中),编译器可能会将该变量分配到堆上,以确保在Goroutine执行期间仍然可以访问。

二、示例

package main

import "fmt"

func add(x, y int) *int {
	res := 0
	res = x + y
	return &res
}

func main() {
	fmt.Println(add(1, 2))
}

这个例子中,函数 add局部变量 res 发生了逃逸。res作为返回值,在 main 函数中继续使用,因此 res 指向的内存不能够分配在栈上,随着函数结束而回收,只能分配在堆上。

编译时可以借助选项 -gcflags=-m,查看变量逃逸的情况

./main.go:6:2: res escapes to heap:
./main.go:6:2:   flow: ~r2 = &res:
./main.go:6:2:     from &res (address-of) at ./main.go:8:9
./main.go:6:2:     from return &res (return) at ./main.go:8:2
./main.go:6:2: moved to heap: res
./main.go:12:13: ... argument does not escape
0xc0000ae008

res escapes to heap 即表示 res 逃逸到堆上了。

最后给自己的原创 Go 面试小册打个广告,如果你从事 Go 相关开发,欢迎扫码购买,目前 10 元买断,加下面的微信发送支付截图额外赠送一份自己录制的 Go 面试题讲解视频

fadf84af3583a49d19466281817fa0fa.jpeg

cad34bb8817749a2f19924f4273ddf29.png

如果对你有帮助,帮我点一下在看或转发,欢迎关注我的公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值