简体   繁体   中英

How to get the underlying array of a slice in Go?

Let's say I have the following array of ints of length 3:

nums := [3]int{1,2,3}

Then I grab the slice of just the first two items

numSlice := nums[:2]

Invoking cap on numSlice and nums yields 3 in both cases, and len yields 2 and 3 respectively.

If I then append to that slice ( numSlice = append(numSlice, 10) ), the underlying array ( nums ) is now [1 2 10] . cap remains at 3 for both, as the underlying array of the slice is the same, and len for the slice is now 3.

However, if I append to that slice again ( numSlice = append(numSlice, 20) ), the underlying array of the slice must change - we see this is the case when cap now has doubled for numSlice and len is now 4.

Sorry for the overwrought explanation, just walking myself through it, but can someone explain to me what happens under the hood to the underlying array and how to get the reference to the new array?

First, if you haven't already, you should read this official blog post about slice internals . That should clear up everything.

Now to access the underlying array, you can use a combination of reflect and unsafe . In particular, reflect.SliceHeader contains a Data field which contains a pointer to the underlying array of a slice.

Example adapted from the documentation of the unsafe package:

s := []int{1, 2, 3, 4}
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
data := *(*[4]int)(unsafe.Pointer(hdr.Data))

Just as a headsup, answering your second question. Starting from Go 1.17 , you can do it like this

(*[2]int)(numSlice)

Playground

package main

import (
    "fmt"
)

func main() {
    nums := [3]int{1, 2, 3}
    numSlice := nums[:2]
    underArr1 := (*[2]int)(numSlice)
    fmt.Println(&underArr1[0]) //0xc000016018

    numSlice = append(numSlice, 10)
    underArr2 := (*[3]int)(numSlice)
    fmt.Println(&underArr2[0]) //0xc000016018 - same
    fmt.Println(nums) // [1 2 10]

    numSlice = append(numSlice, 20)
    underArr3 := (*[3]int)(numSlice)
    fmt.Println(&underArr3[0]) //0xc000078030 - different
    fmt.Println(cap(numSlice)) // 6
}

To be honest, you don't have to convert to array pointer to see the adresses, I just do it to answer your second question.

The behaviour is indeed the way you described it. When you append 10 , you still have one field left in your underlying array (because its length is 3, but your numSlice is 2), and even though it is currently occupied by a 3 , it can be used, and 3 is overwritten by 10 .

When you append a 20 , there are no fields left, so it creates a new underlying array (most likely 6 fields long, twice as big) and copies all the data from the original array there and moves the pointer to that array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM