| by suyi | No comments

slice行为

slice的结构

  • 指向数组的指针
  • 长度
  • 容量

作为函数参数传递时:

Golang函数参数传递基本上是按值传递的,slice也与之相同,因此传递变量的指针是不同的。

func main(){
    s:= [] int { 1,2,3 }
    fmt.Printf("%p\n", &s)   // 0x1040a0b0
    someFunc(s)
}

func someFunc(s [] int){
    fmt.Printf("%p\n", &s)   //与0x1040a0c0不同
}

为什么可以通过slice仍然可以访问相同的元素?原因是由值传递的slice所保持的数组的指针是相同的。

func main(){
    s:= [] int { 1,2,3 }
    fmt.Printf("%p\n", &s)   // 0x1040a0b0 
    fmt.Printf("%p\n", &s[0])   // 0x10410020
    someFunc(s)
}

func someFunc(s [] int){
    fmt.Printf("%p\n", &s)   // 0x1040a0c0 
    fmt.Printf("%p\n", &s[0])   //与0x10410020相同
}

append:

  • 对于容量不足时,append会导致数组重定位
func main(){ 
    s:= [] int { 1,2,3 } 
    fmt.Printf("%P\n", &s)   // 0X1040a0b0 
    fmt.Printf("%P\n", &s[0])   // 0x10410020
 
    s = append(s, 4)
    fmt.Printf("%p\n", &s)   // 0x1040a0b0 
    fmt.Printf("%p\n", &s[0])   // 0x10454000指向新数组 
}

当由函数参数传递时

如果append到函数内部,它将与调用者Slice看到的array不同。因此,函数内的array增加,但调用者的Slice不会改变。

func main() {
    s := []int{1, 2, 3}
    fmt.Printf("%p\n", &s[0])  // 0x10410020

    add(s)
    fmt.Println(s)  // [1 2 3]
    fmt.Printf("%p\n", &s[0])  // 0x10410020
}

func add(s []int) {
    fmt.Printf("before: %p\n", &s[0])  // 0x10410020

    s = append(s, 4)
    fmt.Println(s)  // [1 2 3 4]
    fmt.Printf("after: %p\n", &s[0])  // 指针变为0x10454020 
}
  • 当容量足够时

当有足够的容量时,Slice不会重新排列,所以内容永远不会改变。

func main() {
    s := make([]int, 3, 4)
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Printf("%p\n", &s)  // 0x1040a0b0
    fmt.Printf("%p\n", &s[0])  // 0x10410020

    s = append(s, 4)
    fmt.Printf("%p\n", &s)  // 0x1040a0b0
    fmt.Printf("%p\n", &s[0])  // 0x10410020指向同一个数组
}

当由函数参数传递时

func main() {
    s := make([]int, 3, 4)
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Printf("%p\n", &s[0])  // 0x10410020

    add(s)
    fmt.Println(s)  // [1 2 3]
    fmt.Printf("%p\n", &s[0])  // 0X10410020指代相同的地址
}

func add(s []int) {
    fmt.Printf("before: %p\n", &s[0])  // 0x10410020

    s = append(s, 4)
    fmt.Println(s)  // [1 2 3 4]
    fmt.Printf("after: %p\n", &s[0])  // 0x10410020指向同一个数组 

即使它们指向相同的地址,打印值也不同。这是因为调用者的Slice长度尚未更新。add()在函数中,它append4变为len ,但由于调用者的是一个单独的对象,因此3它保持不变。

在这种情况下,如果还想更新调用者

func main() {
    s := make([]int, 3, 4)
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Printf("%p\n", &s[0])

    add(s)
    s = s[:cap(s)]  // 追加
    fmt.Println(s)  // [1 2 3 4]
    fmt.Printf("%p\n", &s[0])
}

Slice行为:

  • slice有一个指向数组的指针
  • 通过参数传递的slice是通过值传递的,所以它是不同的。但指针内部指向的数组是相同的
  • append时array指向发生重定位等变化

发表评论