C语言 字节对齐问题 详解

 

一  什么是字节对齐

     现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。

二  对齐的原因和作用

    2.1、硬件平台

     不同硬件平台对存储空间的处理上存在很大的不同。某些平台对特定类型的数据只能从特定地址开始存取,而不允许其在内存中任意存放。例如Motorola 68000 处理器不允许16位的字存放在奇地址,否则会触发异常,因此在这种架构下编程必须保证字节对齐。

    2.2、效率问题

      如果不按照平台要求对数据存放进行对齐,会带来存取效率上的损失。比如32位的Intel处理器通过总线访问(包括读和写)内存数据。每个总线周期从偶地址开始访问32位内存数据,内存数据以字节为单位存放。如果一个32位的数据没有存放在4字节整除的内存地址处,那么处理器就需要2个总线周期对其进行访问,显然访问效率下降很多。

    2.3、节省空间

      合理利用字节对齐还可以有效地节省存储空间,后面详细解释原因。

    2.4、注意事项

      合理利用字节对齐还可以有效地节省存储空间。但要注意,在32位机中使用1字节或2字节对齐,反而会降低变量访问速度。因此需要考虑处理器类型。还应考虑编译器的类型。在VC/C++和GNU GCC中都是默认是4字节对齐。

三、对齐方式

基础知识:

32位,X86处理器,GCC编译器各数据类型的长度为:

char为1字节、short为2字节、int为4字节、long为4字节、float为4字节、double为8字节。

    3.1、对齐规则

      结构体字节对齐的细节和具体编译器实现相关,但一般而言满足几个规则:

     1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

     2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

     3) 结构体的总大小为结构体最宽基本类型成员大小(有pack时,看规则5,最小的有效对齐值)的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。

     4) 指定对齐值:#pragma pack (value)时的指定对齐值value。

     5) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

      规则详解:

      1)编译器给结构体开辟空间时,首先找到结构体里最宽的基本数据类型,然后开辟的内存地址能够被该基本数据类型整除,才可以作为结构体首地址,最宽的基本数据类型就是自身对齐值。

      2)在开辟结构体下一个成员之前,要检查预开辟的空间地址的首地址是否是本成员大小的整数倍,若是,则存放本成员;反之,则在本成员和上一个成员之间条虫一定的字节,已达到整数倍的要求。

      3)结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条( 结构体的总大小为结构体最宽基本类型成员大小的整数倍),否则就必须在最后填充几个字节以达到本条要求。

      4)指定对齐值,不会根据结构体成员规定对齐值。

      5)实际上是对规则1、4的情况冲突的解决方法;同样的规则3,也需要在规则5的基础上。

 案例1:

struct A{
    int    a;
    char   b;
    short  c;
};

       1)假设A的地址是0x0000,则a的地址是0x0000~0x0003;

       2)b的地址是0x0004;

       3)c的地址往后排是0x0005,但0x0005%2 != 0,根据规则2,填充一个字节,c应该是0x0006~0x0007;

       4)则sizeof(strcut A) = 8,同时满足规则3,8%4 = 0(默认的是四字节对齐)。

案例二:

struct B{
    char   b;
    int    a;
    short  c;
};

       1)假设B的地址是0x0000,则b的地址是0x0000;

       2)a的地址往后排是0x0001,但0x0001%2 != 0,根据规则2,填充三个字节,a应该是0x0004~0x0007;

       3)c的地址往后排是0x0008,0x0008%2 = 0,c应该是0x0008~0x0009;

       4)现在sizeof(strcut A) = 10,但不满足规则3,10%4 != 0(默认的是四字节对齐),需要往后填充两个字节;

       5)则sizeof(strcut A) = 12。

  3.2、对齐方式修改 (pack)

   1、伪指令   

  • 使用伪指令#pragma pack(n):C编译器将按照n个字节对齐;
  • 使用伪指令#pragma pack(): 取消自定义字节对齐方式。

案例一:

#pragma pack(2)  //指定按2字节对齐
struct C{
    char  b;
    int   a;
    short c;
};
#pragma pack()   //取消指定对齐,恢复缺省对齐

        1)变量b自身对齐值为1,指定对齐值为2,所以有效对齐值为1。

        2)假设C从0x0000开始,则b存放在0x0000,符合0x0000%1= 0;

        3)变量a自身对齐值为4,指定对齐值为2,所以有效对齐值为2,本来应存放在0x0001~0x0004四个字节中,但不符合规则2,因此需要填充1字节, 然后存放在0x0002~0x0005四个字节中,符合0x0002%2=0。

        4)变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006~0x0007中,符合 0x0006%2=0。

        5)所以从0x0000到0x00007共八字节存放的是C的变量。C的自身对齐值为4,所以其有效对齐值为2。又8%2=0,C只占用0x0000~0x0007的八个字节。所以sizeof(struct C) = 8。

案例二:

#pragma pack(8)
struct D{
    char  b;
    short a;
    char  c;
};
#pragma pack()

          1)#pragma pack(8),但依然按照两字节对齐,原因:对齐到的字节数 = min{当前指定的pack值,最大成员大小}。

          2)所以sizeof(struct D)的值为6。分析同上。

   2、GCC特有语法:

  • __attribute((aligned (n))): 让所作用的结构成员对齐在n字节自然边界上。如果结构体中有成员的长度大于n,则按照最大成员的长度来对齐。
  • __attribute__ ((packed)): 取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

    GNU GCC编译器中按1字节对齐可写为以下形式:

#define GNUC_PACKED __attribute__((packed))
struct C{
    char  b;
    int   a;
    short c;
}GNUC_PACKED;

       sizeof(struct C)的值为7。

四、使用字节对齐优化内存

        我们在不同编译平台或处理器上,字节对齐会造成消息结构长度的变化。编译器为了使字节对齐可能会对消息结构体进行填充,不同编译平台可能填充为不同的形式,大大增加处理器间数据通信的风险。

       为了尽量避免这种情况,同时也优化内存单元,提出下面几种方案:

 1)为了提高内存访问效率,采用四字节对齐方式;

 2)对于不同处理器之间的结构,减少编译平台带来的变化,保证内存访问效率,对结构体成员采用字节填充的方法,自己对成员进行四字节对齐;

 3)兼顾成员之间的关系、数据访问效率和空间利用率。顺序安排原则是:四字节的放在最前面,四字节放完之后,两字节的紧跟,两字节放完之后,一字节紧跟,最后放填充字节。

案例:

typedef struct MSG{
    long  ParaA;
    long  ParaB;
    short ParaC;
    char  ParaD;
    char  NO;   //填充字节
}T_MSG;

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值