结构的存储分配,对齐

4 篇文章 0 订阅

在做笔试题时总是碰到求结构体占用空间大小的问题,由于以前学得不细,在网上找了很久,总找不到想要的内容,没想到在书上找到了很详细的东东,以下内容摘自<<C和指针>>一书:

    编译器按照成员列表的顺序一个接一个地给每个成员分配内存。只有当存储成员时需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。为了说明这一点,考虑下面这个结构:

struct ALIGN{
    char    a;
    int    b;
    char    c;
};

    如果某个机器的整型值长度为4个字节,并且它的起始存储位置必须能够被4整除,那么这一个结构在内存中的存储将如下所示:



    系统禁止编译器在一个结构的起始位置跳过几个字节来满足边界对齐要求,因此所有结构的起始存储位置必须是结构中边界要求最严格的数据类型所要求的位置。因此,成员a(最左边的哪个方框)必须存储于一个能够被4整除的地址。结构的下一个成员是一个整型值,所以它必须跳过3个字节(用灰色显示)到达合适的边界才能存储。在整型值之后是最后一个字符。
如果声明了相同类型的第2个结构变量,它的起始存储位置也必须满足4这个边界,所以第1个结构的后面还要再跳过3个字节才能存储第2个结构。因此,每个结构将占据12个字节的内存空间但实际只使用其中的6个,这个利用率可不是很出色。
    你可以在声明中对结构的成员列表重新排列,让那些对边界要求最严格的成员首先出现,对边界要求最弱的成员最后出现。这种做法可以最大限度地减少因边界对齐而带来的空间损失。例如,下面这个结构
struct ALIGN2{
int    b;
char    a;
char    c;
};
    所包含的成员和前面那个结构一样,但它只占用8个字节的空间,节省了33%.两个字符可以紧挨着存储,所以只有结构最后面需要跳过的两个字节才被浪费。
    提示:
    有时,我们有充分的理由,决定不对结构的成员进行重排以减少因对齐带来的空间损失。例如,我们可能想把相关的结构成员存储在一起,提高程序的可维护性和可读性。但是,如果不存在这样的理由,结构的成员应该根据它们的边界需要进行重排,减少因边界对齐而造成的内存损失。
    当程序将创建几百个甚至几千个结构时,减少内存浪费的要求就比程序的可读性更为急迫。在这种情况下,在声明中增加注释可能避免可读性方面的损失。
    sizeof操作符能够得出一个结构的整体长度,包括因边界对齐而跳过的那些字节。如果你必须确定结构某个成员的实际位置,应该考虑边界对齐因素,可以使用offsetof宏(定义于stddef.h)。
    offsetof( type , member )
    type就是结构的类型,member就是你需要的那个成员名。表达式的结果是一个size_t值,表示这个指定成员开始存储的位置距离结构开始存储的位置偏移几个字节。例如,对前面那个声明而言,
    offsetof(struct ALIGN,b)的返回值是4。

附上C语言中各种类型在内存中所占用字节数:

C语言中各种类型所占字节数
C声明32位机器64位机器
char11
short int22
int44
long48
long long88
char *48
float44
double88














有关struct的空间的计算问题:

struct的空间计算较为复杂,总体上遵循两个原则:

  1)  整体空间是占用空间最大的成员(的类型)所占字节数的整数呗,但在32位Linux+gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。

  2)  数据对齐原则——内存按结构体成员的先后顺序排列对齐,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型的大小的整数倍,如果不够则补齐,依次向后类推,但在Linux+gcc环境下,若某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。

1.  当结构体中含有结构体时,则这两个原则应修改为:

  1)  整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整数倍;但在Linux+gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。

  2)  数据对齐原则——父结构体内存按结构体成员的先后顺序排列,当排到子结构体成员时,其前面已摆放的空间大小必须是该子结构体成员中最大类型大小的整数倍,如果不够则补齐,依次向后类推。但在Linux+gcc环境下,若某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。

2.  含数组的结构体的空间计算  在结构体中,数组是按照单个变量一个一个进行摆放,而不是视为整体。

3.  含位域结构体的空间计算,位域的主要目的是压缩存储。其大致规则如下:

  1)  如果相邻位域字段的类型相同,且其位宽之和小于其类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止。在VS2010环境下,一个int a:4,如果后面不是位域,则占用4个字节,而在Dev-C++以及Linux+gcc编译环境下,采用了更加节省空间的方法,即不论位域为何类型,所占字节数以其实际占用字节数为准,以方便后续结构体成员的摆放,即一个int a:4,如果后面不是位域,仅占一个字节;

  2)  如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3)如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩的方式,Dev-C++与gcc仍采取压缩方式;

  4)  如果位域字段之间穿插着非位域字段,则不进行压缩;

  5)  整个结构体的总大小为最宽基本类型成员大小的整数倍。

4.  使用 "#pragma pack"时结构体空间的计算

 一般伪指令#pragma pack(n),编译器将按照n个字节对齐,n为字节数,其取值为1,2,4,8,16等,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即结构体成员的偏移量应该取二者的最小值;

 使用伪指令#pragma pack(),取消自定义对齐方式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值