golang中數組與切片的區別


初始化:數組需要指定大小,不指定也會根據初始化的自動推算出大小,不可改變

數組:

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 \n", 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 \n", 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 了這個時候就會新開辟一塊更大內存,然后把之前的數據復制過去。

  1. 而cap是以乘以2的速度擴展的。這里是不是真的就是乘以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 \n", 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 \n", 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 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM