這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在go裡面,string和slice的互換是需要進行記憶體拷貝的,雖然在底層,它們都只是用 pointer + len來表示的一段記憶體。
通常,我們不會在意string和slice的轉換帶來的記憶體拷貝效能問題,但是總有些地方需要關注的,剛好在看vitess代碼的時候,發現了一種很hack的做法,string和slice的轉換隻需要拷貝底層的指標,而不是記憶體拷貝。當然這樣做的風險各位就要好好擔當了:
func String(b []byte) (s string) { pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) pstring.Data = pbytes.Data pstring.Len = pbytes.Len return}func Slice(s string) (b []byte) { pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) pbytes.Data = pstring.Data pbytes.Len = pstring.Len pbytes.Cap = pstring.Len return}
在我的測試例子中,slice轉string之後,如果slice的值有變化,string也會跟著改變,如下:
b := []byte("hello world")a := String(b)b[0] = 'a'println(a) //output aello world
但是string轉slice之後,就不能更改slice了,如下:
a := "hello world"b := Slice(a)b[0] = 'a' //這裡就等著崩潰吧//但是可以這樣,因為go又重新給b分配了記憶體b = append(b, "hello world"…)
上面為什麼會崩潰我猜想可能是string是immutable的,可能對應的記憶體位址也是不允許改動的。
另外,上面這個崩潰在defer裡面是recover不回來的,真的就崩潰了,原因可能就跟c的非法記憶體訪問一樣,os不跟你玩了。