在 Go 语言中,string 类型的底层结构是一个结构体,包含两个字段:一个指向字节数组的指针和该字节数组的长度。以下是其在 Go 源码中的大致定义:
type stringStruct struct {
str unsafe.Pointer
len int
}
str:这是一个指向字节数组的指针,该数组存储着字符串的实际内容。 len:它表示字符串的长度,也就是字节数组中字节的数量。 当你创建一个字符串时,Go 语言会在内存里分配一块连续的区域,用来存放字符串的字节序列。字符串一旦创建,其内容就不可变,而且这块内存区域的大小等同于字符串的长度。 例如: s := "hello" 在这个例子中,Go 语言会分配一块大小为 5 字节的内存区域,用来存放字符串 "hello" 的字节序列。stringStruct 中的 str 指针会指向这块内存区域的起始地址,len 字段的值则为 5。 当你修改字符串时,Go 语言会重新分配一块内存区域,并将原来的字符串内容复制到新区域中。 例如: s = s + " world" 在这个例子中,Go 语言会重新分配一块大小为 11 字节的内存区域,并将原来的字符串 "hello" 的内容复制到新区域中,再追加上 " world" 字符串的字节序列。stringStruct 中的 str 指针会指向这块内存区域的起始地址,len 字段的值则为 11。j
举个例子测试下:
package main
import (
"fmt"
"unsafe"
)
func main() {
s := "hello"
// 打印字符串的长度
fmt.Printf("Length of s: %d\n", len(s)) // Length of s: 5
// 打印字符串的底层结构
strStruct := (*struct {
str unsafe.Pointer
len int
})(unsafe.Pointer(&s))
fmt.Printf("Pointer to underlying data: %p\n", strStruct.str) // Pointer to underlying data: 0xa60caf
fmt.Printf("Length of underlying data: %d\n", strStruct.len) // Length of underlying data: 5
fmt.Printf(" %v,%v\n", &s, strStruct) // 0xc000026070,&{0xa60caf 5}
// 尝试修改字符串
s = s + " world"
// 打印新字符串的底层结构
newStrStruct := (*struct {
str unsafe.Pointer
len int
})(unsafe.Pointer(&s))
fmt.Printf("Pointer to new underlying data: %p\n", newStrStruct.str) // Pointer to new underlying data: 0xc00000a0e0
fmt.Printf("Length of new underlying data: %d\n", newStrStruct.len) // Length of new underlying data: 11
fmt.Printf(" %v,%v\n", &s, newStrStruct) // 0xc000026070,&{0xc00000a0e0 11}
}