例子1
参考:https://stackoverflow.com/questions/20195296/golang-append-an-item-to-a-slice
package main
import "fmt"
func main(){
a:=make([]int,1,2)
a[0]=0
fmt.Println("before: ", a,len(a),cap(a))
T(a)
fmt.Println("after: ",a,len(a),cap(a)) // place 1
}
func T(a []int){
a=append(a,100)
fmt.Println("in T: ",a,len(a),cap(a))
}
// 输出:
before: [0] 1 2
in T: [0 100] 2 2
after: [0] 1 2
为什么执行了T函数打印的a
的内容没变
- 输出:
after: [0] 1 2
- 其实如果你改成
看到的输出如下:fmt.Println("after: ",a[:2],len(a),cap(a))
after: [0 100] 1 2
注意
- 输出由
[0]
变成了[0 100]
,内容变了, 但是len(a)
依然是1
为什么
- 在
fmt.Println("after: ",a,len(a),cap(a))
这里打印的时候,只打印了len(a)
个元素出来,也就是只打印了a[0] - 在
fmt.Println("after: ",a[:2],len(a),cap(a))
这里打印的时候,就全部打印了出来 - 因为前后的len(a)都为1,所以你不能执行a[1],会panic,因为越界
例子2
参考:https://stackoverflow.com/questions/28115599/when-does-golang-append-create-a-new-slice
package main
import "fmt"
func main() {
s := make([]int, 0, 5)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Println(a)
b := append(s, 6)
fmt.Println(a)
fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 6]
疑惑
- 为什么b改变了,a也改变了?
解惑
- 因为a和b都是在s上append的元素,s的cap是5,append一个元素不会导致扩容,s的底层数组的地址没变,s和a和b都指向了同一个底层数组,当b变了,也就改变了底层数组,所以a也变了
- 这里有一个不太好理解的解答,说是覆盖了。。。
如何证明s,a,b的底层数组地址都没变
package main
import "fmt"
func main() {
s := make([]int, 0, 5)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
fmt.Println("========")
b := append(s, 6)
fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
fmt.Printf("%v,%v,%v\n",b, cap(b), &b[0])
}
// 输出:
[1 2 3 4],5,0xc000010420
[1 2 3 4 5],5,0xc000010420
========
[1 2 3 4],5,0xc000010420
[1 2 3 4 6],5,0xc000010420
[1 2 3 4 6],5,0xc000010420
又问, 为什么s的打印值没变,不是s,a,b指向同一个底层数组么?
package main
import "fmt"
func main() {
s := make([]int, 0, 5)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Println(a)
fmt.Println(s)
fmt.Println("===")
b := append(s, 6)
fmt.Println(a)
fmt.Println(s)
fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4]
===
[1 2 3 4 6]
[1 2 3 4]
[1 2 3 4 6]
再解惑:
其实就是上面的例子1,如果你改成fmt.Println(s[:5])就会发现输出完全是一样的
再问:如果s的cap改成4呢?
package main
import "fmt"
func main() {
s := make([]int, 0, 4)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Println(a)
fmt.Println("===")
b := append(s, 6)
fmt.Println(a)
fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4 5]
[1 2 3 4 6]
再解惑
因为每次a和b在s上append,都导致了超过了s的cap,所以底层都指向了新的数组,a,b,s的底层数组都不一样了
package main
import "fmt"
func main() {
s := make([]int, 0, 4)
s = append(s, []int{1, 2, 3, 4}...)
a := append(s, 5)
fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
fmt.Println("========")
b := append(s, 6)
fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
fmt.Printf("%v,%v,%v\n",b, cap(b), &b[0])
}
// 输出
[1 2 3 4],4,0xc00000c3c0
[1 2 3 4 5],8,0xc00000e540
========
[1 2 3 4],4,0xc00000c3c0
[1 2 3 4 5],8,0xc00000e540
[1 2 3 4 6],8,0xc00000e580
一个关于扩容的不算误区的误区
slice的扩容总是两倍,其实实际的算法比这个复杂,并不一定是2倍
- 可以看看这个链接里的growslice函数,其中的一段:
newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.cap < 1024 { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } }
- 如果新申请的容量(cap)大于2倍的旧容量(old.cap),则最终容量是新申请的容量,否则
- 如果旧切片的长度小于1024,则最终容量是旧容量的2倍,否则
- 如果旧切片的长度大于等于1024,则最终就是新申请的容量,只是里面用一个for循环每次递增1/4
- 如果旧切片的长度小于1024,则最终容量是旧容量的2倍,否则
例子3
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4}
b := a[1:]
b = append(b, 5)
fmt.Println(a, b)
}
// [1 2 3 4] [2 3 4 5]
问:append了b的数据,a里的数据会变么?
答:不会变