![](/img/trans.png)
[英]Go: how to convert unsafe.Pointer into pointer to array with unknown length?
[英]How to create an array or a slice from an array unsafe.Pointer in golang?
一個指向數組的指針,讓我們說:
p := uintptr(unsafe.Pointer(&array))
size := 5
我無法訪問變量array
,上面的代碼用於使其更清晰。
另外,我知道數組的size
,但是size
不是常量,它會根據運行時間而變化。
現在,我想用已知的指針,大小以及數據類型初始化切片或數組。
我想出了以下代碼:
data := make([]byte, size)
stepSize := unsafe.Sizeof(data[0])
for i := 0; i < size; i++ {
data[i] = *(*byte)(unsafe.Pointer(p))
p += stepSize
}
fmt.println(data)
但是這個方法會進行內存復制,這可能是效率低下的,無論如何都沒有復制?
PS我也試過以下兩種方法,
// method 1
data := *(*[]byte)(unsafe.Pointer(p))
// method 2
data := *(*[size]byte)(unsafe.Pointer(p))
但它會在運行時失敗,我現在知道它的原因。
前言:
您應該知道:如果您將指針作為uintptr
類型的值,則不會阻止原始數組進行垃圾回收( uintptr
值不計入引用)。 所以在使用這樣的值時要小心,不能保證它會指向有效的值/內存區域。
從包unsafe.Pointer
引用:
uintptr是整數,而不是引用。 將指針轉換為uintptr會創建一個沒有指針語義的整數值。 即使uintptr保存某個對象的地址,如果對象移動,垃圾收集器也不會更新該uintptr的值,uintptr也不會保留該對象的回收。
一般建議:盡可能遠離包裝unsafe
。 留在Go的類型安全。
聲明切片類型的變量,並使用不安全的轉換來獲取其reflect.SliceHeader
描述符。
然后,您可以使用指針作為SliceHeader.Data
值修改其字段,並將大小修改為SliceHeader.Len
和SliceHeader.Cap
。
完成此操作后,slice變量將指向與初始指針相同的數組。
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)
輸出(在Go Playground上試試):
[0 1 2 3 4 5 6 7 8 9]
請注意,我使用了runtime.KeepAlive()
。 這是因為在獲取arr
的地址並獲取其長度之后,我們不再引用arr
( p
是uintptr
不算作參考),並且積極的GC可能在我們到達點之前正確地擦除arr
打印data
(指向arr
)。 將runtime.KeepAlive()
放在main()
的末尾將確保在此調用之前不會對arr
進行垃圾回收。 有關詳細信息,請參閱In Go,變量何時無法訪問? 如果指針的供應商確保它不會被垃圾收集,則不需要在代碼中調用runtime.KeepAlive()
。
或者,您可以使用復合文字創建一個reflect.SliceHeader
,並使用不安全的轉換從中獲取切片,如下所示:
sh := &reflect.SliceHeader{
Data: p,
Len: size,
Cap: size,
}
data := *(*[]byte)(unsafe.Pointer(sh))
fmt.Println(data)
runtime.KeepAlive(arr)
輸出將是相同的。 在Go Playground嘗試這個。
這個可能性/用例記錄在unsafe.Pointer
,包含警告和警告:
(6)將一個reflect.SliceHeader或reflect.StringHeader數據字段轉換為Pointer或從指針轉換。
與前一種情況一樣,反射數據結構SliceHeader和StringHeader將字段Data聲明為uintptr,以防止調用者將結果更改為任意類型,而無需先導入“unsafe”。 但是,這意味着SliceHeader和StringHeader僅在解釋實際切片或字符串值的內容時有效。
var s string hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) hdr.Len = n
在這種用法中,hdr.Data實際上是一種引用切片頭中的底層指針的替代方法,而不是uintptr變量本身。
通常,reflect.SliceHeader和reflect.StringHeader只能用作* reflect.SliceHeader和* reflect.StringHeader指向實際的切片或字符串,而不是普通的結構。 程序不應聲明或分配這些結構類型的變量。
// 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.