不同硬件平台下,C/C++中的构体填充与对齐分析

在C语言中我们定义的结构体的大小常常不是所有成员变量大小的总和(大于或者等于),这是因为编译器“暗中”执行了“对齐”的动作,使得访问更有效率,当然也会牺牲一定的空间。

对于8位机来说,内部总线的宽度是8位的,所以无论是存取8位的数据,还是16或32位数据,从什么地址开始存取都是无关紧要的,因为所有的访问最终都会拆分成单个字节访问,因此在8位机上,结构体的大小就等于所有成员大小的累加和。

对于32位机来说,内部总线的宽度是32位的,所以很多时候访问如果按照4字节对齐,那么是最有效率的一种访问方式。有的平台甚至是不允许在非对齐的地址上存取数据。举个例子,访问一个int的变量,如果放在可以被4整除的地址上,那么只需要一个CLK就可以存取,如果放在非对齐的地址上(例如奇数地址),则需要2个CLK分别存取高低两部分再进行拼凑,显然效率低下。

因为8位机上基本没有对齐的问题,所以我们下面的讨论主要针对32位机。


首先来看看对齐的概念:

如果一段长度为n字节的数据所存放的地址m能被n整除, 那么d就是对齐的. 可见, 数据的对齐和自身长度和地址有关. 在32位机器上, 并不是要求每个数据以4字节对齐. 对于short类型的数据来说, 将他们存放在偶数地址上, 就是对齐的. 如果是奇数地址则是非对齐. 对于int类型的数据来说, 必须要将他们存放在能被4整除的地址上, 才是对齐的. 而对于char类型的数据. 任意一个地址都可以被看作是对齐的.

 

我们在设计结构的体的时候必须要考虑到4种对齐. 

第一,结构体成员自身对齐(因为存在单独访问结构体成员的情况. 所以这些成员必须满足上面所说的数据对齐).

约束:结构体每个成员相对于结构体首地址的偏移量(offset)必须是成员自身大小的整数倍. 否则需要在成员之间加入填充字节(internal padding).

第二,结构体自身对齐(因为当存在结构体数组的情况,所以整个结构体也必须满足对齐).

约束:结构体的总大小为结构体最宽基本类型成员大小的整数倍, 如有需要编译器会在最末一个成员之后加上填充字节(trailing padding).

第三,指定对齐(默认对齐),跟编译器还有平台有关,在没有使用编译器关键字如#pragma pack等指定之前,使用是默认对齐。在32位的ARM平台上往往默认对齐是4,也可以利用关键字修改为2,或者1。

第四,有效对齐,会影响成员和结构体最终的对齐方式,有效对齐是自身对齐指定对齐之间较小的那一个

下面在32位机,默认4字节对齐的环境下的看几个例子.

    struct student   
    {   
        u8 num;  
        u8 name;  
        u16 score;   
    };  
    sizeof(student) == 4  
      
    struct student  
    {   
        u8 num;  
        u8 name;  
        u16 sex;  
        u16 score;  
    };  
    sizeof(student) == 6  
      
    struct student   
    {    
        u8 num;   
        u8 name;   
        u8 sex;            
        u16 score;          // 为了保证成员自身是对齐的,需要在之前插入一个字节的padding.  
    };  
    sizeof(student) == 6  
      
    struct student   
    {   
        u8 num;   
        u8 name;   
        u32 sex;            // 为了保证成员自身是对齐的, 需要在之前插入两个字节的padding.  
    };  
    sizeof(student) == 8  
      
    struct student         
    {    
        u8 num;  
        u8 name;  
        u32 sex;            // 为了保证成员自身是对齐的, 需要在之前插入两个字节的padding.  
        u16 score;          // 为了保证结构自身是对齐的(按最大的元素u32), 需要在后面加上两个字节的padding  
    };  
    sizeof(student) == 12  
      
    struct student   
    {    
        u8 num;   
        u8 name;   
        u32 sex;            // 为了保证成员自身是对齐的, 需要在之前插入两个字节的padding.  
        u16 score;  
        u8 id;              // 为了保证结构自身是对齐的(按最大的元素u32), 需要在后面加上一个字节的padding  
    };  
    sizeof(student) == 12

以上的例子所有自身对齐都小于或等于指定对齐(默认对齐)4,因此都是以自身对齐作为有效对齐来考虑的,如果通过编译器将指定对齐修改位2字节对齐,大小和padding会有所不同

#pragma pack (2)
struct student
{
    u8 num;
    u32 sex;        // 成员自身对齐是4,指定对齐是2,所以有效对齐也是2,只需插入一个字节padding.
    u16 score;      // 结构体自身对齐是4,指定对齐是2,所以有效对齐也是2,8%2==0,已经是对齐的。
};
#pragma pack()
sizeof (student) == 8
可见结构体的大小会受到结构体成员顺序的影响. 如果对结构体占的空间比较敏感. 应该按大小顺序来排列成员(先小后大,或先大后小).

最典型的例子:

  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值