Go: 透過內存看 slice 和 array

hi, 大家好,我是 hhf。

有這麼一個 Go 面試題:請說出 slice 和 array 的區別?

這簡直就是送分題。但是你如何回答才能讓面試官滿意呢?

我這裏就不貼這道題的答案了。但是我想內存方面簡單分析下 slice 和 array 的區別。

Array

func main() {
  as := [4]int{10, 5, 8, 7}
  
  fmt.Println("as[0]:", as[0])
  fmt.Println("as[1]:", as[1])
  fmt.Println("as[2]:", as[2])
  fmt.Println("as[3]:", as[3])
}

這段很簡單的代碼,聲明瞭一個 array。當然輸出結果也足夠簡單。

我們現在玩點花活,如何通過非正常的手段訪問數組裏面的元素呢?在做這個事情之前是需要先知道 array 的底層結構的。其實很簡單,Go array 就是一塊連續的內存空間。如下圖所示

寫一段簡單的代碼,我們不通過下標訪問的方式去獲取元素。通過移動指針的方式去獲取對應位置的指針。

func main() {
    as := [4]int{10, 5, 8, 7}

    p1 := *(*int)(unsafe.Pointer(&as))
    fmt.Println("as[0]:", p1)

    p2 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])))
    fmt.Println("as[1]:", p2)

    p3 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*2))
    fmt.Println("as[2]:", p3)

    p4 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*3))
    fmt.Println("as[3]:", p4)
}

結果:

as[0]10
as[1]5
as[2]8
as[3]7

下圖演示下獲取對應位置的值的過程:

Slice

同樣對於 slice 這段簡單的代碼:

func main() {
  as := []int{10, 5, 8, 7}
  
  fmt.Println("as[0]:", as[0])
  fmt.Println("as[1]:", as[1])
  fmt.Println("as[2]:", as[2])
  fmt.Println("as[3]:", as[3])
}

想要通過移動指針的方式獲取 slice 對應位置的值,仍然需要知道 slice 的底層結構。如圖:

func main() {
    as := []int{10, 5, 8, 7}

    p := *(*unsafe.Pointer)(unsafe.Pointer(&as))
    fmt.Println("as[0]:", *(*int)(unsafe.Pointer(uintptr(p))))
    fmt.Println("as[1]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0]))))
    fmt.Println("as[2]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*2)))
    fmt.Println("as[3]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*3)))

    var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(8)))
    fmt.Println("len", Len) 

    var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(16)))
    fmt.Println("cap", Cap) 
}

結果:

as[0]10
as[1]5
as[2]8
as[3]7
len 4
cap 4

用指針取 slice 的底層 Data 裏面的元素跟 array 稍微有點不同:

獲取 slice cap 和 len:

獲取 slice 的 Data:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/xCVFzFu5pJl5JkXufSf66w