String 在go中使用从[]字节到字符串的不安全转换可能产生什么后果?
将String 在go中使用从[]字节到字符串的不安全转换可能产生什么后果?,string,pointers,go,unsafe,String,Pointers,Go,Unsafe,将[]字节转换为字符串的首选方法如下: var b []byte // fill b s := string(b) 在这个代码中,字节片被复制,这在性能很重要的情况下可能是一个问题 当性能是关键时,可以考虑执行不安全转换: var b []byte // fill b s := *(*string)(unsafe.Pointer(&b)) 我的问题是:使用不安全转换时会出现什么问题?我知道string应该是不可变的,如果我们改变b,s也会改变。还有:那又怎样?这一切都是坏事吗?修改
[]字节
转换为字符串
的首选方法如下:
var b []byte
// fill b
s := string(b)
在这个代码中,字节片被复制,这在性能很重要的情况下可能是一个问题
当性能是关键时,可以考虑执行不安全转换:
var b []byte
// fill b
s := *(*string)(unsafe.Pointer(&b))
我的问题是:使用不安全转换时会出现什么问题?我知道
string
应该是不可变的,如果我们改变b
,s
也会改变。还有:那又怎样?这一切都是坏事吗?修改语言规范保证不可变的东西是叛国行为
由于规范保证string
s是不可变的,因此允许编译器生成缓存其值的代码,并在此基础上进行其他优化。您不能以任何正常方式更改字符串
s的值,如果您仍然使用肮脏的方式(如packageunsafe
)进行更改,您将失去规范提供的所有保证,继续使用修改后的字符串
s,您可能会随机遇到“bug”和意外情况
例如,如果将字符串
用作映射中的键,并在将其放入映射后更改字符串
,则可能无法使用字符串
的原始值或修改值在映射中找到关联值(这取决于实现)
要演示这一点,请参见以下示例:
m := map[string]int{}
b := []byte("hi")
s := *(*string)(unsafe.Pointer(&b))
m[s] = 999
fmt.Println("Before:", m)
b[0] = 'b'
fmt.Println("After:", m)
fmt.Println("But it's there:", m[s], m["bi"])
for i := 0; i < 1000; i++ {
m[strconv.Itoa(i)] = i
}
fmt.Println("Now it's GONE:", m[s], m["bi"])
for k, v := range m {
if k == "bi" {
fmt.Println("But still there, just in a different bucket: ", k, v)
}
}
b := []byte{'h', 'i'}
s := *(*string)(unsafe.Pointer(&b))
s2 := s // Copy string header
s3 := string([]byte(s)) // New string header but same content
fmt.Println(s, s2, s3)
b[0] = 'b'
fmt.Println(s == s2)
fmt.Println(s == s3)
我们使用s
创建了两个新的局部变量s2
和s3
,s2
通过复制s
的字符串头进行初始化,s3
使用一个新的字符串
值(新字符串头)进行初始化,但内容相同。现在,如果您修改原始的s
,您将期望在正确的程序中,将新字符串与原始字符串进行比较,您将得到相同的结果,无论是true
还是false
(基于是否缓存了值,但应该是相同的)
但输出是(在上尝试):
切勿使用不安全的工具。性能从来没有那么重要。
b := []byte{'h', 'i'}
s := *(*string)(unsafe.Pointer(&b))
s2 := s // Copy string header
s3 := string([]byte(s)) // New string header but same content
fmt.Println(s, s2, s3)
b[0] = 'b'
fmt.Println(s == s2)
fmt.Println(s == s3)
hi hi hi
true
false