字节对齐

一  什么是字节对齐

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

二  对齐的原因和作用

     不同硬件平台对存储空间的处理上存在很大的不同。某些平台对特定类型的数据只能从特定地址开始存取,而不允许其在内存中任意存放。例如Motorola 68000 处理器不允许16位的字存放在奇地址,否则会触发异常,因此在这种架构下编程必须保证字节对齐。
     但最常见的情况是,如果不按照平台要求对数据存放进行对齐,会带来存取效率上的损失。比如32位的Intel处理器通过总线访问(包括读和写)内存数据。每个总线周期从偶地址开始访问32位内存数据,内存数据以字节为单位存放。如果一个32位的数据没有存放在4字节整除的内存地址处,那么处理器就需要2个总线周期对其进行访问,显然访问效率下降很多。
     因此,通过合理的内存对齐可以提高访问效率。为使CPU能够对数据进行快速访问,数据的起始地址应具有“对齐”特性。比如4字节数据的起始地址应位于4字节边界上,即起始地址能够被4整除。

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

主要基于Intel X86架构介绍结构体对齐和栈内存对齐,位域本质上为结构体类型。
对于Intel X86平台,每次分配内存应该是从4的整数倍地址开始分配,无论是对结构体变量还是简单类型的变量

三 用例

 1.先来看个简单用例()32位,X86处理器,GCC编译器):

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

 已知32位机器上各数据类型的长度为:char为1字节、short为2字节、int为4字节、long为4字节、float为4字节、double为8字节。那么上面两个结构体大小如何呢?
     结果是:sizeof(strcut A)值为8;sizeof(struct B)的值却是12。 
     结构体A中包含一个4字节的int数据,一个1字节char数据和一个2字节short数据;B也一样。按理说A和B大小应该都是7字节。之所以出现上述结果,就是因为编译器要对数据成员在空间上进行对齐。

对于

2 对齐准则
     先来看四个重要的基本概念:
     1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。
     2) 结构体或类的自身对齐值:其成员中自身对齐值最大的那个值。
     3) 指定对齐值:#pragma pack (value)时的指定对齐值value。
     4) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。


     基于上面这些值,就可以方便地讨论具体数据结构的成员和其自身的对齐方式。
     其中,有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N表示“对齐在N上”,即该数据的“存放起始地址%N=0”。而数据结构中的数据变量都是按定义的先后顺序存放。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐存放,结构体本身也要根据自身的有效对齐值圆整(即结构体成员变量占用总长度为结构体有效对齐值的整数倍)

2.用例二

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned int uin32_t;
#pragma pack(4)
typedef  struct _tagA
{
char a;
char b;
char c;
int  d;
char e[3];
//long f;
}A;

typedef  struct _tagB
{
    _tagB()
    {
        memset(this, 0x00, sizeof(_tagB));
    }
    unsigned short a:2;
    unsigned short b:12;
    unsigned short c:4; 
    char* p;
}B;

#pragma pack()


int main()
{
    B B1;
    B1.a = 0x7;
    B1.b = 0x010;
    B1.c = 0;
    char c = 'a';
    B1.p = &c;
    char s[10] = {0};
    unsigned int usTmp = *((unsigned int*)(&B1));
    printf("size: %d\n", sizeof(B));
    printf("usTmp: %x, B: %s\n", usTmp, s);    
    B1.c = 10;
    usTmp = *((unsigned int*)(&B1));   
    printf("usTmp: %x, B: %s\n", usTmp, s);    
    return 0;
}

这里注意另外一个知识点。

1.先定义的位域值存储在低位,后定义的存储在高位。

2.这里有效对齐值=min{自身对齐值,当前指定的pack值},即有效对齐值为4,

  则:B1.c = 10 = 1010 存储在了第15到18位


如不指定#pragma pack(4)4字节对齐:


总长度为16,位域按short的2字节对齐了,则:B1.c = 10 = 1010 存储在了第17到20位

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值