Go语言中,结构体中数据的排布与C语言几乎相同,遵循的顺序、对齐等规则也与C语言相同。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type MyStruct struct {
i int
j int
}

func myFunction(ms *MyStruct) {
ptr := unsafe.Pointer(ms)
for i := 0; i < 2; i++ {
c := (*int)(unsafe.Pointer((uintptr(ptr) + uintptr(8*i))))
*c += i + 1
fmt.Printf("[%p] %d\n", c, *c)
}
}

func main() {
a := &MyStruct{i: 40, j: 50}
myFunction(a)
fmt.Printf("[%p] %v\n", a, a)
}

编译运行的结果为:

1
2
3
4
$ go run main.go
[0xc000018180] 41
[0xc000018188] 52
[0xc000018180] &{41 52}

运行结果与预期相符合。

但是有一个点让我比较奇怪,myFunction中的这一行代码:

1
c := (*int)(unsafe.Pointer((uintptr(ptr) + uintptr(8*i))))

为什么是8 * i而不是4 * i,按理说,int类型的大小应该是4字节,这里为什么是8字节?

将代码中的8换成4之后,编译运行得到的结果如下:

1
2
3
[0xc0000180d0] 41
[0xc0000180d4] 2
[0xc0000180d0] &{8589934633 50}

int类型的大小为8字节,那么得到的结果符合预期。

查阅了相关资料,Go语言关于类型大小的规范如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type                    size in bytes
------ ------
uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64 8
float64, complex64 8
complex128 16
uint, int implementation-specific,
generally 4 on 32-bit
architectures, and 8 on
64-bit architectures.
uintptr implementation-specific,
large enough to store
the uninterpreted bits
of a pointer value.

在我的64位平台(x86_64)上,int类型自然是8字节大小了,同样uint类型也是8字节大小。

这个确实是有些反直觉的,Go语言是类C语言,C语言中int类型的大小为4字节,所以很容易认为Go语言中的int类型的大小也是4字节,但实际上不是,所以在学习中还是需要注意思维定势的问题。