go 使用unsafe包实现string和[]byte的转换

目录

直接转换和无类型指针转换的差异

方法一:直接转换

方法二:无类型指针转换

两张方法性能比较


在考核的时候,遇到了一个转换问题:将 string 转换为 \[\]byte 类型。

平常一直用的都是直接转换,这种转换比较方便,但是性能却比较低。然后学习到了用 unsafe 包实现 string 和 \[\]byte 之间的相互转换,这种方法性能高,但是不安全。我们接下来比较一下这两种方法。

直接转换和无类型指针转换的差异

方法一:直接转换

Go 复制代码
str := "Hello, Go!"
bytes := []byte(str)
fmt.Println(bytes)

直接转换性能比较低,因为字符串是只读的字节序列,使用 \[\]byte(str) 的时候会分配一块新的内存,然后将字符串中的数据复制到新的 \[\]byte 中。(内存分配+数据复制)

方法二:无类型指针转换

先将 str 转换为空类型指针,然后再转换为 \[\]byte。

Go 复制代码
str := "Hello, World!"
// unsafe.Pointer(&str):将字符串地址转为通用指针类型。
// *(*[]byte)(   ):将 unsafe.Pointer 转换为 []byte 的指针,并解引用为实际的 []byte 值。
b := *(*[]byte)(unsafe.Pointer(&str))
fmt.Println(b, string(b)) // [72 101 108 108 111 44 32 87 111 114 108 100 33] Hello, World!

优点是高性能,这种方法避免了内存分配和数据复制,直接共享字符串的底层数据。

缺点是不安全,使用了 unsafe 包,如果我们修改返回的 \[\]byte:

Go 复制代码
b[0] = 'a' // 报错

会报错,因为 b 是由 str 的底层数据得来的,如果修改 b 会违背 Go语言字符串的不可变性。

两种方法性能比较

目录结构:

main.go :

Go 复制代码
package main

import (
	"unsafe"
)

// BytesToString []byte 转换为 string
func BytesToString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// StringToBytes string 转换为 []byte
func StringToBytes(s string) []byte {
	return *(*[]byte)(unsafe.Pointer(&s))
}

demo_test.go:

Go 复制代码
package main

import "testing"

var (
	strs = []string{
		"hello wrold!!!",
		"jfoewjfilll",
		"jiayou.",
		"12428jg9k",
		"_*^HfewoHF&3",
		"thanks",
		"Regardless of your past, take account your present and future.\n\n",
		"Make the choice to be happy. The biggest part of being happy is to simply make up your mind to be a happy person.",
	}
)

// BenchmarkDirectConvert 直接转换
func BenchmarkDirectConvert(b *testing.B) {
	n := len(strs)
	for i := 0; i < b.N; i++ {
		for j := 0; j < n; j++ {
			demo := []byte(strs[j])
			_ = string(demo)
		}
	}
}

// BenchmarkUnsafeConvert 使用 unsafe 包转换
func BenchmarkUnsafeConvert(b *testing.B) {
	n := len(strs)
	for i := 0; i < b.N; i++ {
		for j := 0; j < n; j++ {
			demo := StringToBytes(strs[j])
			_ = BytesToString(demo)
		}
	}
}

运行:

可以看到使用 unsafe包 的性能比直接转换性能高。

区别总结

|--------|----------------|------------------------|
| 特性 | \[\]byte(str) | 通过无类型指针转换 |
| 内存分配 | 分配新的内存 | 无额外分配,共享底层数据 |
| 数据安全 | 数据独立,修改互不影响 | 修改返回的 \[\]byte 会影响原字符串 |
| 性能 | 较低:需要分配内存和复制数据 | 较高:直接操作底层指针 |
| 易读性和维护 | 简单易读 | 较复杂 |
| 适用场景 | 通常是首选方法,安全易用 | 用于对性能要求极高且可控的场景 |

相关推荐
weixin_460443563 小时前
2026年新修订《网络安全法》实施:Java私有化考试系统如何做好SBOM、漏洞治理与安全升级?
java·开发语言·在线考试系统·企业培训考试系统·宏远培训考试系统·国企培训考试系统·国产化培训系统
Cc.Y7 小时前
Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战
java·开发语言·python
阿森_dev7 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
by209997 小时前
C++ 多态:同一个基类接口,调用不同的函数
开发语言·c++·经验分享·多态
笔墨登场说说7 小时前
flink bin/start-cluster.sh 帮我做成开机启动
开发语言·python
Sarvartha7 小时前
Lambda 表达式和函数式接口
java·开发语言
小白学大数据7 小时前
拼多多反爬对抗实战:Scrapy 中间件化采集架构解析
开发语言·scrapy·中间件·架构
学逆向的8 小时前
win32注入代码
开发语言·网络安全·api·win32
Sarvartha8 小时前
内部类知识
java·开发语言
FPGA小徐9 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#