初始化:数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变
数组:
a := [...]int{1,2,3} a := [3]int{1,2,3}
切片:
a:= []int{1,2,3} a := make([]int, 5) a := make([]int, 5, 10)
slice的数据结构:
go源码slice的数据结构定义:
type slice struct { array unsafe.Pointer len int cap int }
一个指向真实 array 地址的指针 ptr ,slice 的长度 len 和容量 cap
函数传递:数组需要明确指定大小,切片不需要。数组是值传递,切片是地址传递
numbers2 := [...]int{1, 2, 3, 4, 5, 6} maxIndex2 := len(numbers2) - 1 for i, e := range numbers2 { if i == maxIndex2 { numbers2[0] += e } else { numbers2[i+1] += e } } fmt.Println(numbers2) numbers3 := []int{1, 2, 3, 4, 5, 6} maxIndex3 := len(numbers3) - 1 for i, e := range numbers3 { if i == maxIndex3 { numbers3[0] += e } else { numbers3[i+1] += e } } fmt.Println(numbers3)
输出:
[7 3 5 7 9 11] [22 3 6 10 15 21]
观察slice append的时候内存地址会不会改变:
通过一个例子:
package main
import (
"fmt"
"unsafe"
)
func main() {
//说先定义一个切片,只限定长度为1
s := make([]int, 1)
//打印出slice的长度,容量以及内存地址
fmt.Printf("len :%d cap:%d array ptr :%v ", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("len :%d cap:%d array ptr :%v ", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
}
//打印出slice
fmt.Println("array:", s)
}
输出:
len :1 cap:1 array ptr :0xc042062080 len :2 cap:2 array ptr :0xc0420620c0 len :3 cap:4 array ptr :0xc0420600e0 len :4 cap:4 array ptr :0xc0420600e0 len :5 cap:8 array ptr :0xc0420880c0 len :6 cap:8 array ptr :0xc0420880c0 array: [0 0 1 2 3 4]
可以看出来在append的过程中,内存地址有些是一样的,有些是不一样的,容量也是如此
看出来了吧,每次cap改变的时候指向array内存的指针都在变化。当在使用 append 的时候,如果 cap==len 了这个时候就会新开辟一块更大内存,然后把之前的数据复制过去。
- 而cap是以乘以2的速度扩展的。这里是不是真的就是乘以2的速度呢??
- 最后一句输出slice元素的时候为什么会多了一个0呢??
对于问题2:是因为make初始化时,都会初始化成对应数据类型的原值
我们再来测试一下问题1:
package main import ( "fmt" "unsafe" ) func main() { //说先定义一个切片,只限定长度为1 s := make([]int, 1) //打印出slice的长度,容量以及内存地址 fmt.Printf("len :%d cap:%d array ptr :%v ", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s))) for i := 1; i < 1024*2; i++ { s = append(s, i) fmt.Printf("len :%d cap:%d array ptr :%v ", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s))) } //打印出slice fmt.Println("array:", s) }
输出太多了,就截一部分:
如果按照上面得出的结论,那cap为1024的下一个应该是2048,但是却是1280
实际go在append的时候放大cap是有规律的。在 cap 小于1024的情况下是每次扩大到 2 * cap ,当大于1024之后就每次扩大到 1.25 * cap 。