go中string与[]byte的高效转换(unsafe包)

我们知道在go的设计确保了一些安全的属性来限制很多种可能出现错误的情况,因为go是一个强类型的静态类型语言。所以会在编译器对阻止一些不正确的类型转换。

在string和byte[]这两个类型中允许byte[]向string的直接转换,但是不允许byte[]向string的直接转换,写成代码大概是这样:

// yte[]直接转换为string,反过来就不可以了
var str = []byte("hello world")
var data = string(a)

当然我们也可以把string和byte[]用作另一种类型的初始化,这样可以做到两个类型的通用转换:

// string转bytes
var str string = "hello world"
var data []byte = []byte(str)
var data [10]byte
data[0] = 'H'
data[1] = 'W'
var str string = string(data[:])

以上的转换方法可行,但效率欠佳,因为每次的转换其实都伴随着所有数据的拷贝。

我们先来看看两种类型的底层数据结构的实现:

// string 的底层数组结构如下
struct string {
    unit8 *str
    int len
}

// 而 []byte 的底层结构如下
struct uint8 {
    unit8 *array
    int len
    int cap
}

我们可以看到其实内部差别并不大,只是slice中多了一个容量而已,这也很好理解,毕竟是动态扩展的。

所以我们可以使用unsafe包执行高效的转换。但是注意unsafe包中的内容无法保证和go的未来版本兼容,所以还是需要谨慎使用,我们来看看实现,这也是我们的最终可应用在代码中的版本

// string转ytes
func Str2sbyte(s string) (b []byte) {
    *(*string)(unsafe.Pointer(&b)) = s  // 把s的地址付给b
    *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&b)) + 2*unsafe.Sizeof(&b))) = len(s) // 修改容量为长度
    return
}

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

我们来简单说一说其中的运行原理:

  1. unsafe.Pointer可以获取参数中值的地址。
  2. unsafe.Pointer可以转换为uintptr类型,后者保存着指针指向地址的数值,值得一提的是,uintptr的大小并不明确,但是可存放完整指针
  3. 对于Str2sbyte的第二条语句,也就是修改容量这里,我们使用加上2*unsafe.Sizeof(&b)来使得指针的偏移指向cap,因为前两个值的大小其实和一个指针的大小是一样的。

有一点值得思考,就是我们是否需要把uintptr类型的值当做一个临时变量,也就是类似于这样的写法:

func Str2sbyte(s string) (b []byte) {
    *(*string)(unsafe.Pointer(&b)) = s  
    temp := uintptr(unsafe.Pointer(&b))
    *(*int)(unsafe.Pointer(temp + 2*unsafe.Sizeof(&b))) = len(s)
    return
}

答案是不可以。

原因是temp中实际存储的是指针的地址值,而在go中一个变量的地址随时可能发生改变,因为go中原生支持协程,也就是goroutine,而在一个程序中goroutine的数量成千上万也并不奇怪,这也就意味着goroutine的栈不能是一个固定大小,否则对于并发的限制就太大了,所以goroutine的栈是可增长的,所以其初始值并不大,一般是2KB,所以在运行过程中经常可能出现栈的改变,这意味着所有值的地址都会发生改变。回到上面修改后的Str2sbyte函数,如果在第二句和第三句之间出现栈切换,那么我们记录的uintptr就无效了,其指向了一个未知的地址,这可能导致一些及其隐晦的错误。还有一些GC算法也可能导致此类问题,比如移动的垃圾回收器,但是所幸当前go并没有使用此类垃圾回收器。

原文链接: https://www.cnblogs.com/lizhaolong/p/16437241.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    go中string与[]byte的高效转换(unsafe包)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/396047

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月5日 下午1:52
下一篇 2023年4月5日 下午1:52

相关推荐