golang stringと[]byteの対比
14512 ワード
golang stringと[]byteの対比
なぜstringと[]byteタイプ変換には一定の代価が必要なのか.なぜ内蔵関数copyには
copy(dst []byte, src string) int
の特殊な状況があるのですか?stringと[]byte、下位層は配列ですが、なぜ[]byteはstringより柔軟で、接合性能も高いのですか(動的文字列接合性能の比較)?今日はソースを見て探究しました.以下のすべての観点はすべて個人の愚見で、異なる提案あるいは補充の歓迎emial私aboutmeがありますstringとは?
文字列とは?標準ライブラリ
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 {
str unsafe.Pointer
len int
}
strは実際にはポインタであり、ある配列のヘッダアドレスを指し、別のフィールドはlen長であることがわかります.では、この配列は何ですか.このstringStructをインスタンス化するとき:
func gostringnocopy(str *byte) string {
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
s := *(*string)(unsafe.Pointer(&ss))
return s
}
ははは、実はbyte配列で、stringは実はstructであることに注意してください.
[]byteとは?
まずgoの中でbyteはuint 8の別名です.一方、slice構造はgoのソースコードの
src/runtime/slice.go
で定義されています.type slice struct {
array unsafe.Pointer
len int
cap int
}
arrayは配列のポインタであり,lenは長さを表し,capは容量を表す.cap以外はstringの構造に似ているように見えます.しかし、実は彼らの差は本当に大きい.
区別する
文字列の値は変更できません
前述した文字列の値は変更できませんが、この文は実は不完全で、文字列の値は変更できませんが、置換できます.やはりstringの構造体で説明しましょう.すべてのstringは下層ではこのような構造体
stringStruct{str: str_point, len: str_len}
です.string構造体のstrポインタは文字定数のアドレスを指しています.このアドレスの内容は読み取り専用なので変更できません.しかし、このポインタは異なるアドレスを指すことができます.string、[]byteタイプの再付与の違いを比較してみましょう.s := "A1" // "A1" ,s str
s = "A2" // "A2" ,s str
実は[]byteとstringの違いは変数を変更するときにarrayの内容を変更できることです.
s := []byte{1} // 1 ,s array 。
s = []byte{2} // array 2
stringのポインタが指す内容は変更できないため、文字列を変更するたびにメモリを再割り当てしなければなりません.以前にスペースを割り当てたものはgcで回収しなければなりません.これはstring操作が非効率になる根本的な原因です.
stringと[]byteの相互変換
stringを[]byteに変換し、構文
[]byte(string)
のソースコードは以下の通りです.func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
*buf = tmpBuf{}
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s)
return b
}
func rawstring(size int) (s string, b []byte) {
p := mallocgc(uintptr(size), nil, false)
stringStructOf(&s).str = p
stringStructOf(&s).len = size
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
return
}
bが新しく割り当てられたのを見て、sをbにコピーすることができます.なぜcopy関数がstringを[]byteに直接コピーできるのかというと、goソースコードが
slicestringcopy
関数を単独で実現したためです.具体的にはsrc/runtime/slice.go
を参照してください.[]byteをstringに変換します.構文
string([]byte)
のソースコードは次のとおりです.func slicebytetostring(buf *tmpBuf, b []byte) string {
l := len(b)
if l == 0 {
// Turns out to be a relatively common case.
// Consider that you want to parse out data between parens in "foo()bar",
// you find the indices and convert the subslice to string.
return ""
}
if raceenabled && l > 0 {
racereadrangepc(unsafe.Pointer(&b[0]),
uintptr(l),
getcallerpc(unsafe.Pointer(&buf)),
funcPC(slicebytetostring))
}
if msanenabled && l > 0 {
msanread(unsafe.Pointer(&b[0]), uintptr(l))
}
s, c := rawstringtmp(buf, l)
copy(c, b)
return s
}
func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
if buf != nil && l <= len(buf) {
b = buf[:l]
s = slicebytetostringtmp(b)
} else {
s, b = rawstring(l)
}
return
}
依然としてsが新しく割り当てられていることがわかり、bをsにコピーします.stringと[]byteの相互変換に新しいメモリ割り当てがあるからこそ、その代価は小さくないが、読者は誤解しないでください.今の機械にとってこれらの代価は実際には価値がありません.しかし、stringと[]byteを頻繁に変換したい場合(仮定のみ)、新しいメモリ割り当てはないので、方法はありますか?答えはある.
package string_slicebyte_test
import (
"log"
"reflect"
"testing"
"unsafe"
)
func stringtoslicebyte(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}
func slicebytetostring(b []byte) string {
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh := reflect.StringHeader{
Data: bh.Data,
Len: bh.Len,
}
return *(*string)(unsafe.Pointer(&sh))
}
func TestStringSliceByte(t *testing.T) {
s1 := "abc"
b1 := []byte("def")
copy(b1, s1)
log.Println(s1, b1)
s := "hello"
b2 := stringtoslicebyte(s)
log.Println(b2)
// b2[0] = byte(99) unexpected fault address
b3 := []byte("test")
s3 := slicebytetostring(b3)
log.Println(s3)
}
答えはありますが、stringtoslicebyteでstringを[]byteに変更した場合、共通の場合は同じメモリで、元のstringメモリ領域は読み取り専用ですが、変更するとプロセス全体がダウンし、このエラーはruntimeでは回復できません.
どのように取捨選択しますか?
stringが一連のバイトであり、[]byteも一連のバイトを表現できる以上、実際の運用ではどのように取捨選択すればいいのでしょうか.
最後にシーンから離れて性能を話すのはごろつきで、実際のシーンに応じて選択する必要があります.
転載先:https://www.cnblogs.com/zhangboyu/p/7623712.html