Golang将数组传递给函数并修改它

5
在大多数语言中(如c++),传递数组会隐式地通过引用进行,因此在函数中对传递的数组进行的任何更改都会导致更改原始数组。我正在学习Golang,在Alan A.A. Donovan和Brian W. Kernighan的书《The Go Programming Language》中,它说它的行为与其他语言不同 - 它不会隐式地通过引用传递数组。
这使我有点困惑 - 那么不使用引用传递数组是否意味着不修改数组本身?让我举个例子:
func main() {
    tab := []int{1, 2, 3}
    fmt.Println(tab)
    // Results in [1 2 3]
    reverse(tab)
    fmt.Println(tab)
    // Results in [3 2 1]
}

func reverse(tab []int) {
    for i, j := 0, len(tab)-1; i < j; i, j = i+1, j-1 {
        tab[i], tab[j] = tab[j], tab[i]
    }
}

在上面的代码中,数组并没有被引用传递,但是 reverse 函数却修改了原始数组,所以它的工作方式有点像 C++ 程序。谁能解释一下其中的区别呢?
PS:如果这是一个愚蠢的问题,我很抱歉,我完全是 Golang 的新手,正在努力理解基础知识。

3
你是否查阅过任何文档,例如《Effective Go》中的Slices部分"Tour of Go"关于slices的章节Slice规范,甚至是关于slices的Go博客文章 - JimB
2个回答

13
解释相当简单:在上面的代码中,没有定义或显式使用任何一个数组。你的tab本地变量和tab参数是切片

在Go语言中,数组的长度是类型的一部分,例如[3]int(这个特性可以理解为[2]int[3]int是两种不同/独立的数组类型)。如果没有长度(无论是显式的,如[2]int,还是隐式的,如复合字面量[...]int{1, 2, 3}),则它不是数组类型,而是切片类型。

是的,正如您所读的那样,数组值意味着其所有元素,并且在传递时(或分配时),将复制其所有元素。然而,切片只是描述符号,头文件,在描述一个连续的数组段;当切片传递时(或分配时),仅会复制此header(包括指针),该header将指向相同的基础数组。因此,如果修改了切片副本的元素,则更改将反映在原始切片中,因为只有一个支持元素的数组。

如果想了解切片header的确切内容,可以查看reflect.SliceHeader类型:它是一个包含切片第一个元素指针、长度和容量的struct

请阅读以下博客文章,详细说明了这一点:

Go Slices: usage and internals

数组、切片(和字符串):'append' 函数的机制

了解更多相关问题,请参阅以下链接:

为什么 Go 语言需要数组?

Go 语言中的切片是按值传递的吗?


1
你定义的不是 `array`,而是一个通过引用传递的 `slice`,如 golang 文档所述。查看 this 链接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接