package main
import (
"fmt"
"unsafe"
)
type V struct {
i int32
j int64
}
func (v *V) PrintI() {
fmt.Println(v.i)
}
func (v *V) PrintJ() {
fmt.Println(v.j)
}
func main() {
v := new(V)
iPointer := (*int32)(unsafe.Pointer(v))
*iPointer = int32(666)
v.PrintI()
jPointer := (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))
*jPointer = int64(777)
v.PrintJ()
}
我从[https://studygolang.com/articles/1414]获得了示例代码,我不知道为什么总是得到j的值为0。
答案 0 :(得分:4)
TL; DR:使用unsafe.Offsetof()
代替unsafe.Sizeof()
。有关更多信息,请参见Purpose of memory alignment。
结构(甚至变量)中的字段不一定会立即出现 在内存中的另一个。由于 数据类型是对齐的。这种对齐方式会因单词大小和 数据类型的大小。
在您的示例中,您尝试通过以下方式使用指针算术访问v.j
:
将v.i
的大小(即4个字节)添加到v.i
的指针中。这将
碰巧在32位系统上可以正常工作,因为其中的字长为4个字节
下一个字段v.j
将与内存中的4个字节对齐。试试看
在执行程序之前设置环境变量GOARCH=386
(386
是32位体系结构)。
但是在64位系统上,v.j
将与内存中的8个字节对齐,以便
处理器可以一次性从内存中获取整个8字节字段。如果它是
未对齐8个字节(字长),则需要两次抓取操作才能获得
完整值,因为它将存储在两个不同的词中。结果,&v.i
+ 4个字节的值不是起点
v.j
中的。相反,它指向未未分配给任何字段的内存区域,因此v.j
实际上没有被修改。
虽然正确的方法是让Go向您提供有关何处的信息
v.j
使用unsafe.Offsetof(v.j)
在内存中开始。然后,您可以存储
该位置的int64
值。
这是您的代码的略微修改版本,以演示该问题。它 打印结构中每个字节的十六进制值。
// Slightly modified original code to print all the bytes in the struct
package main
import (
"fmt"
"unsafe"
)
type V struct {
i int32
j int64
}
func (v *V) PrintI() {
fmt.Println(v.i)
}
func (v *V) PrintJ() {
fmt.Println(v.j)
}
// PrintStruct prints every byte in the struct individually
func (v *V) PrintStruct() {
for i := uintptr(0); i < unsafe.Sizeof(*v); i++ {
b := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + i))
fmt.Printf("%2d %02x\n", i, *b)
}
}
func main() {
v := new(V)
iPointer := (*int32)(unsafe.Pointer(v))
// Use hex values so they're easy to see in the output
*iPointer = int32(0x01020304) // 4 bytes
jPointer := (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))
*jPointer = int64(0x08090A0B0C0D0E0F) // 8 bytes
fmt.Println("Assign using offset Sizeof")
v.PrintStruct()
fmt.Println("")
v = new(V)
v.i = 0x01020304
v.j = 0x08090A0B0C0D0E0F
fmt.Println("Assign directly to the fields")
v.PrintStruct()
}
这是我在以低字节序存储数据的64位计算机上运行时得到的输出:
Assign using offset Sizeof
0 04
1 03
2 02
3 01
4 0f
5 0e
6 0d
7 0c
8 0b
9 0a
10 09
11 08
12 00
13 00
14 00
15 00
Assign directly to the fields
0 04
1 03
2 02
3 01
4 c0
5 00
6 00
7 00
8 0f
9 0e
10 0d
11 0c
12 0b
13 0a
14 09
15 08
请注意如何将值分配给中的其他(不正确)位置
尝试将值存储在大小为0的偏移量时的内存
int32
。将unsafe.Sizeof(int32(0))
更改为unsafe.Offsetof(v.j)
,然后
应该给您正确的结果。