在Go函数中返回局部数组的切片安全吗? [英] Is returning a slice of a local array in a Go function safe?

查看:242
本文介绍了在Go函数中返回局部数组的切片安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我返回一个数组的切片,该切片是函数或方法的局部变量,那会发生什么? Go会将数组数据复制到使用make()创建的切片中吗?容量会匹配切片大小还是数组大小?

What happens if I return a slice of an array that is a local variable of a function or method? Does Go copy the array data into a slice create with make()? Will the capacity match the slice size or the array size?

func foo() []uint64 {
    var tmp [100]uint64
    end := 0
    ...
    for ... {
        ...
        tmp[end] = uint64(...)
        end++
        ...
    }
    ... 
    return tmp[:end]
} 

推荐答案

这在规范中进行了详细说明:表达式.

不会复制该数组,但是slice表达式的结果将是引用该数组的slice.在Go中,从函数或方法返回局部变量或其地址是绝对安全的,Go编译器执行转义分析,以确定一个值是否可以转义该函数,如果可以(或者更确切地说,它不能证明某个值不能转义),它将在堆上分配它,以便在函数返回后可用

The array will not be copied, but instead the result of the slice expression will be a slice that refers to the array. In Go it is perfectly safe to return local variables or their addresses from functions or methods, the Go compiler performs an escape analysis to determine if a value may escape the function, and if so (or rather if it can't prove that a value may not escape), it allocates it on the heap so it will be available after the function returns.

切片表达式:tmp[:end]表示tmp[0:end](因为缺少的low索引默认为零).由于您未指定容量,因此默认设置为len(tmp) - 0,即len(tmp),即100.

The slice expression: tmp[:end] means tmp[0:end] (because a missing low index defaults to zero). Since you didn't specify the capacity, it will default to len(tmp) - 0 which is len(tmp) which is 100.

您还可以使用完整切片表达式来控制结果切片的容量,该表达式的形式为:

You can also control the result slice's capacity by using a full slice expression, which has the form:

a[low : high : max]

将结果切片的容量设置为max - low.

Which sets the resulting slice's capacity to max - low.

更多示例来阐明所得切片的长度和容量:

More examples to clarify the resulting slice's length and capacity:

var a [100]int

s := a[:]
fmt.Println(len(s), cap(s)) // 100 100
s = a[:50]
fmt.Println(len(s), cap(s)) // 50 100
s = a[10:50]
fmt.Println(len(s), cap(s)) // 40 90
s = a[10:]
fmt.Println(len(s), cap(s)) // 90 90

s = a[0:50:70]
fmt.Println(len(s), cap(s)) // 50 70
s = a[10:50:70]
fmt.Println(len(s), cap(s)) // 40 60
s = a[:50:70]
fmt.Println(len(s), cap(s)) // 50 70

游乐场上尝试.

如果要在堆栈上分配它,则不能返回任何指向它(或其一部分)的值.如果将其分配在堆栈上,将无法保证返回后仍然可用.

If you want to allocate it on the stack, you can't return any value that points to it (or parts of it). If it would be allocated on the stack, there would be no guarantee that after returning it remains available.

对此的一种可能的解决方案是将指向数组的指针作为该函数的参数传递(并且您可以返回指定该函数填充的有用部分的切片),例如:

A possible solution to this would be to pass a pointer to an array as an argument to the function (and you may return a slice designating the useful part that the function filled), e.g.:

func foo(tmp *[100]uint64) []uint64 {
    // ...
    return tmp[:end]
}

如果调用者函数在堆栈上创建了数组,则不会导致重新分配"或移动"到堆:

If the caller function creates the array (on the stack), this will not cause a "reallocation" or "moving" to the heap:

func main() {
    var tmp [100]uint64
    foo(&tmp)
}

运行go run -gcflags '-m -l' play.go,结果是:

./play.go:8: leaking param: tmp to result ~r1 level=0
./play.go:5: main &tmp does not escape

变量tmp不会移到堆中.

请注意,[100]uint64被视为要在堆栈上分配的一个小数组.有关详细信息,请参见小" Go中有关堆栈分配的对象?

Note that [100]uint64 is considered a small array to be allocated on the stack. For details see What is considered "small" object in Go regarding stack allocation?

这篇关于在Go函数中返回局部数组的切片安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