关于golang:见微知著-带你透过内存看-Slice-和-Array的异同

6次阅读

共计 2177 个字符,预计需要花费 6 分钟才能阅读完成。

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 变量 as 取地址后,拿到的是 SiceHeader 的地址,对这个指针进行挪动,失去是 slice 的 Data, Len, Cap。
  • 所以当拿到 Data 的值时,咱们拿到的是 Data 所指向的 array 的首地址的值。
  • 因为这个值是个指针,须要对这个值 *Data,取到 array 真正的首地址的指针值
  • 而后对这个值 &(*Data),获取到真正的首地址,而后对这个值进行指针的挪动,能力获取到 slice 的数组里的值

获取 slice cap 和 len:

获取 slice 的 Data:

欢送关注公众号。更多学习学习材料分享,关注公众号回复指令:

  • 回复 0,获取《Go 面经》
  • 回复 1,获取《Go 源码流程图》

正文完
 0