我有这个函数将字符串转换为字节切片而不复制func StringToByteUnsafe(s string) []byte { strh := (*reflect.StringHeader)(unsafe.Pointer(&s)) var sh reflect.SliceHeader sh.Data = strh.Data sh.Len = strh.Len sh.Cap = strh.Len return *(*[]byte)(unsafe.Pointer(&sh))}这工作正常,但是通过非常具体的设置会产生非常奇怪的行为:设置在这里:https://github.com/leviska/go-unsafe-gc/blob/main/pkg/pkg_test.go会发生什么情况:创建字节切片将其转换为临时(右值)字符串,并使用不安全再次将其转换为字节切片然后,复制此切片(通过引用)然后,用戈鲁丁内的第二个切片做点什么打印前后的指针我在我的linux mint笔记本电脑上有这个输出,带有go 1.16:go test ./pkg -v -count=1=== RUN TestSomething0xc000046720 123 0xc000046720 1230xc000076f20 123 0xc000046721 z--- PASS: TestSomething (0.84s)PASSok github.com/leviska/go-unsafe-gc/pkg 0.847s因此,第一个切片神奇地改变了它的地址,而第二个切片则不是如果我们删除 goroutine(并且可能稍微玩弄一下代码),我们可以获取两个指针来更改值(更改为同一个指针)。runtime.GC()如果我们将不安全的强制转换更改为所有内容,则无需更改地址即可工作。此外,如果我们从这里将其更改为不安全的强制值,https://stackoverflow.com/a/66218124/5516391 一切工作原理相同。[]byte()func StringToByteUnsafe(str string) []byte { // this works fine var buf = *(*[]byte)(unsafe.Pointer(&str)) (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Cap = len(str) return buf}我运行它并得到相同的结果。我运行它,没有错误。GOGC=off-race如果将其作为具有 main 函数的主包运行,它似乎可以正常工作。此外,如果您删除该函数。我的猜测是编译器在这种情况下会优化内容。Convert所以,我对此有几个问题:这到底是怎么回事?看起来像一个奇怪的UB为什么运行时会神奇地更改变量的地址?为什么在无并发情况下它可以更改两个地址,而在并发情况下不能?这个不安全的强制转换与堆栈溢出答案的强制转换之间有什么区别?为什么它有效?或者这只是一个编译器错误?
1 回答

三国纷争
TA贡献1804条经验 获得超7个赞
来自github问题的回答 https://github.com/golang/go/issues/47247
的后备存储是在堆栈上分配的,因为它不会转义。戈鲁丁堆栈可以动态移动。另一方面,b逃逸到堆中,因为它被传递给另一个戈鲁廷。通常,我们不会假设对象的地址不会更改。
这按预期工作。
我的版本不正确,因为
它使用反射。切片标题作为普通结构。你可以对它进行兽医检查,去兽医会警告你。
- 1 回答
- 0 关注
- 73 浏览
添加回答
举报
0/150
提交
取消