字符串String
字符串的底层表示
type StringHeader struct {
Data uintptr
Len int
}
- Data 指向底层的字符数组
- Len 表示字符串的长度
Go语言中,所有的文件都采用UTF-8编码,字符常量也是用的是UTF-8字符编码集。UTF-8 是可变长的编码方式,比如ASCII码 就用一个字节表示,中文就是3个字节表示。我记得ASCII的UTF-8编码是最高位为0。
使用range 轮询字符串,所出来的是utf-8编码的符文rune。比如"Hello你好",它轮询的时候,就是挨个输出H e l l o 你 好。
字符串与字节数组的转换
a := "hello world"
b := []byte(a)
c := string(b)
当a通过[]byte转换为b后,可能会觉得Go会把StringHeader.Data指向的字符数组地址直接给到b,但不是,因为string是不可修改的,所以StringHeader.Data指向的字符数组是不能直接拿出来修改的。所以这里就牵涉到了复制,当字符串长度大于32字节,还需要申请堆内存,所以牵涉到大字符串转换的时候,要考虑一下对内存的影响,比如日志的函数内部就不可能直接将字符串转换为[]byte数组。所以要考虑如何用指针就能将其弄出来呢?需要熟悉unsafe.Pointer 和 uintptr转换,可以看这篇文章Go指针的使用限制以及unsafe.Pointer的突破之路
这里,我们先把Slice的底层结构给出来
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
type StringHeader struct {
Data uintptr
Len int
}
// 在了解Slice和String的底层结构表示后,也可以使用反射类型来构造
func string2bytes(s string) []byte {
stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: stringHeader.Data,
Len: stringHeader.Len,
Cap: stringHeader.Len,