Go语言,string 实现原理

简介: 在 Go 中,string 不包含内存空间,它只有一个内存指针,所以 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 来表示含义;
  • 需要切片操作;
相关文章
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
12 2
|
3天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
14 2
|
7天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
84 58
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
3天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
10 4
|
3天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
14 1
|
7天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
7天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。
|
5天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。