专栏首页Go语言指北Go高阶指南06,string 实现原理

Go高阶指南06,string 实现原理

string 概念

源代码中 src/builtin/builtin.go string 的描述如下:

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

  1. string 是所有 8 位字节字符串的集合,通常但不一定代表 UTF-8 编码的文本。
  2. 字符串可以为空(长度为0),但不会是 nil。
  3. 字符串类型的值是不可变的。

string 数据结构

源码包中 src/runtime/string.go:stringStruct 定义的 string 的数据结构如下:

type stringStruct struct {
 str unsafe.Pointer
 len int
}
  • str : 字符串的首地址
  • len : 字符串的长度

发现 string 的数据结构有点类似于切片,切片比它多了一个容量成员。string 和 byte 切片经常互转。

string 操作

字符串的构建是先构建 stringStruct,再转换成 string:

//go:nosplit
func gostringnocopy(str *byte) string {
 ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} // 先构造 stringStruct
 s := *(*string)(unsafe.Pointer(&ss)) // stringStruct 转换成 string
 return s
}

[]byte 转 string

示例:

func ByteToString(s []byte) string {
  return string(s)
}

注意这里的转换进行一次内存拷贝:

  1. 根据切片长度申请内存空间(假设内存地址为 p,切片长度为 len(b))
  2. 构建 string(string.str = p; string.len = len)
  3. 拷贝数据(切片中的数据拷贝到新的内存空间)

string 转 []byte

示例:

func StringToByte(str string) []byte {
  return []byte(str)
}

注意这里的转换也会进行一次内存拷贝:

  1. 申请切片内存空间
  2. 将 string 拷贝到切片

字符串拼接

示例:

str := "str1" + "str2" + "str3" + "str4"
  • 新字符串的内存空间是一次性分配好的,所以即使有很多的字符串进行拼接,性能也会有很好的保证。
  • 拼接语句在编译时会先放到一个切片中,然后再两次遍历此切片,一次获取字符串长度用来申请内存,一次用来把字符串逐个拷贝过去。
  • 由于 string 是不能修改的,源码在拼接过程中会用 「rawstring()」 方法生成一个指定大小的 string,并同时返回一个切片,二者共享同一块内存空间,后面向切片中拷贝数据,也就间接修改了 string。

rawstring()源代码如下:

// rawstring allocates storage for a new string. The returned
// string and byte slice both refer to the same storage.
// The storage is not zeroed. Callers should use
// b to set the string contents and then drop b.
func rawstring(size int) (s string, b []byte) {
 p := mallocgc(uintptr(size), nil, false)

 stringStructOf(&s).str = p
 stringStructOf(&s).len = size

 *(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}

 return
}

string 不能修改

在 Go 中,string 不包含内存空间,它只有一个内存指针,所以 string 非常轻量,很方便进行传递且不用担心内存拷贝。string 通常指向字符串字面量,字面量存储的位置是只读段,并不是堆或栈上,所以 string 不能被修改。

[]byte 转 string 不拷贝内存的情况

有时只是临时需要字符串的场景下,byte切片转换成 string 时并不会拷贝内存,而是直接返回一个 string,这个 string 的指针指向切片的内存。

如:

  • 使用 m[string(b)] 来查找map(map是string为key,临时把切片 b 转成string)
  • 字符串拼接,如”<” + “string(b)” + “>”
  • 字符串比较:string(b) == “foo”

用 []byte 还是 string

[]byte 和 string 都可以表示字符串,它们数据结构不同,其衍生出来的方法也不同。

string 擅长的场景:

  • 需要字符串比较;
  • 不需要nil字符串;

[]byte擅长的场景:

  • 修改字符串的时候;
  • 函数返回值,需要使用 nil 来表示含义;
  • 需要切片操作;

有什么问题,可以公众号内回复或加我微信交流。

