1) 基础
在c++中字节对齐主要存在符合类型中:union,struct和class中
先介绍四个概念:
1) 数据类型自身的对齐值:基本数据类型的自身对齐值,等于sizeof(基本数据类型)。
2) 指定对齐值:#pragma pack (value)时的指定对齐值value。
3) 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。
有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍)
#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。
如#pragma pack (1) /*指定按1字节对齐*/
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
2) 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 中a为8字节,很明显u1的大小就为8。
在u2中由于int为4字节,char为1 个字节,所以min(8,max(4,1))=4,以4字节对齐,u2理论为13个字节,要以4字节对齐的话所以为16字节。同理u3 :min(max(1,1),8)=1,sizeof(u3)=13, u4:min(max(1,4),2)=2,sizeof(u4)=14,u5:min(max(1,1),2)=1,sizeof(u5)=13.
3) 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的位置。
最后, 该结构体的自身对齐值为8(其成员中自身对齐值最大的那个d), 指定的对齐值为默认值8, 故该结构体的有效对齐值为min(8, 8) = 8, 结构体本身也要根据自身的有效对齐值圆整, 所以sizeof(T) = 32
再举个例子,看看在默认对齐规则下,各结构体成员的对齐规则:
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字节(上面sizeo(A)的值),最长成员为4字节,需按4字节对齐, 所以前面跳过2个字节, 到24-35字节处 */
char h; //1个字节,分配到36字节处
int i; //4个字节,要对齐4字节,跳过3字节,分配到40-43 字节
}; /* 整个结构体的最大分配成员为8字节,所以结构体后面加5字节填充,被到48字节。
故:sizeof(B)==48;*/