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 来表示含义;
  • 需要切片操作;
相关文章
|
1天前
|
Java 编译器 Go
探索Go语言的性能优化技巧
在本文中,我们将深入探讨Go语言的底层机制,以及如何通过代码层面的优化来提升程序性能。我们将讨论内存管理、并发控制以及编译器优化等关键领域,为你提供一系列实用的技巧和最佳实践。
|
1天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
1天前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
1天前
|
安全 Go 开发者
破译Go语言中的并发模式:从入门到精通
在这篇技术性文章中,我们将跳过常规的摘要模式,直接带你进入Go语言的并发世界。你将不会看到枯燥的介绍,而是一段代码的旅程,从Go的并发基础构建块(goroutine和channel)开始,到高级模式的实践应用,我们共同探索如何高效地使用Go来处理并发任务。准备好,让Go带你飞。
|
2天前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
2天前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
3天前
|
Go 开发者
Go语言中的并发编程:从基础到实践
在当今的软件开发中,并发编程已经成为了一项不可或缺的技能。Go语言以其简洁的语法和强大的并发支持,成为了开发者们的首选。本文将带你深入了解Go语言中的并发编程,从基础概念到实际应用,帮助你掌握这一重要的编程技能。
|
4天前
|
Go
使用go语言将A助手加入项目中
使用go语言将A助手加入项目中
13 2
|
3天前
|
安全 Go 调度
探索Go语言的并发模型:Goroutine与Channel的魔力
本文深入探讨了Go语言的并发模型,不仅解释了Goroutine的概念和特性,还详细讲解了Channel的用法和它们在并发编程中的重要性。通过实际代码示例,揭示了Go语言如何通过轻量级线程和通信机制来实现高效的并发处理。
|
3天前
|
存储 安全 Go
Go语言切片:从入门到精通的深度探索###
本文深入浅出地剖析了Go语言中切片(Slice)这一核心概念,从其定义、内部结构、基本操作到高级特性与最佳实践,为读者提供了一个全面而深入的理解。通过对比数组,揭示切片的灵活性与高效性,并探讨其在并发编程中的应用优势。本文旨在帮助开发者更好地掌握切片,提升Go语言编程技能。 ###