Golang在func中分配的变量通过参数传递出函数域之后变nil的问题

简介: Golang在func中分配的变量通过参数传递出函数域之后变nil的问题,其实就是一个变量逃逸的问题。

Golang的func中分配的变量通过参数传递出函数域之后变nil的问题

最近在Go上面碰到了一个传出参数的问题:

func testOutString(out *string) {
    if out == nil {
        // str := "hellow"
        // out = &str
        out = new(string)
    }
    *out = "hello"
}

func main() {
    var str *string
    testOutString(str)
    fmt.Println(str)
}

不管是用的堆分配out = new(string)
还是栈上内存分配str := "hellow"
以上代码打印出来的必然是nil

然后下面的代码则不会:

func testOutString(out *string) {
    *out = "hello"
}

func main() {
    var str = new(string)
    testOutString(str)
    fmt.Println(str)
}

没错,在调用func之前,传入的参数必须先初始化。

其实,仔细想一想这个问题,要理解也是容易的:

因为Go语言是带GC的语言,内存是在func域中分配的,所以在func域中分配的内存,在func结束之后,会被GC所回收。

换言之,也就是在func中创建出来的变量具有一个引用计数,创建之后引用计数+1。出域的时候,引用计数-1,引用计数就变成了0,GC回收内存也就是很自然的事情了。

而返回值的话则不会出现这样的问题,很显然在用返回值传递的时候,引用计数并没有归零,如以下代码:

func testOutString() *string {
    str := "hellow"
    return &str
}

func main() {
    var str *string
    str = testOutString()
    fmt.Println(*str)
}

在这里得提一提golang里面的一个概念:变量逃逸

关于变量逃逸,简单来说,就是原本应该分配在函数栈上的局部变量,因为其生命周期超出了所在函数的生命周期,所以编译器将其由栈分配改为堆分配,也就是我们通常所讲的“变量逃逸到了堆上”。

上面这个例子实际上就是一种变量逃逸的例子。
而我上面产生问题的代码则并没有产生变量逃逸。
照理来说,按照我曾经C和C++的经验,在函数域里边new出来的变量应该是可以传递出来的,直觉上是没问题的,然而就是这个直觉并不是正确的。

这样可以产生变量逃逸:

func testOutString(out **string) {
    var n = "hellow"
    *out = &n
}

func main() {
    var str *string
    testOutString(&str)
    fmt.Println(*str)
}

还有全局变量也能产生变量逃逸:

var str *string

func testOutString() {
    var n = new(string)
    *n = "hello"
    str = n
}

func main() {
    testOutString()
    fmt.Println(*str)
}
目录
相关文章
初识go变量,使用var和:=来声明变量,声明变量的三种方式
这篇文章介绍了Go语言中使用`var`和`:=`声明变量的三种不同方式,包括声明单个或多个变量、通过值确定数据类型以及在函数体内使用`:=`声明局部变量。
初识go变量,使用var和:=来声明变量,声明变量的三种方式
|
11天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
28 9
|
2月前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
64 3
Golang语言之函数(func)进阶篇
|
2月前
|
Go
Golang语言之函数(func)基础篇
这篇文章深入讲解了Golang语言中函数的定义和使用,包括函数的引入原因、使用细节、定义语法,并通过多个案例展示了如何定义不返回任何参数、返回一个或多个参数、返回值命名、可变参数的函数,同时探讨了函数默认值传递、指针传递、函数作为变量和参数、自定义数据类型以及返回值为切片类型的函数。
62 2
Golang语言之函数(func)基础篇
|
2月前
|
Unix Go
Golang语言标准库time之日期和时间相关函数
这篇文章是关于Go语言日期和时间处理的文章,介绍了如何使用Go标准库中的time包来处理日期和时间。
48 3
|
2月前
|
Go
Golang语言基础之标识符和变量定义
这篇文章详细介绍了Go语言中标识符和变量的定义、命名规则、关键字、变量类型、声明方式、作用域等基础知识。
28 3
|
6月前
|
Go
golang中置new()函数和make()函数的区别
golang中置new()函数和make()函数的区别
|
3月前
|
Go
Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围
Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围
|
3月前
|
安全 Go
|
3月前
|
自然语言处理 Go 开发者
深入理解Go语言中的变量作用域
【8月更文挑战第31天】
27 0