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打包附件内容到执行文件的方法

    Go打包附件内容到执行文件的方法

    处于种种原因, 我们不希望这部分额外的内容以附件的形式出现, 有没有什么办法能够将附件内容直接打包进可执行文件中呢,下面小编给大家介绍下Go打包附件内容到执行文件的方法,感兴趣的朋友一起看看吧
    2023-03-03
  • Golang中crypto/rand库的使用技巧与最佳实践

    Golang中crypto/rand库的使用技巧与最佳实践

    在Golang的众多随机数生成库中,crypto/rand 是一个专为加密安全设计的库,本文主要介绍了Golang中crypto/rand库的使用技巧与最佳实践,感兴趣的可以了解一下
    2024-02-02
  • go语言定时器Timer及Ticker的功能使用示例详解

    go语言定时器Timer及Ticker的功能使用示例详解

    这篇文章主要为大家介绍了go语言定时器的功能使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • intelliJ idea安装go开发环境并搭建go项目(打包)全过程

    intelliJ idea安装go开发环境并搭建go项目(打包)全过程

    最近在配置idea开发go语言时碰到很多问题,所以这里给大家总结下,这篇文章主要给大家介绍了关于intelliJ idea安装go开发环境并搭建go项目(打包)的相关资料,需要的朋友可以参考下
    2023-10-10
  • GO语言基本数据类型总结

    GO语言基本数据类型总结

    这篇文章主要介绍了GO语言基本数据类型,较为详细的总结了GO语言的基本数据类型,对于GO语言的学习有一定的借鉴参考价值,需要的朋友可以参考下
    2014-12-12
  • golang实现京东支付v2版本的示例代码

    golang实现京东支付v2版本的示例代码

    这篇文章主要介绍了golang实现京东支付v2版本,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • go语言 http模型reactor示例详解

    go语言 http模型reactor示例详解

    这篇文章主要介绍了go语言 http模型reactor,接下来看一段基于reactor的示例,这里运行通过 go run main.go,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Go语言基础数组用法及示例详解

    Go语言基础数组用法及示例详解

    这篇文章主要为大家介绍了Go语言基础Go语言数组的用法及示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2021-11-11
  • 详解Go语言RESTful JSON API创建

    详解Go语言RESTful JSON API创建

    这篇文章主要介绍了详解Go语言RESTful JSON API创建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Go语言使用钉钉机器人推送消息的实现示例

    Go语言使用钉钉机器人推送消息的实现示例

    本文主要介绍了Go语言使用钉钉机器人推送消息的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09

最新评论