chm*_*ike 6 arrays return return-value go slice
如果我返回作为函数或方法局部变量的数组切片会发生什么?Go 是否将数组数据复制到切片中make()?容量是否与切片大小或数组大小匹配?
func foo() []uint64 {
var tmp [100]uint64
end := 0
...
for ... {
...
tmp[end] = uint64(...)
end++
...
}
...
return tmp[:end]
}
Run Code Online (Sandbox Code Playgroud)
这在规范:切片表达式中有详细说明。
数组不会被复制,而是切片表达式的结果将是一个引用数组的切片。在 Go 中,从函数或方法返回局部变量或它们的地址是完全安全的,Go 编译器执行转义分析来确定一个值是否可以转义函数,如果是(或者更确切地说,它不能证明一个值可能不会转义),它会在堆上分配它,以便在函数返回后可用。
切片表达式:tmp[:end]意味着tmp[0:end](因为缺失的low索引默认为零)。由于您没有指定容量,它将默认为len(tmp) - 0which is len(tmp)which is 100。
您还可以使用完整的切片表达式来控制结果切片的容量,其形式如下:
a[low : high : max]
Run Code Online (Sandbox Code Playgroud)
这将结果切片的容量设置为max - low.
更多示例来阐明结果切片的长度和容量:
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
Run Code Online (Sandbox Code Playgroud)
在Go Playground上试一试。
如果你想在堆栈上分配它,你不能返回任何指向它(或它的一部分)的值。如果它将在堆栈上分配,则无法保证返回后它仍然可用。
一个可能的解决方案是将指向数组的指针作为参数传递给函数(并且您可以返回一个切片,指定函数填充的有用部分),例如:
func foo(tmp *[100]uint64) []uint64 {
// ...
return tmp[:end]
}
Run Code Online (Sandbox Code Playgroud)
如果调用函数创建数组(在堆栈上),这不会导致“重新分配”或“移动”到堆:
func main() {
var tmp [100]uint64
foo(&tmp)
}
Run Code Online (Sandbox Code Playgroud)
运行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
Run Code Online (Sandbox Code Playgroud)
变量tmp不会移动到堆中。
请注意,这[100]uint64被视为要在堆栈上分配的小数组。有关详细信息,请参阅Go 中关于堆栈分配的“小”对象是什么?