為啥string和byte型別轉換需要一定的代價?
為啥內建函式copy會有一種特殊情況copy(dst byte, src string) int
?
string和byte,底層都是陣列,但為什麼byte比string靈活,拼接效能也更高(動態字串拼接效能對比)?
今天看了原始碼**了一下。
以下所有觀點都是個人愚見,有不同建議或補充的的歡迎emial我aboutme
什麼是字串?標準庫builtin
的解釋:
type string
string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing utf-8-encoded text. a string may be empty, but not nil. values of string type are immutable.
簡單的來說字串是一系列8位位元組的集合,通常但不一定代表utf-8編碼的文字。字串可以為空,但不能為nil。而且字串的值是不能改變的。
不同的語言字串有不同的實現,在go的原始碼中src/runtime/string.go
,string的定義如下:
type stringstruct struct
func gostringnocopy(str *byte) string
s := *(*string)(unsafe.pointer(&ss))
return s
}
哈哈,其實就是byte陣列,而且要注意string其實就是個struct。
首先在go裡面,byte是uint8的別名。而slice結構在go的原始碼中src/runtime/slice.go
定義:
type slice struct
array是陣列的指標,len表示長度,cap表示容量。除了cap,其他看起來和string的結構很像。
但其實他們差別真的很大。
字串的值是不能改變
在前面說到了字串的值是不能改變的,這句話其實不完整,應該說字串的值不能被更改,但可以被替換。 還是以string的結構體來解釋吧,所有的string在底層都是這樣的乙個結構體stringstruct
,string結構體的str指標指向的是乙個字元常量的位址, 這個位址裡面的內容是不可以被改變的,因為它是唯讀的,但是這個指標可以指向不同的位址,我們來對比一下string、byte型別重新賦值的區別:
s := "a1" // 分配儲存"a1"的記憶體空間,s結構體裡的str指標指向這快記憶體
s = "a2" // 重新給"a2"的分配記憶體空間,s結構體裡的str指標指向這快記憶體
其實byte和string的差別是更改變數的時候array的內容可以被更改。
s := byte // 分配儲存1陣列的記憶體空間,s結構體的array指標指向這個陣列。
s = byte // 將array的內容改為2
因為string的指標指向的內容是不可以更改的,所以每更改一次字串,就得重新分配一次記憶體,之前分配空間的還得由gc**,這是導致string操作低效的根本原因。
string和byte的相互轉換
將string轉為byte,語法byte(string)
原始碼如下:
func stringtoslicebyte(buf *tmpbuf, s string) byte
b = buf[:len(s)]
} else
copy(b, s)
return b
}func rawstring(size int) (s string, b byte)
return
}
可以看到b是新分配的,然後再將s複製給b,至於為啥copy函式可以直接把string複製給byte,那是因為go原始碼單獨實現了乙個slicestringcopy
函式來實現,具體可以看src/runtime/slice.go
。
將byte轉為string,語法string(byte)
原始碼如下:
func slicebytetostring(buf *tmpbuf, b byte) string
if raceenabled && l > 0
if msanenabled && l > 0
s, c := rawstringtmp(buf, l)
copy(c, b)
return s
}func rawstringtmp(buf *tmpbuf, l int) (s string, b byte) else
return
}
依然可以看到s是新分配的,然後再將b複製給s。
正因為string和byte相互轉換都會有新的記憶體分配,才導致其代價不小,但讀者千萬不要誤會,對於現在的機器來說這些代價其實不值一提。 但如果想要頻繁string和byte相互轉換(僅假設),又不會有新的記憶體分配,能有辦法嗎?答案是有的。
package string_slicebyte_test
import (
"log"
"reflect"
"testing"
"unsafe"
)func stringtoslicebyte(s string) byte
return *(*byte)(unsafe.pointer(&bh))
}func slicebytetostring(b byte) string
return *(*string)(unsafe.pointer(&sh))
}func teststringslicebyte(t *testing.t)
答案雖然有,但強烈推薦不要使用這種方法來轉換型別,因為如果通過stringtoslicebyte將string轉為byte的時候,共用的時同一塊記憶體,原先的string記憶體區域是唯讀的,一但更改將會導致整個程序down掉,而且這個錯誤是runtime沒法恢復的。
既然string就是一系列位元組,而byte也可以表達一系列位元組,那麼實際運用中應當如何取捨?
最後脫離場景談效能都是耍流氓,需要根據實際場景來抉擇。
GoLang string及其相關操作
str hello world n hello gopher n 輸出 hello world hello gopher str hello world n hello gopher n 輸出 hello world nhello gopher n 雙引號中的轉義字元被替換,而反引號中原生字串中的 ...
golang string迭代和結構體初始化
1.golang字串range時返回的型別為rune 在某次測試時發現,string字串,直接用下標訪問和用range訪問返回的型別不同,參看下面 func main 輸出結果為 type is uint8 is type int32 is type int32即直接下標訪問和用range訪問返回的...
golang string轉json的一些坑
大佬們都知道怎麼在string中給string型別賦值帶雙引號的字串,沒錯就是用反斜槓,如下 msg 但是golang還支援另外乙個符號,我初學時候以為是單引號,但其實不是,是esc鍵下邊那個,那麼賦值帶雙引號的字串就如下就行了 ret 先看一段 起作用是把字串轉換為結構體對應的json type ...