Golang中为何两个切片地址相同但值却不一样?
Go切片底层结构与
fmt.Printf("%p")行为解析 切片的底层结构
Go源码中切片的结构体定义为:
type slice struct { array unsafe.Pointer len int cap int }
核心误解纠正
你之前认为s := make([]string, 0)等价于s := &slice{},这是错误的。切片在Go中是值类型,make返回的是slice结构体的实例,而非指向该结构体的指针。
代码现象的具体解析
看你运行的这段代码:
s := make([]int, 0, 2) s2 := s s = append(s, 10) fmt.Printf("%v, %p\n", s, s) fmt.Printf("%v, %p\n", s2, s2)
出现s和s2打印地址相同但值不同的原因如下:
- 执行
s2 := s时,是对切片结构体做值拷贝,因此s和s2是两个完全独立的slice实例,但它们的array指针指向同一个底层数组。 append操作后,s的len字段变为1,而s2的len仍为0,两者的array指针并未改变,依然指向同一数组。fmt.Printf("%p", s)的特殊处理:对于切片类型,%p格式化符不会打印切片结构体自身的内存地址,而是打印其底层数组的起始地址(即s.array的值)。这就是为什么两个独立的切片实例,打印出的地址看起来一致。
你类比的这段代码完全准确,清晰体现了两者的关系:
array := [2]int{10} s := &slice{ array: unsafe.Pointer(&array), len: 1, cap: 2, } s2 := &slice{ array: unsafe.Pointer(&array), len: 0, cap: 2, }
s和s2是指向不同slice结构体的指针,但这两个结构体的array指针指向同一个底层数组。如果是普通指针类型,fmt.Printf("%p")会打印指针自身的地址,但切片作为值类型,%p被特殊处理为打印底层数组地址。
内容的提问来源于stack exchange,提问作者AeroXu
相关产品推荐
相关产品推荐

