Go底层之string和[]byte相互转换原理分析

 更新时间:2025年06月26日 08:36:23   作者:在成都搬砖的鸭鸭  
这篇文章主要介绍了Go底层之string和[]byte相互转换原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

背景

在写go代码的过程中,我们会经常遇到字节数组([]byte)和字符串(string)的转换,比如将[]byte转换为string:string(b);

将string转换为[]byte:[]byte(s),这种转换并不是无损耗的,在数据量大的时候会在堆上申请一块新的内存去存储转换之后的数据,要对性能有要求的情况下就要考虑避免这种转换,接下来我们来看一下源码实现。

Go版本

$ go version
go version go1.21.4 windows/386

源码解释

【1】string转[]byte

[]byte(s)这种转换方式实际上用了标准库中的stringtoslicebyte函数,文件路径:src/runtime/string.go

func stringtoslicebyte(buf *tmpBuf, s string) []byte {
	var b []byte //定义一个字节数组
	if buf != nil && len(s) <= len(buf) { //临时buf足够就使用临时buf作为返回的字节数组
		*buf = tmpBuf{}
		b = buf[:len(s)]
	} else {  //临时buf不够就申请一块空间作为返回的字节数组
		b = rawbyteslice(len(s))
	}
	copy(b, s) //将字符串底层数组的内容拷贝到字节数组中
	return b //返回字节数组
}

这种转换方式将字符串底层数组的内容拷贝到另一块空间再返回,因为使用了新的空间地址并且涉及到了拷贝,是有一定性能损耗的。

也有方法直接将字符串的底层数组作为字节数组返回,不使用额外的内存空间和拷贝,但是这个操作比较危险,一旦修改字节数组的内容就会影响到字符串底层数组的内容,而字符串是不允许修改的。

【2】[]byte转string

string(b)这种转换方式用了标准库中的slicebytetostring函数,文件路径:src/runtime/string.go

func slicebytetostring(buf *tmpBuf, ptr *byte, n int) string {
	if n == 0 { //字节长度为0时,返回空字符串
		return ""
	}
	
	if n == 1 { //字节长度为1时
		p := unsafe.Pointer(&staticuint64s[*ptr]) //得到指向字节数组地址对应的int64的值
		if goarch.BigEndian { //是否为大端字节序
			p = add(p, 7) //指针后移7字节
		}
		return unsafe.String((*byte)(p), 1) //返回单字节字符串
	}

	var p unsafe.Pointer
	if buf != nil && n <= len(buf) { //字节数组长度小于临时buf长度直接使用临时buf作为字符串底层数组
		p = unsafe.Pointer(buf)
	} else { //字节数组长度大于临时buf长度在堆上新申请一块空间
		p = mallocgc(uintptr(n), nil, false)
	}
	memmove(p, unsafe.Pointer(ptr), uintptr(n)) //将字节数组内容拷贝到新的地址p上
	return unsafe.String((*byte)(p), n) //转换为string类型返回
}

这种转换方式将字节数组的内容拷贝到一个新的地址上做为字符串的底层数组,因为涉及到新的地址空间的申请和拷贝,所以是有一定开销的。

标准库也提供了直接将要转换的字节数组作为字符串底层数组的方法,slicebytetostringtmp:

func slicebytetostringtmp(ptr *byte, n int) string {
	if raceenabled && n > 0 {
		racereadrangepc(unsafe.Pointer(ptr),
			uintptr(n),
			getcallerpc(),
			abi.FuncPCABIInternal(slicebytetostringtmp))
	}
	if msanenabled && n > 0 {
		msanread(unsafe.Pointer(ptr), uintptr(n))
	}
	if asanenabled && n > 0 {
		asanread(unsafe.Pointer(ptr), uintptr(n))
	}
	return unsafe.String(ptr, n) //直接使用要转换的字节数组作为底层数组
}

同样如果修改了字节数组的内容,字符串也会被影响,不推荐使用。

总结

字符串和字节数组转换虽然很简单,但是会带来一定的开销,当然并不是就不能使用这种转换方式,最主要还是根据我们的业务场景去使用,大部分业务场景性能要求并不是在这,但是我们也要尽量避免频繁的进行字符串和字节数组的相互转换。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Go语言Goroutinue和管道效率详解

    Go语言Goroutinue和管道效率详解

    这篇文章主要为大家介绍了Go语言Goroutinue和管道效率使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Go语言struct类型介绍

    Go语言struct类型介绍

    这篇文章主要介绍了Go语言struct类型介绍,本文讲解了struct的2种声明方式,struct的匿名字段等内容,需要的朋友可以参考下
    2015-01-01
  • 从零封装Gin框架及项目初始化教程

    从零封装Gin框架及项目初始化教程

    这篇文章主要为大家介绍了从零封装Gin框架及项目的初始化教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Golang 数据库操作(sqlx)和不定字段结果查询

    Golang 数据库操作(sqlx)和不定字段结果查询

    本文主要介绍了Golang 数据库操作(sqlx)和不定字段结果查询,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 浅析Go中关于零值和空值判断的问题

    浅析Go中关于零值和空值判断的问题

    这篇文章主要是对零值和空值判断现状进行简单的梳理和分享,文中的示例代码讲解详细,对我们深入了解go语言有一定的帮助,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-08-08
  • golang实现ping命令的完整代码

    golang实现ping命令的完整代码

    这篇文章给大家介绍了如何使用golang实现ping命令,文中给大家介绍了完整的实现代码,并有详细的图文讲解,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-02-02
  • go语言中布隆过滤器低空间成本判断元素是否存在方式

    go语言中布隆过滤器低空间成本判断元素是否存在方式

    这篇文章主要为大家介绍了go语言中布隆过滤器低空间成本判断元素是否存在方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Golang gin跨域解决方案示例

    Golang gin跨域解决方案示例

    这篇文章主要为大家介绍了Golang gin跨域解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Golang 实现Redis 协议解析器的解决方案

    Golang 实现Redis 协议解析器的解决方案

    这篇文章主要介绍了Golang   实现 Redis 协议解析器,本文将分别介绍Redis 通信协议 以及 协议解析器 的实现,若您对协议有所了解可以直接阅读协议解析器部分,需要的朋友可以参考下
    2022-10-10
  • Go表达式引擎expr基础用法实战指南

    Go表达式引擎expr基础用法实战指南

    expr是一个高性能的 Go 表达式引擎,它允许你在代码中安全地执行动态生成的表达式,本文给大家介绍Go表达式引擎expr基础用法实战指南,感兴趣的朋友跟随小编一起看看吧
    2025-10-10

最新评论