美文网首页
字节对齐

字节对齐

作者: _我和你一样 | 来源:发表于2017-12-01 11:49 被阅读0次

    什么叫做字节对齐?

    对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。

    对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:

    • 数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
    • 联合 :按其包含的长度最大的数据类型对齐。
    • 结构体: 结构体中每个数据类型都要对齐。

    编译器是按照什么样的原则进行对齐的?

    1. 数据类型自身的对齐值:

    对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,类型,其自身对齐值为4,double类型,其对齐值为8,单位字节。

    1. 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
    1. 指定对齐值:#pragma pack (value)时的指定对齐值value。
    1. 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

    有效对齐值N是最终用来决定数据存放地址方式的值,最重要。

    有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。

    第一个数据变量的起始地址就是数据结构的起始地址。

    结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整

    举例说明:

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

    编译器默认情况下会对这个struct作自然边界对齐。

    假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。

    第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.

    第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。

    第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。

    再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。

    故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;

    例子:

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

    第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;

    第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。

    第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
    在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。

    所以sizeof(struct C)=8.

    案例分析

    例子:联合体

    union u
    {
     double a;
     int b;
    }; 
    
    union u2
    {
     char a[13];
     int b;
    }; 
    
    union u3
    {
     char a[13];
     char b;
    }; 
    
    cout<<sizeof(u)<<endl; // 8cout<<sizeof(u2)<<endl; // 16cout<<sizeof(u3)<<endl; // 13
    

    联合体按其包含的长度最大的数据类型对齐。联合体的内存不会为了所有成员安排,而是只取最大的成员的所需内存大小,每次只能使用其中一个成员。

    联合体的内存除了取最大成员内存外,还要保证是所有成员类型size的最小公倍数

    u中 a的数据类型为double,长度为8,有效对齐值为8,b的有效对齐值是4,而选择按8对齐。复核是8的整数倍。

    u2中,a占据13个字节,包含长度最大,但char的有效对齐值是1.int的有效对齐值是4,所以复核13%4 !=0 还要补3个字节。所以共16个子节大小。

    u3中,a占据13个字节,包含长度最大,a和b的有效对齐值都是1 复核13%1===0

    所以以上分别是 8 16 13

    例子:结构体

    struct s1
    {
     char a;
     double b;
     int c;
     char d; 
    }; 
    
    struct s2
    {
     char a;
     char b;
     int c;
     double d;
    }; 
    
    cout<<sizeof(s1)<<endl; // 24cout<<sizeof(s2)<<endl; // 16
    

    s1:

    a的有效对齐值是1,占据一个字节

    b的有效对齐值是8,前面补7个字节,占据8个字节。

    c的有效对齐值是4,

    D的有效对齐值是1

    复核 1+7+8+4+1=21 21%8 == 5;

    所以最后要补3个字节。最终结果是24;

    s2:

    a的有效对齐值是1,占据1个字节

    b的有效对齐值是1,占据1个字节

    c的有效对齐值是4,前面需要补2个字节。占据4个字节。

    d的有效对齐值是8,占据8个字节

    1+1+2+4+8=16

    复核16%8==0

    所以结果是16.

    由此可见,结构体中,相同的数据类型,不同的顺序结果也会不一样。我们通常会把长度多的放在最后。通常会减少内存占用。

    相关文章

      网友评论

          本文标题:字节对齐

          本文链接:https://www.haomeiwen.com/subject/obgibxtx.html