Golang – 根据地址计算字段索引

huangapple go评论75阅读模式
英文:

Golang - Compute field index based on address

问题

根据地址偏移量计算结构体字段索引是否安全(与平台/编译器无关)?

type T struct {
    F1 int
    F2 int
    F3 string
    F4 float64
    F5 bool
}

t := new(T)
t.F1 = 1
t.F2 = 2
t.F3 = "hello world!"
t.F4 = 1234.7283823
t.F5 = true

fmt.Println("T address:", &t)
fmt.Println("t.F1 address:", &t.F1, "size:", unsafe.Sizeof(t.F1), "offset:", unsafe.Offsetof(t.F1), "value:", t.F1)
fmt.Println("t.F2 address:", &t.F2, "size:", unsafe.Sizeof(t.F2), "offset:", unsafe.Offsetof(t.F2), "value:", t.F2)
fmt.Println("t.F3 address:", &t.F3, "size:", unsafe.Sizeof(t.F3), "offset:", unsafe.Offsetof(t.F3), "value:", t.F3)
fmt.Println("t.F4 address:", &t.F4, "size:", unsafe.Sizeof(t.F4), "offset:", unsafe.Offsetof(t.F4), "value:", t.F4)
fmt.Println("t.F5 address:", &t.F5, "size:", unsafe.Sizeof(t.F5), "offset:", unsafe.Offsetof(t.F5), "value:", t.F5)

编辑

也许"offset"这个词让问题有点混淆。当运行上述代码时,地址空间是在增长的。我想确切地知道的是结构体字段是否按照它们的定义顺序分配。

英文:

Is it safe to compute a structure field index based on its address offset (platform/compiler independent)?

type T struct {
	F1 int
	F2 int
	F3 string
	F4 float64
	F5 bool
}

t := new(T)
	t.F1 = 1
	t.F2 = 2
	t.F3 = "hello wordl!"
	t.F4 = 1234.7283823
	t.F5 = true
	
	fmt.Println("T address:", &t)
	fmt.Println("t.F1 address:", &t.F1, "size: ", unsafe.Sizeof(t.F1), "offset: ", unsafe.Offsetof(t.F1), "value: ", t.F1)
	fmt.Println("t.F2 address:", &t.F2, "size: ", unsafe.Sizeof(t.F2), "offset: ", unsafe.Offsetof(t.F2), "value: ", t.F2)
	fmt.Println("t.F3 address:", &t.F3, "size: ", unsafe.Sizeof(t.F3), "offset: ", unsafe.Offsetof(t.F3), "value: ", t.F3)
	fmt.Println("t.F4 address:", &t.F4, "size: ", unsafe.Sizeof(t.F4), "offset: ", unsafe.Offsetof(t.F4), "value: ", t.F4)
	fmt.Println("t.F5 address:", &t.F5, "size: ", unsafe.Sizeof(t.F5), "offset: ", unsafe.Offsetof(t.F5), "value: ", t.F5)

Edit

Maybe the offset word makes the question a bit confusing. When the above code is run the address space is growing. What I want to know exaclty is if the struct fields are allocated in the order of their definition.

答案1

得分: 6

“这个安全吗?”好的,让我来告诉你。你正在使用一个名为unsafe的包。

“假设不对垃圾回收器等进行根本性的更改,这通常可以正常工作吗?”是的,可能可以。

根据unsafe文档中的OffsetOf

OffsetOf返回结构体中由v表示的字段的偏移量,v必须具有structValue.field的形式。换句话说,它返回从结构体的开始到字段的开始之间的字节数。

换句话说,它保证考虑了对齐等问题。unsafe.Pointer(uintptr(&t) + unsafe.OffsetOf(t.F1))保证是指向该特定实例的字段F1的实际不安全指针,就在那个特定的时刻。

在使用unsafe时仍然需要非常小心,不安全指针通常不被视为对象的“引用”以进行垃圾回收。因此,如果对该对象的唯一引用是该字段的不安全指针,可能会发生奇怪的事情。此外,有关实施“压缩式GC”的讨论,这意味着在进行垃圾回收时,内存可能会被重新排列,这意味着除非固定该对象(将在压缩式GC中添加),否则不安全指针可能会在您不知情的情况下自动变为无效(常规指针当然会保持完好)。

