dpb56083 2017-03-01 11:13
浏览 407
已采纳

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

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]
} 
  • 写回答

3条回答 默认 最新

  • doudao1950 2017-03-01 11:23
    关注

    This is detailed in Spec: Slice expressions.

    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.

    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]
    

    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
    

    Try it on the Go Playground.

    Avoiding heap allocation

    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)
    }
    

    Running go run -gcflags '-m -l' play.go, the result is:

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

    The variable tmp is not moved to heap.

    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?

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能
  • ¥15 jmeter脚本回放有的是对的有的是错的
  • ¥15 r语言蛋白组学相关问题
  • ¥15 Python时间序列如何拟合疏系数模型