[英]Why Golang string.Builder String() can convert []byte to slice without a reflect.StringHeader?
[英]Convert byte to string using reflect.StringHeader still allocates new memory?
我有這個小代碼片段來測試 2 種將字節切片轉換為字符串對象的方法,一個函數用於分配一個新的字符串對象,另一個使用不安全的指針算法來構造 string*,它不分配新的內存:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func byteToString(b []byte) string {
return string(b)
}
func byteToStringNoAlloc(b []byte) string {
if len(b) == 0 {
return ""
}
sh := reflect.StringHeader{uintptr(unsafe.Pointer(&b[0])), len(b)}
return *(*string)(unsafe.Pointer(&sh))
}
func main() {
b := []byte("hello")
fmt.Printf("1st element of slice: %v\n", &b[0])
str := byteToString(b)
sh := (*reflect.StringHeader)(unsafe.Pointer(&str))
fmt.Printf("New alloc: %v\n", sh)
toStr := byteToStringNoAlloc(b)
shNoAlloc := (*reflect.StringHeader)(unsafe.Pointer(&toStr))
fmt.Printf("No alloc: %v\n", shNoAlloc) // why different from &b[0]
}
我在 go 1.13 下運行這個程序:
1st element of slice: 0xc000076068
New alloc: &{824634204304 5}
No alloc: &{824634204264 5}
我期望“切片的第一個元素”應該打印出與“No alloc”相同的地址,但實際上它們非常不同。 我哪里錯了?
首先,類型轉換調用的是一個內部函數,在這種情況下它是 slicebytetostring。 https://golang.org/src/runtime/string.go?h=slicebytetostring#L75
它確實將切片的內容復制到新分配的內存中。
在第二種情況下,您正在創建切片的新標頭並將其轉換為字符串標頭,即切片內容的新非官方持有者。 這樣做的問題是垃圾收集器不處理這種情況,並且生成的字符串標頭將被標記為與保存實際內容的實際切片沒有關系的單個結構,因此,您生成的字符串將僅有效而實際的內容持有者還活着(不計算這個字符串標題本身)。 因此,一旦垃圾收集器清除了實際內容,您的字符串仍將指向相同的地址但已釋放內存,如果您觸摸它,您將收到恐慌錯誤或未定義的行為。
順便說一句,沒有必要使用反射包及其頭文件,因為直接轉換已經創建了新的頭文件:
*(*string)(unsafe.Pointer(&byte_slice))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.