Foreword:
You should know: if you get the pointer as a value of uintptr
type, that does not prevent the original array to get garbage collected (an uintptr
value does not count as a reference). So be careful when using such value, there is no guarantee it will point to a valid value / memory area.
Quoting from package unsafe.Pointer
:
A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.
General advice: stay away from package unsafe
as much as possible. Stay inside Go's type safety.
Declare a variable of slice type, and use unsafe conversions to obtain its reflect.SliceHeader
descriptor.
Then you can modify its fields, using the pointer as the SliceHeader.Data
value, and the size as SliceHeader.Len
and SliceHeader.Cap
.
Once you're done with this, the slice variable will point to the same array as your initial pointer.
arr := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
size := len(arr)
p := uintptr(unsafe.Pointer(&arr))
var data []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
sh.Data = p
sh.Len = size
sh.Cap = size
fmt.Println(data)
runtime.KeepAlive(arr)
Output (try it on the Go Playground):
[0 1 2 3 4 5 6 7 8 9]
Note that I used runtime.KeepAlive()
. This is because after taking the address of arr
and getting its length, we don't refer to arr
anymore (p
being uintptr
does not count as a reference), and an aggressive GC might–rightfully–erase arr
before we get to the point to print data
(pointing to arr
). Placing a runtime.KeepAlive()
to the end of main()
will ensure that arr
will not be garbage collected
before this call. For details, see In Go, when will a variable become unreachable? You do not need to call runtime.KeepAlive()
in your code if the supplier of the pointer ensures it will not be garbage collected.
Alternatively you can create a reflect.SliceHeader
with a composite literal, and use unsafe conversions to obtain a slice from it, like this:
sh := &reflect.SliceHeader{
Data: p,
Len: size,
Cap: size,
}
data := *(*[]byte)(unsafe.Pointer(sh))
fmt.Println(data)
runtime.KeepAlive(arr)
Output will be the same. Try this one on the Go Playground.
This possibility / use case is documented at unsafe.Pointer
, with the caveats and warnings:
(6) Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.
As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case)
hdr.Len = n
In this usage hdr.Data is really an alternate way to refer to the underlying pointer in the slice header, not a uintptr variable itself.
In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types.
// INVALID: a directly-declared header will not hold Data as a reference.
var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
unsafe
. – Monk