本文分享自微信公众号 - 微客鸟窝(gophpython),作者:有码无尘

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go高阶指南04,struct 实现原理

    Go 语言中没有像类的概念,但是可以通过结构体 struct实现面向对象编程。struct 结构体的一些基础使用可以看下之前的文章--结构体和接口的使用,传送门...

    微客鸟窝
  • Go高阶指南05,iota 实现原理

    我们知道,iota 常用于 const 表达式中,它的值是从 0 开始,每增加一行,iota 值 +1。

    微客鸟窝
  • Go高阶指南09,select 实现原理

    select 是 GO 语言中用来提供 IO 复用的机制,它可以检测多个 chan 是否 ready(可读/可写)。

    微客鸟窝
  • Go高阶指南02,slice 实现原理

    slice 切片,因为其可以方便的进行扩容、传递等,在实际应用中比数组更加灵活。切片的一些基础使用可以看下之前的文章,传送门。

    微客鸟窝
  • Go高阶指南10,一文搞懂 range 实现原理

    range 是 Go 语言用来遍历的一种方式,它可以操作数组、切片、map、channel 等。

    微客鸟窝
  • Go高阶指南07,一文搞懂 defer 实现原理

    defer 语句用于延迟函数的调用,使用 defer 关键字修饰一个函数,会将这个函数压入栈中,当函数返回时,再把栈中函数取出执行。

    微客鸟窝
  • Go高阶指南14,内存的分配原理

    Go 中实现的内存分配器,简单的说就是维护了一大块全局内存,每个线程(Go 中的 P)维护一小块的私有内存,当私有内存不足时再向全局申请。内存分配与 GC(垃圾...

    微客鸟窝
  • 我的书单

    songleo
  • PHP转Go速学手册

    整理了一份简要的手册,帮助大家高效的上手Go语言,主要是通过对比PHP和Go的不同点来强化理解,内容主要分为以下四部分:

    用户1093396
  • Go高阶11,手摸手带你深入了解 Mutex 实现原理

    互斥锁是对于并发程序的共享资源进行访问控制的主要手段,之前在介绍并发的时候已经对互斥锁的使用进行过介绍:并发控制,同步原语 sync 包

    微客鸟窝
  • Go高阶12,手摸手带你深入了解 RWMutex 实现原理

    之前我们介绍了互斥锁 Mutex,今天再来介绍下 RWMutex,即读写锁。读写锁是对 Mutex 的改进,在程序中,如果存在读操作多,写操作少的场景,使用 R...

    微客鸟窝
  • go grpc 初步笔记

    像许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。

    solate
  • 【实践】如何在本地环境用GO实现HTTPS链接?

    本篇文章是基于实操,如何在本地环境用GO实现HTTPS链接。原理部分请参考文章《【深度知识】HTTPS协议原理和流程分析》。

    辉哥
  • Go 译文之词法分析与解析 Part Three

    最近发现我的翻译是越来越随性了,刚开始文章翻译的时候比较拘束,现在更多强调可读性,比如有些对文章大意没有什么影响的文字我现在都会选择直接跳过。

    波罗学
  • Github开源免费编程书籍

    时见疏星
  • Golang学习笔记之方法(method)

    一个方法只是一个函数,它有一个特殊的接收者(receiver)类型,该接收者放在 func 关键字和函数名之间。接收者可以是结构体类型或非结构体类型。可以在方法...

    李海彬
  • Go开发者路线图2019,请收下这份指南

    Go是Google开发的一种静态、强类型、编译型、并发型,并具有垃圾回收功能的类C编程语言。2009以开源项目的形式发布,2012年发布1.0稳定版本,距今已经...

    AI科技大本营
  • Web前端开发推荐阅读书籍、学习课程下载

    学校里没有前端的课程,那如何学习JavaScript,又如何使自己成为一个合格的前端工程师呢?

    慕白
  • 手把手教你用 reflect 包解析 Go 的结构体 - Step 2: 结构体成员遍历

    上一篇文章我们学习了如何用 reflect 检查一个参数的类型。这一篇文章,咱们获得了一个结构体类型,那么我们需要探究结构体内部的结构以及其对应的值。

    amc

扫码关注云+社区

领取腾讯云代金券