3 回答

TA貢獻1874條經驗 獲得超12個贊
背景
您的“簡單方法”的問題在于 slice (任何類型)是struct由一個指針和兩個整數組成的類型值;指針包含底層(后備)數據數組的地址,整數包含為該切片返回的內容len()和內置函數。cap()
換句話說,切片是數組的一種視圖。
然后,在 Go 中,沒有類型轉換的概念;只有類型轉換,并且這些轉換可能只發生在具有相同底層表示的類型之間。
由于切片和數組可能不具有相同的底層表示(數組實際上是一個連續的內存塊,其大小足以包含所有數組元素),您所謂的類型轉換可能不合法。
可能的解決方案
有兩種可能的解決方案。
最簡單的方法是將數據從切片的支持數組復制到新分配的數組中:
var (
src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
dst [8]uint8
)
copy(dst[:], src[:8])
請注意,切片和數組類型之間存在固有差異:數組類型對其元素的類型及其長度(即長度是類型的一部分)進行編碼,而切片類型僅對其元素的類型進行編碼元素(并且在運行時可以是任意長度)。
這意味著您可能需要在進行此類復制之前進行檢查,以確保源切片恰好具有 8 個元素,即len(src) == len(dst).
這個不變量可能會被其他一些代碼強制執行,但我想我會提前警告你:如果src少于 8 個元素,src[:8]表達式將在運行時出現 panic,如果包含更多,那么就會有是否復制的問題只是其中的前 8 個正是所需要的。
第二種方法(誠然比較混亂)是重新使用切片的底層數組:
import "unsafe"
var (
src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
dstPtr *[8]uint8
)
if len(src) != len(*dstPtr) {
panic("boom")
}
dstPtr = (*[8]uint8)(unsafe.Pointer(&src[0]))
在這里,我們剛剛獲取了切片底層數組中包含的第一個元素的地址,并執行了“臟”兩階段類型轉換,使獲得的指針成為類型——即“數組的地址*[8]uint8” 8uint8秒”。
注意兩個注意事項:
結果指針現在指向與原始切片相同的內存塊。這意味著現在可以通過切片和我們獲得的指針來改變該內存。
一旦您決定將數組的數據分配給類型的變量[8]uint8(并將其作為參數傳遞給該類型的函數參數),您將取消引用該指針(如 with *dstPtr),并且此時 數組的數據將被復制。
我特別提到這一點,因為人們經常訴諸像這樣的 hack 來精確地將支持陣列從切片中拉出,以試圖不復制內存。
長話短說
復制數據(在假設驗證 len(src) == len(dst)不變保持之后)。
復制 8 個字節很快(在典型的 64 位 CPU 上,這將是一條MOV指令,最多兩條),代碼也很簡單。
只有當你真的需要優化一些關鍵的熱路徑時,才使用第二種解決方案。在這種情況下,對解決方案進行廣泛注釋并注意不要意外取消引用您的指針。
1 此規則有明顯的例外情況:
A[]byte可以類型轉換為string,反之亦然。
Astring可以類型轉換為[]rune,反之亦然。
Anint是類型可轉換的string(但由于 Go 1.15go vet 給出了關于它的警告,并且將來可能會禁止此功能)。

TA貢獻2021條經驗 獲得超8個贊
您可以使用 非常簡單地將切片的內容復制byte到數組中,如下所示:uint8copy
package main
import (
"fmt"
)
func main() {
slice := []byte{1, 2, 3, 4, 5, 6, 7, 8}
array := [8]uint8{}
copy(array[:], slice)
fmt.Println(array)
}
產出
[1 2 3 4 5 6 7 8]
在操場上試試吧。
但請問您為什么要使用數組?通常最好只使用切片,除非你有充分的理由。

TA貢獻2065條經驗 獲得超14個贊
從 Go 1.17 開始,您可以直接使用類型轉換,從切片到數組指針:
a := make([]byte, 8)
b := (*[8]uint8)(a) // b is pointer to [8]uint8
您可以取消引用以獲得非指針[8]uint8類型。
a := make([]byte, 8)
b := *(*[8]uint8)(a) // b is [8]uint8
筆記:
與 不同copy,轉換方法不會產生額外分配(不是您的分配,也不可能由 分配copy),因為它只是生成一個指向現有后備數組的指針。盡管取消引用數組指針會生成一個副本。
如果數組的長度大于切片的長度,則轉換會出現混亂
a := make([]byte, 5)
b := (*[10]byte)(a) // panics
指針指向切片的底層數組,因此通過索引可以看到相同的值:
a := []byte{0xa1, 0xa2}
b := (*[2]uint8)(a)
fmt.Printf("%x\n", a[0]) // a1
b[0] = 0xff
fmt.Printf("%x\n", a[0]) // ff
您可以將 from 轉換byte為uint8,包括從它們派生的類型文字,因為 byte 是uint8 的別名(等同于)。
- 3 回答
- 0 關注
- 489 瀏覽
添加回答
舉報