我们知道在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