簡體   English   中英

如何從golang中的數組unsafe.Pointer創建數組或切片?

[英]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.LenSliceHeader.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的地址並獲取其長度之后,我們不再引用arrpuintptr不算作參考),並且積極的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM