一,默认对齐
//准则
//其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
//1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
//2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
//3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
二,对齐系数
每个特定平台上的编译器都有自己的默认“对齐系数”。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2项推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
以下的代码提供了很方便的理解的对齐的规则,甚至可以查看内存地址。(例子中的注释是以编译器默认对齐规则分析)
例子分析
//#pragma pack(n)//n=1,2,4,8,16 自个测试
#include <iostream>
using namespace std;
int main()
{
struct TestA
{
char a;
double b; //准则2,所以填充a
char c; //准则3,所以填充c
};
struct TestB
{
double a;
char b;
char c; //准则3,所以填充c
};
struct TestC
{
char a;
int b; //准则2,所以填充a
char c;
double d; //准则2,所以填充c
};
struct TestD
{
char a;
char b;
int c; //准则2,所以填充b
double d;
};
struct TestE
{
char a;
char b;
double c;//准则2,所以填充b
};
cout<<"char siz="<<sizeof(char)<<endl;
cout<<"int siz="<<sizeof(int)<<endl;
cout<<"double siz="<<sizeof(double)<<endl;
cout<<"TestA size="<<sizeof(TestA)<<endl;
cout<<"TestB siz="<<sizeof(TestB)<<endl;
cout<<"TestC siz="<<sizeof(TestC)<<endl;
cout<<"TestD siz="<<sizeof(TestD)<<endl;
cout<<"TestE siz="<<sizeof(TestE)<<endl;
//以下为具体测试例子,使用数组计算一个元素的长度
TestA m[2];
TestB n[2];
TestC x[2];
TestD y[2];
TestE z[2];
cout<<"/n---以下为内存地址查看例子---------------/n"<<endl;
cout<<"TestA m[0]总长度:"<<sizeof(m[0])<<endl;
cout<<"m[0].a地址:"<<(int)&m[0].a<<" 长度:"<<((int)&m[0].b)-((int)&m[0].a)<<endl;
cout<<"m[0].b地址:"<<(int)&m[0].b<<" 长度:"<<((int)&m[0].c)-((int)&m[0].b)<<endl;
cout<<"m[0].c地址:"<<(int)&m[0].c<<" 长度:"<<((int)&m[1])-((int)&m[0].c)<<endl;
cout<<"----------------------------------------"<<endl;
cout<<"TestB n[0]总长度:"<<sizeof(n[0])<<endl;
cout<<"n[0].a地址:"<<(int)&n[0].a<<" 长度:"<<((int)&n[0].b)-((int)&n[0].a)<<endl;
cout<<"n[0].b地址:"<<(int)&n[0].b<<" 长度:"<<((int)&n[0].c)-((int)&n[0].b)<<endl;
cout<<"n[0].c地址:"<<(int)&n[0].c<<" 长度:"<<((int)&n[1])-((int)&n[0].c)<<endl;
cout<<"----------------------------------------"<<endl;
cout<<"TestC x[0]总长度:"<<sizeof(x[0])<<endl;
cout<<"x[0].a地址:"<<(int)&x[0].a<<" 长度:"<<((int)&x[0].b)-((int)&x[0].a)<<endl;
cout<<"x[0].b地址:"<<(int)&x[0].b<<" 长度:"<<((int)&x[0].c)-((int)&x[0].b)<<endl;
cout<<"x[0].c地址:"<<(int)&x[0].c<<" 长度:"<<((int)&x[0].d)-((int)&x[0].c)<<endl;
cout<<"x[0].d地址:"<<(int)&x[0].d<<" 长度:"<<((int)&x[1])-((int)&x[0].d)<<endl;
cout<<"----------------------------------------"<<endl;
cout<<"TestD y[0]总长度:"<<sizeof(y[0])<<endl;
cout<<"y[0].a地址:"<<(int)&y[0].a<<" 长度:"<<((int)&y[0].b)-((int)&y[0].a)<<endl;
cout<<"y[0].b地址:"<<(int)&y[0].b<<" 长度:"<<((int)&y[0].c)-((int)&y[0].b)<<endl;
cout<<"y[0].c地址:"<<(int)&y[0].c<<" 长度:"<<((int)&y[0].d)-((int)&y[0].c)<<endl;
cout<<"y[0].d地址:"<<(int)&y[0].d<<" 长度:"<<((int)&y[1])-((int)&y[0].d)<<endl;
cout<<"----------------------------------------"<<endl;
cout<<"TestE z[0]总长度:"<<sizeof(z[0])<<endl;
cout<<"z[0].a地址:"<<(int)&z[0].a<<" 长度:"<<((int)&z[0].b)-((int)&z[0].a)<<endl;
cout<<"z[0].b地址:"<<(int)&z[0].b<<" 长度:"<<((int)&z[0].c)-((int)&z[0].b)<<endl;
cout<<"z[0].c地址:"<<(int)&z[0].c<<" 长度:"<<((int)&z[1])-((int)&z[0].c)<<endl;
cout<<"----------------------------------------"<<endl;
return 0;
}
运行效果如下:
char size=1 ---以下为内存地址查看例子--------------- TestA m[0]总长度:24 |
以上在VC 2010/2008 ,mingw32 3.4.5下测试通过!
再将代码#pragma pack(n),去除注释n=1,2,4,8,16 自个测试看看!相信会有深入了解