slice行为

slice的结构

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

作为函数参数传递时:

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

1
2
3
4
5
6
7
8
9
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所保持的数组的指针是相同的。

1
2
3
4
5
6
7
8
9
10
11
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会导致数组重定位
1
2
3
4
5
6
7
8
9
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不会改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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不会重新排列,所以内容永远不会改变。

1
2
3
4
5
6
7
8
9
10
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指向同一个数组
}

当由函数参数传递时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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它保持不变。

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

1
2
3
4
5
6
7
8
9
10
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指向发生重定位等变化