我们知道在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))
}
我们来简单说一说其中的运行原理:
unsafe.Pointerunsafe.PointeruintptrStr2sbyte2*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
}
答案是不可以。
Str2sbyteuintptr