几种编程语言之结构体内存对齐

写在前面

计算机的内存空间是以 byte 来划分,因此我们可以简单的认为不同的数据类型,就占有相应大小的内存空间,但实际此举会为内存的访问带来效率上的问题。实际内存访问是会以一个固定字长来开始访问,而这样就会产生一个问题,有些数据类型的字长会大于或小于这个固定字长。
为了解决这个问题,不同的编译器会对不同的数据类型做一些规定,会分两步走:一个是每个数据类型所占的空间,应该是某个字节数的整数倍;另一个是如果实际类型变量所占的空间达不到规定的大小,就会做填充,使其符合。这个过程就是内存对齐。

内容

可以从 Go 语言里看一个例子:

package main

import (
	"fmt"
	"unsafe"
)

type demo1 struct {
	a int8
	b int16
	c int32
}

type demo2 struct {
	a int8
	c int32
	b int16
}

func main() {
	fmt.Println(unsafe.Sizeof(demo1{})) // 8
	fmt.Println(unsafe.Sizeof(demo2{})) // 12
}

可以看到,两个结构体里的属性其实是一样的,只是它们的顺序有所不同,导致打印出来的 size 也不同。那为什么呢?
以 demo1 为例,第一个变量类型为 int8,所占为 1 个字节,编译器规定为 1 的倍数,而它也是第一个,从下标为0的偏移量开始,所以无需对齐。
第二个变量 int16,规定对齐倍数是2,故需要在下标偏移量为2的地方开始,并且占2个字节,它前面的偏移量1会留空填充。
第三个变量 int32,规定对齐倍数是4,故需要在下标偏移量为4的地方开始,并且占4个字节。
因此 demo1 所占的空间为 1 + 1 + 2 + 4 = 8 个字节。
而 demo2 里,同样第一个变量类型为 int8,所占为 1 个字节,编译器规定为 1 的倍数,而它也是第一个,从下标为0的偏移量开始,所以无需对齐。
第二个变量 int32,规定对齐倍数是4,故需要在下标偏移量为4的地方开始,并且占4个字节。故前面的1、2、3会作为留空填充。
第三个变量 int16,规定对齐倍数是2,故需要在下标偏移量为8的地方开始,并且占2个字节。
因此似乎 demo2 所占的空间为 1 + 3 + 4 + 2 = 10 个字节。

这里就需要引入另外一个东西 unsafe.Alignof:

package main

import (
	"fmt"
	"unsafe"
)

type demo1 struct {
	a int8
	b int16
	c int32
}

type demo2 struct {
	a int8
	c int32
	b int16
}

func main() {

	fmt.Println(unsafe.Alignof(demo1{})) // 4
	fmt.Println(unsafe.Alignof(demo2{})) // 4
}

这个东西叫对齐保证,因为这里我们要处理的是 demo1、demo2 这一整个的特定类型,通过对齐保证,我们可以得到一个数字,这个数字告诉我们,这个特定类型实际所占的空间,将会是这个数字的整数倍。
由于demo1 的对齐保证是4,刚好里面的各个属性的空间+对齐空间为8字节,为4的整数倍。
而 demo2 的对齐保证也是4,但里面各个属性的空间+对齐空间为10字节,不是4的整数倍,故最后一个 int16 类型的变量,需要再填充对齐2个字节,使得实际是12个字节,为4的整数倍。

在C语言里也有类似的例子:

#include <stdio.h>

typedef struct t1{
    char x;
    int y;
    double z;
}T1;

typedef struct t2{
    char x;
    double z;
    int y;
}T2;

int main(int argc, char* argv[])
{
    printf("sizeof(T1) = %lu\n", sizeof(T1)); // 16
    printf("sizeof(T2) = %lu\n", sizeof(T2)); // 24

    return 0;
}

有时候如果我们对一些内存空间要求比较高的地方,可以通过分析内存对齐,调整字段的位置,来作为一种优化的手段。但大多数时候应该是没必要去关注这些的吧。

参考

C语言内存对齐详解
Go语言101-内存布局
Go struct 内存对齐
Size and alignment guarantees

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值