英文:
Is it safe to directly convert a struct 'point' to another struct using unsafe.Pointer?
问题
这是一个关于Go语言的代码片段,主要涉及到指针和类型转换。以下是翻译好的内容:
这段代码中使用了指针和类型转换。(*TeamData)(unsafe.Pointer(&team.Id))
这一行代码将team.Id
的地址转换为*TeamData
类型的指针。这种转换是不安全的,因为它绕过了Go语言的类型系统。
在testTrans
函数中,通过遍历teams
切片,将每个team.Id
的地址转换为*TeamData
类型的指针,并添加到teamDatas
切片中。最后,teamDatas
切片作为函数的返回值。
关于teams := testTrans()
数组的成员是否会被垃圾回收,这取决于具体的上下文。如果teamDatas
切片在函数外部被引用,那么其中的元素不会被垃圾回收。如果teamDatas
切片只在函数内部使用,并且没有被返回或者被其他地方引用,那么其中的元素可能会被垃圾回收。
需要注意的是,使用不安全的指针转换可能会导致内存安全问题和未定义行为。在使用这种转换之前,应该仔细考虑代码的安全性和可维护性。
完整的示例代码可以在以下链接中找到:https://go.dev/play/p/q3gwp2mERvj
英文:
Is it safe?
> (*TeamData)(unsafe.Pointer(&team.Id))
Example code:
func testTrans() []*TeamData {
teams := createTeams()
teamDatas := make([]*TeamData, 0, len(teams))
for _, team := range teams {
// is this safe?
teamDatas = append(teamDatas, (*TeamData)(unsafe.Pointer(&team.Id)))
}
return teamDatas
}
// ??
teams := testTrans()
Will the members of the teams := testTrans()
array be garbage collected?
There are many structs and many fields returned through grpc and their definitions are the same as the local definitions, so I want to use this more efficient way((*TeamData)(unsafe.Pointer(&team.Id))
), but I don't know if there will be any risks.
Full Example:
https://go.dev/play/p/q3gwp2mERvj
答案1
得分: 1
unsafe.Pointer 的文档描述了其支持的用法。特别是:
(1) 将 *T1 转换为 *T2 的指针。
假设 T2 不比 T1 大,并且两者具有相同的内存布局,这种转换允许将一种类型的数据重新解释为另一种类型的数据。
Go 的垃圾回收器识别内部指针,并且只有在没有对该块的引用时才会回收原始分配。因此,在存在对 *TeamData
的引用时,较大的分配(例如你的示例中的 GrpcRetTeam
)将被固定。
另一个关键的考虑因素是结构字段的对齐方式。例如:
type Parent struct {
A uint8
B uint8
// 6 字节的填充以对齐 C。
C uint64
}
type Bad struct {
B uint8
// 7 字节的填充以对齐 C。
C uint64
}
在这种情况下,使用 unsafe 从 Parent
中提取 Bad
将是无效的,因为内存布局不同。
在大多数情况下,最好避免使用 unsafe.Pointer
的技巧,除非需要满足功能或性能要求。通常可以重构代码以最小化分配。
如果必须使用 unsafe
来满足性能要求 -
我建议使用 reflect
包实现一个测试,以确保子结构体的内存对齐/布局是有效的。
英文:
The documentation for unsafe.Pointer describes supported uses. In particular:
> (1) Conversion of a *T1 to Pointer to *T2.
>
> Provided that T2 is no larger than T1 and that the two share an
> equivalent memory layout, this conversion allows reinterpreting data
> of one type as data of another type.
Go's garbage collector recognises interior pointers an will not collect the original allocation until there are no remaining references to that block.
Hence the larger allocation (GrpcRetTeam
in your example) will be pinned while references to *TeamData
exists.
Another critical consideration is the alignment of the struct fields. Eg:
type Parent struct {
A uint8
B uint8
// 6 bytes of padding to align C.
C uint64
}
type Bad struct {
B uint8
// 7 bytes of padding to align C.
C uint64
}
In this case it would be invalid to use unsafe to extract Bad
from Parent
since the memory layout is different.
In most cases it's typically better to avoid unsafe.Pointer
tricks unless required to meet functionality or performance requirements. It's often possible to refactor code to minimise allocations instead.
If you must use unsafe
to meet performance requirements --
I would recommend implementing a test using the reflect
package to ensure the memory alignment/layout is valid for the child struct.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论