如果你确信会保留对它的引用,并且你对将来的更改完全破坏你的代码没有问题,那就去做吧。我的意思是,确保你确实需要使用unsafe,但如果你认为这是你问题的最佳或唯一可能的解决方案,那就去做吧,前提是你接受这些风险。

编辑:至于“内存顺序”的问题。嗯,让我们说我在规范中找不到保证结构体按字段顺序排列的参考。也就是说,如果不是这样的话,某些东西(例如encoding/binary.Read)将变得...应该说...不太有意义。

因此,在超级技术意义上不要“依赖它”,但对于任何主流的Go编译器,你可以大致确保字段按内存顺序排列 - 当然还有关于字对齐和打包的常规注意事项。

英文:

"Is it safe" well, let me stop you there. You're using a package called unsafe.

"Can this generally be expected to work assuming no radical changes are made to things like the Garbage Collector?" Yeah, probably.

From the unsafe documentation for OffsetOf:

> Offsetof returns the offset within the struct of the field represented
> by v, which must be of the form structValue.field. In other words, it
> returns the number of bytes between the start of the struct and the
> start of the field.

In other words, it's guaranteed to take into account issues of alignment and such. unsafe.Pointer(uintptr(&t) + unsafe.OffsetOf(t.F1)) is guaranteed to be the actual unsafe pointer to the actual unsafe address of the field F1 of that specific instance of t at that precise moment in time.

You still have to be very careful about unsafe, unsafe pointers don't generally count as "references" to an object for garbage collection purposes. So if your only reference to that object is that unsafe pointer to that field... weird things may happen. In addition, there's been talk of implementing a "compacting GC" which means that on Garbage Collection memory may be shuffled around, which means that unless you pin the object (which will be added with the compacting GC), the unsafe pointer could spontaneously become invalid without you knowing (regular pointers would remain in tact of course).

If you're sure you'll keep a reference to it, and you're okay with future changes totally breaking your code, go for it. I mean, make absolutely sure you need to use unsafe, but if you've deemed this is the best or only possible solution to your problem, go for it assuming you accept those risks.

Edit: As for "memory order" business. Well, let's just say that I can't find a reference in the spec that guarantees structs are laid out in field order. That said, certain things like encoding/binary.Read would make... shall we say... less than a lot of sense if they weren't.

So don't "rely on it" in a super technical sense, but for any mainstream Go compiler, you can probably be assured fields are in memory order -- with all the usual caveats about word alignment and packing that brings.

答案2

得分: 2

也许"offset"这个词让问题有点混淆。当上述代码运行时,地址空间是在增长的。我想确切地知道的是结构体字段是否按照它们的定义顺序分配。

一般情况下,这应该是成立的,但要注意像这样的特殊情况:

type foo struct {
    a int
    b [0]byte
    c int
}

此外,Go在技术上并不保证这一点。我没有看到任何标准的Go实现会改变字段的顺序。然而,请注意,有一些非标准的实现(例如将Go代码编译为JavaScript的一些实现)并不将结构体实现为连续的内存位置,这时unsafe.Offsetof的概念就变得无意义了。据我所知,这些实现不提供unsafe包,所以你的代码无论如何都无法编译。

简而言之:是的。

英文:

> Maybe the offset word makes the question a bit confusing. When the above code is run the address space is growing. What I want to know exaclty is if the struct fields are allocated in the order of their definition.

This should generally hold, but watch out for corner cases like this:

type foo struct {
    a int
    b [0]byte
    c int
}

Also, Go technically doesn't guarantee this. I don't see any reason why any standard Go implementation would change the field-order. Notice however, that there are non-standard implementations (e.g. some implementations that compile Go code to JavaScript) of the Go runtime that do not implement structs as consecutive memory locations, where the notion of unsafe.Offsetof becomes meaningless. These implementations don't provide the unsafe package as far as I know, so your code will not compile anyway.

TL;DR: Yes.

huangapple
  • 本文由 发表于 2014年6月10日 01:31:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/24125604.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定