我有這個函數(shù)將字符串轉(zhuǎn)換為字節(jié)切片而不復(fù)制func StringToByteUnsafe(s string) []byte { strh := (*reflect.StringHeader)(unsafe.Pointer(&s)) var sh reflect.SliceHeader sh.Data = strh.Data sh.Len = strh.Len sh.Cap = strh.Len return *(*[]byte)(unsafe.Pointer(&sh))}這工作正常,但是通過非常具體的設(shè)置會產(chǎn)生非常奇怪的行為:設(shè)置在這里:https://github.com/leviska/go-unsafe-gc/blob/main/pkg/pkg_test.go會發(fā)生什么情況:創(chuàng)建字節(jié)切片將其轉(zhuǎn)換為臨時(右值)字符串,并使用不安全再次將其轉(zhuǎn)換為字節(jié)切片然后,復(fù)制此切片(通過引用)然后,用戈魯丁內(nèi)的第二個切片做點什么打印前后的指針我在我的linux mint筆記本電腦上有這個輸出,帶有g(shù)o 1.16:go test ./pkg -v -count=1=== RUN TestSomething0xc000046720 123 0xc000046720 1230xc000076f20 123 0xc000046721 z--- PASS: TestSomething (0.84s)PASSok github.com/leviska/go-unsafe-gc/pkg 0.847s因此,第一個切片神奇地改變了它的地址,而第二個切片則不是如果我們刪除 goroutine(并且可能稍微玩弄一下代碼),我們可以獲取兩個指針來更改值(更改為同一個指針)。runtime.GC()如果我們將不安全的強制轉(zhuǎn)換更改為所有內(nèi)容,則無需更改地址即可工作。此外,如果我們從這里將其更改為不安全的強制值,https://stackoverflow.com/a/66218124/5516391 一切工作原理相同。[]byte()func StringToByteUnsafe(str string) []byte { // this works fine var buf = *(*[]byte)(unsafe.Pointer(&str)) (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Cap = len(str) return buf}我運行它并得到相同的結(jié)果。我運行它,沒有錯誤。GOGC=off-race如果將其作為具有 main 函數(shù)的主包運行,它似乎可以正常工作。此外,如果您刪除該函數(shù)。我的猜測是編譯器在這種情況下會優(yōu)化內(nèi)容。Convert所以,我對此有幾個問題:這到底是怎么回事?看起來像一個奇怪的UB為什么運行時會神奇地更改變量的地址?為什么在無并發(fā)情況下它可以更改兩個地址,而在并發(fā)情況下不能?這個不安全的強制轉(zhuǎn)換與堆棧溢出答案的強制轉(zhuǎn)換之間有什么區(qū)別?為什么它有效?或者這只是一個編譯器錯誤?
字節(jié)切片轉(zhuǎn)換與不安全的字符串更改其地址
慕碼人2483693
2022-09-12 20:54:19