采用字节对齐的原因:
各种平台在内存处理上有很大不同,存取方式也差别很大。很多平台为了提高自己数据的存取效率,都会充内存上对数据进行一些调整,如数据存放进行对齐,以便提高自己生的数据存取速度,提高程序执行效率。
字节对齐规则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
实例说明:
假如我们定义了一个结构体
- typedef struct tagHeader{
- int nLen;
- char type;
- short one;
- }Header,*pHeader;
按照我们正常的思路 sizeof(Header) = 4(int)+1(char)+2(short ) 也就是7个字节 但是事实上呢,你如果运行一下你会发现
sizeof(Header) = 8 为什么会这样呢?原因就是上面所说的字节对齐造成的,那么现在我们就按照上面所说的字节对齐的规则来,看一下他到底是 怎么进行对齐的
1.首先我们来 找出 在这个结构体里面最大的基本成员变量空间是int 他占4个字节.
2.然后我们按照结构体内部变量定义顺序(既是 int-char-short)来分析
(1)int nlen 相对于结构体的起始地址0, 他是4的整数倍,满足字节对齐的条件2
(2)第二个变量char type 相对于结构体的其实地址是4,他是1的整数倍,显然也满足字节对齐规则的条件2
(3)第三个变量 short one 相对于结构体的起始地址是5(0+4+1) 显然这不是2的整数倍,不满足对齐规则条件2,于是这时候就要采取字节对齐了,系统为了让他满足字节对齐,会自动填充一个字节使其的其实地址变为6 ,这样便可以整除 2 了,所以one的起始地址就变为了6 这时就满足对齐规则条件2.
3.最后我们来看看 结构体总共占的大小 4(int)+1(char)+1(系统自动填充)+2(short) =8 而8恰好是4(该结构体最大的基本类型是int)的倍数 满足条件3,因此该结构体真正的大小就为8.
内存结构如下图一
下面我们再来看一个例子,我们仅仅把结构体内容调换一下
- typedef struct tagHeader{
- char type;
- int nLen;
- short one;
- }Header,*pHeader;
1.首先找出 在这个结构体里面最大的 基本成员变量是int 他占4个字节.
2.然后我们按照结构体内部变量定义顺序(既是 char-int-short)来分析
(1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2
(2)第二个变量 int nlen相对于结构体的起始地址是1,他的起始地址不是4的整数倍,不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。
(3)第三个变量 short one 相对于结构体的起始地址是8(0+1+3+4) 是2的整数倍.满足字节对齐的条件2。
3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+4(int)+2(short) =10 而10不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是12.
内存结够如下图二
最后我们再来看一个复合的结构体
- typedef struct tagHeader{
- char type;
- int nLen;
- short one;
- }Header,*pHeader;
- typedef struct tagTest{
- char type;
- Header head;
- }TEST,*LPTEST;
仍然按照我们自己的步骤来 分析
1.首先找出 在这个结构体里面最大的基本类型成员变量是int 他占4个字节(Header 不是基本类型).
2.然后我们按照结构体内部变量定义顺序(既是 char-header)来分析
(1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2
(2)第二个变量 Header相对于结构体的起始地址是1,他的起始地址不是4的整数倍(这里是最大基本类型成员变量是int),不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。
(3)按照我们分析的前面分析的方法分析结构体的head的内存情况。
3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+10(结构体) =14 而14不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是16.
内存结构如下图三
内存结构图解
以上便是字节对齐的情况。
字节对齐是提高了数据存取熟读,提高程序的效率,但是有些时候我们的程序不需要字节对齐。比如我们要将一个结构体发送出去,发送到网络中的另一台计算机上,但是这台计算机采用的平台和我们开发的平台却不一样。假若我让系统字节对齐的话,但接受方由于平台和我们不一样,这样很可能就导致对方解析数据不正确,所以有时候不能使用字节对齐。可那么假如我不想让系统字节对齐,或者说我们想自己指定对齐字节是多少,那我们该怎么办?
C++便可以这样 采用#pragma pack 命令, 当你定义结构体时这样定义。
- #pragma pack(push,1) //表示已1字节对齐
- typedef struct tagHeader{
- char type;
- int nLen;
- short one;
- }Header,*pHeader;
- #pragma pack(pop)
- #pragma pack(push)
- #pragma pack(1)//表示以一字节对齐
- typedef struct tagHeader{
- char type;
- int nLen;
- short one;
- }Header,*pHeader;
- #pragma pack(pop)
#pragma pack(push)和#pragam pack(pop) 之间的结构体就采用一字节对齐,如果你想以其他字节对齐,自己修改#pragma中的数字便是,如以4字节对齐 就改为
#pragma pack(4) 这样便可以了。
对于单个结构可以使用GUN中的__attirbute__((aligned(N)))。