我有一个小函数,它将 Go 字符串数据的指针传递给 C(Lua 库):
func (L *C.lua_State) pushLString(s string) {
gostr := (*reflect.StringHeader)(unsafe.Pointer(&s))
C.lua_pushlstring(L, (*C.char)(unsafe.Pointer(gostr.Data)), C.ulong(gostr.Len))
// lua_pushlstring copies the given string, not keeping the original pointer.
}
它可以在简单的测试中工作,但从文档来看,尚不清楚这是否安全。
根据Go文档,
reflect.StringHeader
的内存应该固定为gostr,但是Stringheader.Data
已经是一个uintptr
,“没有指针语义的整数值” - 这本身就是奇怪的因为如果它没有指针语义,那么该字段是否完全无用,因为内存可能会在读取值后立即移动?还是像reflect.Value.Pointer
一样对字段进行了特殊处理?或者也许有不同的方法从字符串获取 C 指针?
尚不清楚这是否安全。
2022 年第 4 季度:Tapir Liui (https://twitter.com/TapirLiu/) 和 Go101 (https://github.com/go101/go101) 给出了关于 reflect 的“安全性”的线索。 此推文中的 StringHeader:
和reflect.StringHeader
类型将被贬值,不建议使用。reflect.SliceHeader
因此,Go 1.20 中将引入两个函数
和unsafe.StringData
来接管两个旧反射类型的用例。unsafe.SliceData
这最初在 CL 401434 中讨论,然后在 issue 53003 中讨论。
弃用的原因是
和reflect.SliceHeader
经常被误用。reflect.StringHeader
此外,这些类型一直被记录为不稳定且不值得依赖。我们可以在 Github code search 中看到这些类型的使用无处不在。
我见过的最常见的用例是:
- 将
转换为[]byte
:string
相当于,实际上从未在任何地方正式记录为可以信赖的东西。*(*string)(unsafe.Pointer(&mySlice))
在引擎盖下,的形状小于切片,因此根据不安全规则这似乎是有效的。string
- 将
转换为string
:[]byte
通常被视为,默认情况下会被破坏,因为*(*[]byte)(unsafe.Pointer(&string))
字段可能会超出页面边界的末尾(此处的示例,在广泛使用的代码中)——这违反了不安全规则。Cap
- 获取 ffi 的数据指针字段或其他一些利基使用将一种类型的切片转换为另一种类型的切片
unsafe.Slice 的主要用例之一是创建一个切片,其支持数组是从 C 代码或 syscall.MMap 等调用返回的内存缓冲区。
我同意它可以用于(不安全地)从一种类型的切片转换为另一种类型的切片。
2023 年第 4 季度:此问题已从提案中关闭/删除,问题 53003 的结论是:
为什么最初提出的函数
和StringToBytes
没有添加到不安全的包中,所以人们一次又一次地停止编写各种正确性有问题的实现,这超出了我的理解。BytesToString
回复:
因为:
现在只是StringToBytes
和unsafe.Slice(unsafe.StringData(s), len(s))
就是BytesToString
,unsafe.String(unsafe.SliceData(b), len(b))
这两者都非常简单,并且不应该有任何正确性值得怀疑的实现。