字节对齐

来源:互联网 发布:淘宝拍卖翡翠手镯不错 编辑:程序博客网 时间:2024/06/06 15:40


c++中字节对齐主要存在符合类型中:unionstructclass

先介绍四个概念:

1)数据类型自身的对齐值:基本数据类型的自身对齐值,等于sizeof(基本数据类型)

2)指定对齐值#pragma pack (value)时的指定对齐值value

3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

  有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是 数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整 数倍)

#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。

#pragma pack (1)  /*指定按2字节对齐*/

#pragma pack () /*取消指定对齐,恢复缺省对齐*/

 

1 union

Union就是取整个成员中大的内存块作为整个共用体的内存大小

对齐方式为成员中对齐方式最大的成员的对齐方式, 对界取编译器对界方式与自身大小中较小的一个

测试程序如下:

#include <iostream>

#include <stdio.h>

using std::cout;

using std::endl;

union u1

{

double a;

int b;

};

union u2

{

char a[13];

int b;

}; 

union u3

{

char a[13];

char b;

}; 

#pragma pack(2)//对界方式2字节¨²

union u4

{

char a[13];

int b;

}; 

union u5

{

char a[13];

char b;

};

 

#pragma pack()  //恢复默认对界方式

void main()

{

cout<<sizeof(u1)<<endl;

cout<<sizeof(u2)<<endl;

cout<<sizeof(u3)<<endl;

cout<<sizeof(u4)<<endl;

cout<<sizeof(u5)<<endl;

system("pause");

}

测试结果如下:

8

16

13

14

13

结论:由于默认是8字节对齐, 在u1 a8字节,很明显u1的大小就为8

u2中由于int4字节,char个字节,所以min(8,max(4,1))=4,以4字节对齐,u2理论为13个字节,要以4字节对齐的话所以为16字节。同理 u3 minmax(1,1),8=1sizeofu3=13, u4:  minmax(1,4),2=2sizeofu4=14u5: minmax(1,1),2=1sizeofu5=13.

2   struct/class:都是对整个成员所占内存大小的求和。大小仅和成员变量的类型有关还和定义的先后顺序有关

举个例子,一个结构体如下:

typedef struct T

    char c; //本身长度1字节 

    __int64 d;  //本身长度8字节

    int e;  //本身长度4字节

    short f;  //本身长度2字节

    char g;  //本身长度1字节

    short h;  //本身长度2字节

}; 

    假设定义了一个结构体变量C,在内存中分配到了0x00的位置,显然:

    对于成员C.c  无论如何,也是一次寄存器读入,所以先占一个字节。

    对于成员C.d  是个64位的变量,如果紧跟着C.c存储,则读入寄存器至少需要3次,为了实现最少的2次读入,至少需要以4字节对齐;同时对于8字节的原始变量,为了在寻址单位上统一,则需要按8字节对齐,所以,应该分配到0x08-0xF的位置。

    对于成员C.e  是个32位的变量,自然只需满足分配起始为整数个32位即可,所以分配至0x10-0x13

    对于成员C.f  是个16位的变量,直接分配在0x14-0x16上,这样,反正只需一次读入寄存器后加工,边界也与16位对齐。

    对于成员C.g  是个8位的变量,本身也得一次读入寄存器后加工,同时对于1个字节的变量,存储在任何字节开始都是对齐,所以,分配到0x17的位置。

    对于成员C.h  是个16位的变量,为了保证与16位边界对齐,所以,分配到0x18-0x1A的位置。

    分配图如下(还不正确,耐心读下去)

    结构体C的占用空间到h结束就可以了吗?我们找个示例:如果定义一个结构体数组 CA[2],按变量分配的原则,这2个结构体应该是在内存中连续存储的,分配应该如下图:
 

    分析一下上图,明显可知,CA[1]的很多成员都不再对齐了,究其原因,是结构体的开始边界不对齐。

    那结构体的开始偏移满足什么条件才可以使其成员全部对齐呢。想一想就明白了:很简单,保证结构体长度是原始成员最长分配的整数倍即可。
    上述结构体应该按最长的.d成员对齐,即与8字节对齐,这样正确的分配图如下:

    当然结构体T的长度:sizeof(T)==0x20;

     再举个例子,看看在默认对齐规则下,各结构体成员的对齐规则:

typedef struct A 

    char c;  //1个字节

    int d;  //4个字节,要与4字节对齐,所以分配至第4个字节处

    short e;  //2个字节, 上述两个成员过后,本身就是与2对齐的,所以之前无填充

 }; //整个结构体,最长的成员为4个字节,需要总长度与4字节对齐,所以, sizeof(A)==12 

typedef struct B 

    char c;  //1个字节

    __int64 d;  //8个字节,位置要与8字节对齐,所以分配到第8个字节处

    int e;  //4个字节,成员d结束于15字节,紧跟的16字节对齐于4字节,所以分配到16-19

    short f;  //2个字节,成员e结束于19字节,紧跟的20字节对齐于2字节,所以分配到20-21

    A g;  //结构体长为12字节,最长成员为4字节,需按4字节对齐,所以前面跳过2个字节

//24-35字节处

    char h;   //1个字节,分配到36字节处

    int i;   //4个字节,要对齐4字节,跳过3字节,分配到40-43 字节

}; //整个结构体的最大分配成员为8字节,所以结构体后面加5字节填充,被到48字节。故:

//sizeof(B)==48;

    具体的分配图如下:

 上述全部测试代码如下:

#include "stdio.h" 

typedef struct A 

    char c; 

    int d; 

    short e; 

 

}; 

typedef struct B 

    char c; 

    __int64 d; 

    int e; 

    short f; 

    A g; 

    char h; 

    int i; 

}; 

typedef struct C 

    char c; 

    __int64 d; 

    int e; 

    short f; 

    char g; 

    short h; 

}; 

typedef struct D 

    char a; 

    short b; 

    char c; 

}; 

int main() 

 

    B *b=new B; 

    void *s[32]; 

    s[0]=b; 

    s[1]=&b->c; 

    s[2]=&b->d; 

    s[3]=&b->e; 

    s[4]=&b->f; 

    s[5]=&b->g; 

    s[6]=&b->h; 

    s[7]=&b->g.c; 

    s[8]=&b->g.d; 

    s[9]=&b->g.e; 

    s[10]=&b->i; 

    b->c= 0x11; 

    b->d= 0x2222222222222222; 

    b->e= 0x33333333; 

    b->f=0x4444; 

    b->g.c=0x50; 

    b->g.d=0x51515151; 

    b->g.e=0x5252; 

    b->h=0x66; 

    int i1=sizeof(A); 

    int i2=sizeof(B); 

    int i3=sizeof(C); 

    int i4=sizeof(D); 

    printf("i1:%d\ni2:%d\ni3:%d\ni4:%d\n",i1,i2,i3,i4);//12 48 32 6 

运行时的内存情况如下图:


ps:部分材料来自网络,本人整理而得

原创粉丝点击