字符串本质
上一节,我们讲了go语言中变量的大小如何获取,我们先来看一下字符串的大小
str1 := "hello"
str2 := "hello world"
fmt.Printf("str1 size: %d\n", unsafe.Sizeof(str1))
fmt.Printf("str2 size: %d\n", unsafe.Sizeof(str2))
//output
str1 size: 16
str2 size: 16
很奇怪,”hello world”明明比“hello”长啊,为啥输出的size都是字节呢?
这就需要看下string的底层实现:
runtime/string.go
type stringStruct struct {
str unsafe.Pointer
len int
}
可以看到,是一个结构体,包含两个字段,一个是万能指针,一个是int,;而这两个字段我们在上一节已经知道,指针跟int都是跟随系统字长的,在64位系统中都是8字节大小。
所以,字符串的本质就是个结构体。
unsafe.Pointer万能指针,可以指向任何类型,这里可以看到这个指针指向的一定是底层存放数据的Byte数组;len ,这个是底层数组的长度还是字符大小?
我们可以借助反射包中的StringHeader来打印看看
str3 := "中国"
sh := (*reflect.StringHeader)(unsafe.Pointer(&str3))
fmt.Printf("str3 len is %d\n", sh.Len)
//output
str3 len is 6
可以看到,字符串底层实现中的len表示的是字符串底层字节数组的长度。
现在访问字符串长度已经不用这么麻烦了,可以直接使用len()方法。
字符串访问
1)通过下标来访问
得到的是字节数,而不是字符数。最直观的就是访问中文时无法得到想要的输出。
str3 := "中国"
for i := 0; i < len(str3); i++ {
fmt.Printf("%c\n", str3[i])
}
//output
ä
¸
å
½
2)for-range
被解码成rune类型的字符
str3 := "中国"
for _, v := range str3 {
fmt.Printf("%c\n", v)
}
//output
中
国