```
func b() {
x := []int{}
x = append(x, 0)
x = append(x, 1)
x = append(x, 2)
y := append(x, 3)
z := append(x, 6)
fmt.Println(y)
fmt.Println(z)
}
```
调用函数b 输出结果
```
[0 1 2 6]
[0 1 2 6]
```
为什么 y和z输出是一样的?
2、
```
func b() {
x := []int{}
x = append(x, 0)
x = append(x, 1)
x = append(x, 2)
x = append(x, 3)
y := append(x, 6)
z := append(x, 7)
fmt.Println(y)
fmt.Println(z)
}
```
x多append一个数,y和z的输出又不一样了,
为什么?
golang版本1.13
这个属于`入门基础`问题,涉及`Slice切片`的底层实现,之前我已经回答过相关的问题,有兴趣看一下类似的问题
- [为什么在函数传参时,数组参数有无固定长度,会产生这样的差距,就是arr5里面的内容不会被改变,arr7却会改变,刚开始学go,求大佬赐教](https://studygolang.com/topics/12422)
- [Go语言设计与实现 - 切片](https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-array-and-slice/)
# 一、为什么 y 和 z 的结果一样
1. 创建y变量时,没有发生扩容,导致复用x的底层数组
2. 创建z变量时,同样没有发生扩容,依旧复用x的底层数组
3. 最终y和z的底层数组是一样的,即`3个变量都是用同一个底层数组`,且y和z都能访问到第4个元素,而x只能访问到第3个元素
4. 可以通过以下的例子进行验证
```go
func TestB(t *testing.T) {
x := []int{}
x = append(x, 0) // 0
x = append(x, 1) // 0,1
x = append(x, 2) // 0,1,2
y := append(x, 3) // y: 0,1,2,3 x: 0,1,2
z := append(x, 6) // z: 0,1,2,6 y: 0,1,2,6, x: 0,1,2
_x := (*reflect.SliceHeader)(unsafe.Pointer(&x))
_y := (*reflect.SliceHeader)(unsafe.Pointer(&y))
_z := (*reflect.SliceHeader)(unsafe.Pointer(&z))
t.Log(x, _x)
t.Log(y, _y)
t.Log(z, _z)
}
/*
=== RUN TestB
main_test.go:44: [0 1 2] &{824633781600 3 4}
main_test.go:45: [0 1 2 6] &{824633781600 4 4}
main_test.go:46: [0 1 2 6] &{824633781600 4 4}
--- PASS: TestB (0.00s)
PASS
*/
```
# 二、x多append了一个数,y和z的输出又不一样
1. x多追加了一个数,导致切片的大小`len`与`cap`一样
2. 接着再对x进行追加,会导致切片发生扩容,对底层数组发生值拷贝,生成新的底层数组
3. 所以x、y和z所用的底层数组都不一样,互不影响
```go
func TestB(t *testing.T) {
x := []int{}
x = append(x, 0) // 0
x = append(x, 1) // 0,1
x = append(x, 2) // 0,1,2
x = append(x, 3) // 0,1,2,3
y := append(x, 6) // y: 0,1,2,3,6 x: 0,1,2,3
z := append(x, 7) // z: 0,1,2,3,7 y: 0,1,2,3,6, x: 0,1,2,3
_x := (*reflect.SliceHeader)(unsafe.Pointer(&x))
_y := (*reflect.SliceHeader)(unsafe.Pointer(&y))
_z := (*reflect.SliceHeader)(unsafe.Pointer(&z))
t.Log(x, _x)
t.Log(y, _y)
t.Log(z, _z)
}
/*
=== RUN TestB
main_test.go:45: [0 1 2 3] &{824634321440 4 4}
main_test.go:46: [0 1 2 3 6] &{824634425536 5 8}
main_test.go:47: [0 1 2 3 7] &{824634425600 5 8}
--- PASS: TestB (0.00s)
PASS
*/
```
---
顺便扔个小博客
[Avtion](https://www.avtion.cn)
#1
更多评论
#### 这里有个slice扩容的基础知识
**规则1: 如果切片的容量`小于1024`个元素,那么扩容的时候slice的cap就直接`翻番,乘以2`;一旦元素个数`超过1024`个元素,cap扩容就要减缓,变成了`四分之一,乘以0.25`,即每次增加原来容量的四分之一。**
**规则2: 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。**
```golang
package main
import (
"fmt"
)
func main() {
x := []int{}
lastCapX := 0
for i := 0; i < 1026; i++ {
x = append(x, i)
fmt.Printf("len=%d cap=%d 扩容:%d \n", len(x), cap(x), cap(x)-lastCapX)
lastCapX = cap(x)
}
}
/**
len=1 cap=1 扩容:1
len=2 cap=2 扩容:1
len=3 cap=4 扩容:2
len=4 cap=4 扩容:0
len=5 cap=8 扩容:4
len=6 cap=8 扩容:0
len=7 cap=8 扩容:0
len=8 cap=8 扩容:0
len=9 cap=16 扩容:8
len=10 cap=16 扩容:0
len=11 cap=16 扩容:0
.
.
len=16 cap=16 扩容:0
len=17 cap=32 扩容:16
len=18 cap=32 扩容:0
len=19 cap=32 扩容:0
.
.
len=31 cap=32 扩容:0
len=32 cap=32 扩容:0
len=33 cap=64 扩容:32
len=34 cap=64 扩容:0
.
.
len=127 cap=128 扩容:0
len=128 cap=128 扩容:0
len=129 cap=256 扩容:128
len=130 cap=256 扩容:0
.
.
len=255 cap=256 扩容:0
len=256 cap=256 扩容:0
len=257 cap=512 扩容:256
len=258 cap=512 扩容:0
len=259 cap=512 扩容:0
.
.
len=511 cap=512 扩容:0
len=512 cap=512 扩容:0
len=513 cap=1024 扩容:512
len=514 cap=1024 扩容:0
len=515 cap=1024 扩容:0
.
.
len=1023 cap=1024 扩容:0
len=1024 cap=1024 扩容:0
len=1025 cap=1280 扩容:256
len=1026 cap=1280 扩容:0
*/
```
##### 上面的例子验证了规则1
##### 1楼的例子验证了规则2
相信这些应该能够解开你的疑惑
#3