当前位置: 首页 > 面试题库 >

在Go函数中返回局部数组的切片安全吗?

孟征
2023-03-14
问题内容

如果返回作为函数或方法的局部变量的数组的切片会怎样?Go是否将数组数据复制到使用创建的切片中make()?容量将匹配切片大小还是阵列大小?

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

问题答案:

这在Spec:Slice
expressions中
有详细说明。

不会复制该数组,但slice表达式的结果将是引用该数组的slice。在Go中,从函数或方法返回局部变量或它们的地址是绝对安全的,Go编译器执行逸出分析以确定值是否可以逸出该函数,以及是否可以逸出(或是否不能证明某个值)可能无法转义),它会在堆上分配它,因此函数返回后将可用。

切片表达式:tmp[:end]意思是tmp[0:end](因为缺少的low索引默认为零)。既然你没有指定容量,则默认为len(tmp) - 0这是len(tmp)这是100

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

a[low : high : max]

将结果切片的容量设置为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

在Go Playground上尝试一下。

避免堆分配

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

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

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

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

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被认为是要在堆栈上分配的小数组。



 类似资料:
  • 问题内容: 这似乎是这将是整个interwebs一个相当普遍的事情,丰富的实例,但我似乎无法找到如何将一个转换的例子来。 我有一个从外部lib调用的函数,该函数返回一个数组 然后,我需要将该结果传递给其他函数以进行进一步处理。 不幸的是,如果我尝试致电 我懂了 在做 好多了。我该怎么做,尤其是在不创建数据副本的情况下(当我正在做的事情是传递数据时,似乎很愚蠢地复制该数据)。 问题答案: 这应该工作

  • 创建一个数组切片,从 arr 数组的最后一个元素开始向前提取n个元素。 使用 Array.slice() 来创建一个从第 n 个元素开始从末尾的数组。 const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length); takeRight([1, 2, 3], 2); // [ 2, 3 ] takeRight([1,

  • 创建一个数组切片,从arr数组的起始元素开始提取n个元素。 使用 Array.slice() 创建一个数组包含第一个元素开始,到 n 个元素结束的数组。 const take = (arr, n = 1) => arr.slice(0, n); take([1, 2, 3], 5); // [1, 2, 3] take([1, 2, 3], 0); // []

  • 问题内容: 我正在读一本叫做《 Go编程语言》的书,在有关指针的第二章中,写了以下内容 函数返回局部变量的地址是绝对安全的。例如,在下面的代码中,即使调用返回后,由对f的特定调用创建的局部变量v仍将存在,并且指针p仍将引用它: 我完全不明白这一点,应该在执行函数后销毁局部变量。是因为可能v是在堆上分配的。我知道在C语言中,如果您使用malloc分配空间,函数执行后就不会销毁它,因为它在堆上。 问题

  • Go语言内置支持多返回值,这个在Go语言中用的很多,比如一个函数同时返回结果和错误信息。 package main import "fmt" // 这个函数的返回值为两个int func vals() (int, int) { return 3, 7 } func main() { // 获取函数的两个返回值 a, b := vals() fmt.Println(a

  • 问题内容: 这是对问题的跟进:协议func返回Self。协议如下: 以下工作正常,但每个实现的功能完全相同,即 按照这个http://nshipster.com/swift-default-protocol- implementations/ 我尝试了一个全局功能 但是,在实现以下协议的类中调用它时 我得到指出的错误。当我键入自动完成显示为,我不知道那是什么意思。我也尝试过添加,但显然只允许这样做