[][]string是引用类型吗

package main

import (
    "fmt"
)

func main() {
    var ms [][]string
    test(ms)
    fmt.Println(ms)
}

func test(ms [][]string) {
    m := []string{"hello", "world"}
    ms = append(ms, m)
}

为什么最终ms没有增长

阅读 4.3k
4 个回答

确切的说, Go中的切片不是引用(不是C++中的那种引用)。

func main() {
    ms := [][]string{}
    fmt.Printf("&ms: %p, ms: %p, val: %v\n", &ms, ms, ms)
    test(ms)
    fmt.Printf("&ms: %p, ms: %p, val: %v\n", &ms, ms, ms)
}

func test(ms [][]string) {
    m := []string{"hello", "world"}
    fmt.Printf("&ms: %p, ms: %p, val: %v\n", &ms, ms, ms)
    ms = append(ms, m)
    fmt.Printf("&ms: %p, ms: %p, &ms[0]: %p\n", &ms, ms, &ms[0])
    fmt.Printf("&ms: %p, ms: %p, val: %v\n", &ms, ms, ms)
}

这段代码在我的机器上的输出:

&ms: 0xc420010240, ms: 0x5168b0, val: []
&ms: 0xc4200102c0, ms: 0x5168b0, val: []
&ms: 0xc4200102c0, ms: 0xc420010340, &ms[0]: 0xc420010340
&ms: 0xc4200102c0, ms: 0xc420010340, val: [[hello world]]
&ms: 0xc420010240, ms: 0x5168b0, val: []

可以明显看出:

  1. test函数中ms的地址和main函数中ms的地址一样

  2. test函数中&ms的地址和main函数中&ms的地址不一样

  3. append之前和append之后, test函数中ms的地址不一样

  4. append之前和append之后, test函数中&ms的地址一样

  5. append之后, main函数中ms&ms的地址都没有变化

再结合第三条输出:
ms输出的地址&ms[0]输出的地址一样, 但是与&ms输出的地址不一样。

我们知道&是取地址符,所以可以得出结论:

  1. test函数中的ms和main函数中的ms不是一个变量,但是它们指向同一个地址。

  2. ms变量里存储了它这个二维字符串数组的第一个元素的地址(实际上还存储了其他的信息)。

append函数的官方文档同时也说了,如果slice中没有足够的容量,则会重新分配内存并返回。
所以在ms = append(ms, m)返回了一个新的地址,并把这个地址赋值给了ms,也就导致ms变量里存储的第一个元素的地址发生了改变。

以上的说法并不是完全精确的,因为只关注了地址,而实际上对于一个slice变量来说,它除了存储地址外,还存储了len、cap这两个信息, 可以使用unsafe.Sizeof(ms)获取ms变量占用的内存大小来证明。

ms是切片,当append空间不足时,系统会重新申请一段内存给ms,main里面的ms还是之前的ms

如果需要改变传入的参数的值,需要传入指针。

func main() {
    var ms [][]string
    test(&ms)
    fmt.Println(ms)
}

func test(ms *[][]string) {
    m := []string{"hello", "world"}
    *ms = append(*ms, m)
}

C系统语言,传值调用值拷贝,传指针调用指针地址拷贝,指针与值的区别

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题