【Go笔记】字符串

字符串的本质

  • 一般的字符串分为编译时指定长度和动态长度两种,Go使用的是前者
  • 字符串的终止有两种方式,一种是C中的隐式申明,以\0结尾;一种是Go中的显示声明
  • Go中的字符串本质上是字符数组,可以用下标访问,但是不能被修改
  • 空间方面,一般字符占1字节,特殊字符如中文,日文等占3个字符

符文类型 (rune)

  • 设计者认为,因为有些字符非常相似,用于表示字符串可能会有歧义,而其不同的本质是编码之后的整数,因此使用符文 (rune) 类型来区分字符串中的字符
  • rune其实是int32的别称,代表字符编码之后的整数
  • 用range轮询字符串的时候,轮询的不是单字节,而是具体的rune,因此index不会是一个一个一个一个一个一个一个一个
  • printf中%#U可以打印符文数十六进制的Unicode编码方式和字符形状

字符串底层原理

  • 语法解析阶段,字符串被标记成StringLit的Token被传到下一个阶段
  • 语法分析阶段,采取递归下降的方式读取UTF-8字符,单撇号或双引号是字符串的标识
  • 抽象语法树阶段,如果是两个字符串相加的话,中间的加号Op会被解析为OADDSTR
  • 两个字符串常量相加会发生在语法分析阶段,调用noder.sum函数,将各字符串常量放在字符串数组中,然后使用strings.Join完成拼接
  • 字符串变量的拼接实在运行时完成,在语法分析阶段会做一些检查之类的工作
  • 运行时字符串拼接是找到一个大空间,通过内存复制的方式将两个字符串复制到里面
  • 通过使用concatstring{2, 3, 4, 5}来指定性优化2,3,4,5个字符串的拼接
  • 拼接过程中只分配一块内存,缓冲区够用(拼接后字符串小于32字节)的就从缓冲区上切一块下来
  • 通过unsafe手段,可以在运行时层面让string和[]byte指向同一块内存
  • 字符串和字符数组之间可以相互转换,并不是简单地指针引用,而是涉及到了复制。当内存大于32字节的时候,还需要申请堆内存,在密集转换的时候,需要评估性能损耗

总结

  • 字符串存储于静态存储区,只能被访问而不能被修改和国运
  • 存储使用UTF-8,与Go文件的编码方式相同
  • 引入rune类型来消除字符歧义,是4字节的int32整数
  • 字符串常量拼接发生在编译时,变量拼接发生在运行时
  • 拼接后的字符串小于32时直接调用临时缓存使用,大于32字节时需要在堆区分配内存,有性能损耗
  • 字符串和字符数组之间的相互转换涉及到复制,在频繁转换的时候需要考虑到性能损耗
  • src/runtime/string.go, syntax/scanner.go

【Go笔记】字符串
https://study.0x535a.cn/go-note/go-string/
Author
Stephen Zeng
Posted on
August 12, 2025
Licensed under