Golang高性能编程之string篇
clean code that works.
为什么不能对string使用cap()函数
str := "hello"
fmt.Println(len(str)) // output 5
fmt.Println(cap(str)) // complier error, string内部结构是没有cap字段的
string内部结构是什么样的呢?
可以清楚看到,只有数据指针和len,没有cap字段
如何将string转换成[]byte
case 1: 直接强转,会导致内存拷贝;转换后的切片是可编辑的
func main() {
str := "hello"
strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
fmt.Printf("string\tdata:0x%x\tlen:%d\n", strHeader.Data, strHeader.Len)
b := []byte(str) // 会进行内存拷贝
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
fmt.Printf("slice\tdata:0x%x\tlen:%d\tcap:%d\n", sliceHeader.Data, sliceHeader.Len, sliceHeader.Cap)
b[0] = 'a' // 可以进行赋值
}
// output
string data:0x100f251e3 len:5
slice data:0x140002afec0 len:5 cap:32
// 从以上输出上看,地址是不同的
case 2: 不进行内存拷贝的转换
func main() {
str := "hello" // str的存储地址是文字常量区是只读的
strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
fmt.Printf("string\tdata:0x%x\tlen:%d\n", strHeader.Data, strHeader.Len)
b := *(*[]byte)(unsafe.Pointer(&str))
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
fmt.Printf("slice\tdata:0x%x\tlen:%d\tcap:%d\n", sliceHeader.Data, sliceHeader.Len, sliceHeader.Cap)
b[0] = 'a' // panic: unexpected fault address
}
// output:
string data:0x10521d1e3 len:5
slice data:0x10521d1e3 len:5 cap:4377373460
unexpected fault address 0x10521d1e3
// 注意看data地址,发现地址是一致的;之所以会panic,是因为str使用的是文字常量区地址(Text Segment),禁止写入
疑问:string的底层结构和slice的底层结构不同(slice的结构比string多了一个cap字段),为什么可以强转成功?
答:因为slice前两个字段和string前两个字段是类型是一致的,在内存布局上,是可以转换的;至于cap字段,由于没有string没有,所以,值是为定义;并且,len和cap是值传递,并不会导致string后续的内存被slice对象占用,所以是安全的
将[]byte转成string,官方源码中的事例
go1.16 file: /go/src/strings/builder.go:47
Data Segment
Text Segment也称为Code Segment,用于存储常量和代码