c语言编译结构体padding,失落的C语言结构体封装艺术(2)

4. 填充(Padding)

现在我们来看一个简单变量在内存里的分布的例子。考虑在C模块的最顶上的以下一系列的变量声明:

char*p;

charc;

intx;

如果你不知道任何关于数据对齐的事情,你可能会假设这3个变量在内存里会占据一个连续字节空间。那也就是说,在一个32位机器上,指针的4字节,之后紧接着1字节的字符型,且之后紧接着4字节的整型。在64位机器只在指针是8字节上会有所不同。

这里是实际发生的(在x86或ARM或其他任何有自对齐的处理器类型)。p的存储地址始于一个自对齐的4字节或者8字节边界,取决于机器的字长。这是指针对齐——可能是最严格的情况。

紧跟着的是c的存储地址。但是x的4字节对齐要求,在内存分布上造成了一个间隙;变成了恰似第四个变量插在其中,像这样:

char*p;/* 4 or 8 bytes */

charc;/* 1 byte */

charpad[3];/* 3 bytes */

intx;/* 4 bytes */

pad[3]字符数组表示了一个事实,结构体中有3字节的无用的空间。 老派的术语称之为“slop(水坑)”。

比较如果x是2字节的短整型会发生什么:

char*p;

charc;

shortx;

在那个情况下,实际的内存分布会变成这样:

char*p;/* 4 or 8 bytes */

charc;/* 1 byte */

charpad[1];/* 1 byte */

shortx;/* 2 bytes */

另一方面,如果x是一个在64位机上的长整型

char*p;

charc;

longx;

最终我们会得到:

char*p;/* 8 bytes */

charc;/* 1 byte

char pad[7]; /* 7 bytes */

longx;/* 8 bytes */

如果你已仔细看到这里,现在你可能会想到越短的变量声明先声明的情况:

charc;

char*p;

intx;

如果实际的内存分布写成这样:

charc;

charpad1[M];

char*p;

charpad2[N];

intx;

我们可以说出M和N的值吗?

首先,在这个例子中,N是零。x的地址,紧接在p之后,是保证指针对齐的,肯定比整型对齐更严格的。

M的值不太能预测。如果编译器恰巧把c映射到机器字的最后一个字节,下一个字节(p的第一部分)会成为下一个机器字的第一个字节,并且正常地指针对齐。M为零。

c更可能会被映射到机器字的第一个字节。在那个情况下,M会是以保证p指针对齐而填补的数——在32位机器上是3,64位机器上是7。

如果你想让那些变量占用更少的空间,你可以通过交换原序列中的x和c来达到效果。

char*p;/* 8 bytes */

longx;/* 8 bytes */

charc;      /* 1byte

通常,对于C程序里少数的简单变量,你可以通过调整声明顺序来压缩掉极少几个字节数,不会有显著的节约。但当用于非标量变量(nonscalar variables),尤其是结构体时,这项技术会变得更有趣。

在我们讲到非标量变量之前,让我们讲一下标量数组。在一个有自对齐类型的平台上,字符、短整型、整型、长整型、指针数组没有内部填充。每个成员会自动自对齐到上一个之后(译者注:原文 self-aligned at the end of the next one 似有误)。

在下一章,我们会看到对于结构体数组,一样的规则并不一定正确。

5. 结构体的对齐和填充

总的来说,一个结构体实例会按照它最宽的标量成员对齐。编译器这样做,把它作为最简单的方式来保证所有成员是自对齐,为了快速访问的目的。

而且,在C语言里,结构体的地址与它第一个成员的地址是相同的——没有前置填充。注意:在C++里,看上去像结构体的类可能不遵守这个规则!(遵不遵守依赖于基类和虚拟内存函数如何实现,而且因编译器而不同。)

(当你不能确定此类事情时,ANSI C提供了一个offsetof()宏,能够用来表示出结构体成员的偏移量。)

考虑这个结构体:

structfoo1 {

char*p;

charc;

longx;

};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值