slice的底层数组指针在某些操作下会保持一致,在其他操作下则可能会发生改变。

在Go语言中,slice是一个引用类型,它包含了指向底层数组的指针、slice的长度以及它的容量。

通过某些操作修改slice时(如切片、append等),底层数组的指针可能会保持不变,也可能会发生改变。

切片操作

对一个slice进行切片操作时(即获取slice的一个子集),结果slice与原slice将共享同一个底层数组。这意味着它们的指针会指向同一个地址,但是切片操作后的slice将拥有不同的长度和容量。

a := []int{1, 2, 3, 4}
b := a[1:3]
// a和b共享同一个底层数组,但是它们的长度和容量不同。

通过append增加元素

当通过append函数给slice增加元素,且未超过其容量时,新元素将被添加到底层数组中,这时slice的指针不会改变,只是长度增加了。

如果在添加新元素时超过了原slice的容量,Go运行时将分配一个新的、更大的底层数组,并复制原有元素到这个新数组中,然后添加新元素。

这种情况下,slice的指针会指向这个新的底层数组的地址,因此指针会发生改变。

直接修改slice元素

如果直接修改slice的元素,比如a[1] = 10,这不会影响到slice本身的指针、长度或容量,

因为没有进行重新分配或切片操作,只是修改了底层数组已经存在的元素。

如何检查slice在进行操作后指针是否发生了变化

仅仅是为了进行检查,使用unsafe包中的功能来获取或比较指针是不推荐的

package main

import (
  "fmt"
  "unsafe"
)

func main() {
  a := []int{1, 2, 3}
  fmt.Printf("Original slice pointer: %p
", &a[0])

  b := a[:2]
  fmt.Printf("After slicing pointer: %p
", &b[0])

  c := append(a, 4)
  fmt.Printf("After appending within capacity pointer: %p
", &a[0])
  fmt.Printf("New slice pointer: %p
", &c[0])

  d := append(a, 5, 6, 7) // Assuming this exceeds original capacity
  fmt.Printf("After appending beyond capacity pointer: %p
", &d[0])
}

func slicePointer(slice []int) unsafe.Pointer {
  return unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&slice)))
}

slice的底层数组指针在某些操作下会保持一致,在其他操作下则可能会发生改变。