golang中切片slice的引用问题

 package main
import "fmt"
func main() {

        var t=make([]int,0,10)
        var s=make([]int,0,10)
    
        fmt.Printf("addr:%p \t\tlen:%v content:%v\n",t,len(t),t);
        fmt.Printf("addr:%p \t\tlen:%v content:%v\n",s,len(s),s);
        
        t =append(s,1,2,3,4)
    
        fmt.Println(t)
        fmt.Println(s)
         
        fmt.Printf("addr:%p \t\tlen:%v content:%v\n",t,len(t),t);
        fmt.Printf("addr:%p \t\tlen:%v content:%v\n",s,len(s),s);
        
        }
    
 
    

result:

addr:0x1044c030 len:0 content:[]
addr:0x1044c060 len:0 content:[]
[1 2 3 4]
[]
addr:0x1044c060 len:4 content:[1 2 3 4]
addr:0x1044c060 len:0 content:[]

一开始两个切片地址不同,可以理解。
后面我进行append,容量足够的情况下,切片地址是不会变的,那为什么s和t的地址是一样的,它们的内容却不一样?

阅读 3.8k
2 个回答

@xxdd11223 首先你要知道slice是两级结构:底层数组 + 描述slice的struct,该struct包含了指向底层数组的指针,slice的长度和容量,也就是代码里的ts. 关于slice的具体结构可见golang官方的go-slices-usage-and-internals.

我在你的代码里加了注释和其他辅助代码,你可以对照起来看:

package main

import "fmt"
import "reflect"
import "unsafe"

func main() {

    var t = make([]int, 0, 10)
    var s = make([]int, 0, 10) // 到此为止, t,s 都分配了底层数组,cap为10,只是t,s指定了len为0,fmt.Print时才没显示出内容来

    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", t, len(t), t) // 这里的%p打印的其实是slice底层数组的首地址
    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", s, len(s), s)

    t = append(s, 1, 2, 3, 4) // s的底层数组变化,append返回新的描述struct,假设是tmp,tmp的len则为4,并指向了s的底层数组,再用tmp覆盖t,所以下面两行fmt.Printf打印的%p一样

    fmt.Println(t)
    fmt.Println(s)

    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", t, len(t), t) // t的len为4
    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", s, len(s), s) // s的len为0,因为t,s的len不一样,内容才不同

    fmt.Println("---- 辅助代码 -----")
    sliceHeaderT := (*reflect.SliceHeader)((unsafe.Pointer(&t)))
    sliceHeaderS := (*reflect.SliceHeader)((unsafe.Pointer(&s)))
    fmt.Printf("sliceHeaderT: %+v\n", sliceHeaderT) // Data字段的值其实和上面你打印的地址是同一个,自己可以去换算一下
    fmt.Printf("sliceHeaderS: %+v\n", sliceHeaderS)
    
    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", &t, len(t), t) // t的真实地址,明显和你上面打印的不同
    fmt.Printf("addr:%p \t\tlen:%v content:%v\n", &s, len(s), s)

    s = append(s, 5) // 修改s的底层数组,且len变为1

    fmt.Println(t) // 因为t,s 共享底层数组,所以t,s的首个元素都是5
    fmt.Println(s)
    fmt.Printf("sliceHeaderT: %+v\n", sliceHeaderT)
    fmt.Printf("sliceHeaderS: %+v\n", sliceHeaderS)

    sliceHeaderS.Len = 2
    fmt.Println(s)
}

t =append(s,1,2,3,4)

你这里用的 s 啊

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