3 回答

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

TA貢獻(xiàn)2021條經(jīng)驗(yàn) 獲得超8個(gè)贊
您可以使用 非常簡(jiǎn)單地將切片的內(nèi)容復(fù)制byte到數(shù)組中,如下所示: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)
}
產(chǎn)出
[1 2 3 4 5 6 7 8]
在操場(chǎng)上試試吧。
但請(qǐng)問您為什么要使用數(shù)組?通常最好只使用切片,除非你有充分的理由。

TA貢獻(xiàn)2065條經(jīng)驗(yàn) 獲得超14個(gè)贊
從 Go 1.17 開始,您可以直接使用類型轉(zhuǎn)換,從切片到數(shù)組指針:
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,轉(zhuǎn)換方法不會(huì)產(chǎn)生額外分配(不是您的分配,也不可能由 分配copy),因?yàn)樗皇巧梢粋€(gè)指向現(xiàn)有后備數(shù)組的指針。盡管取消引用數(shù)組指針會(huì)生成一個(gè)副本。
如果數(shù)組的長(zhǎng)度大于切片的長(zhǎng)度,則轉(zhuǎn)換會(huì)出現(xiàn)混亂
a := make([]byte, 5)
b := (*[10]byte)(a) // panics
指針指向切片的底層數(shù)組,因此通過(guò)索引可以看到相同的值:
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 轉(zhuǎn)換byte為uint8,包括從它們派生的類型文字,因?yàn)?byte 是uint8 的別名(等同于)。
- 3 回答
- 0 關(guān)注
- 484 瀏覽
添加回答
舉報(bào)