2.认识go语言中的字符串

67 阅读1分钟

字符串本质

上一节,我们讲了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表示的是字符串底层字节数组的长度。

image.png

现在访问字符串长度已经不用这么麻烦了,可以直接使用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
中
国