用的最多的两个方法
1:#pragma pack(push,n)
#pragma pack(pop)
其中上一句是设置字节对齐的参数n,下一句是取消该字节对齐设置。
2.__declspec(align(n))
这一句也是设置字节对齐不过这一句最常用的方式是在结构体内部指定某一特定的成员的起始偏移地址。
测试代码如下:
#pragma pack(show) //编译时以warning的形式显示当前的字节对齐方式,VS2010默认的对齐参数为8.
#pragma pack(push,2) //设置新的字节对齐参数为2
struct Test1
{
char c ;
__declspec(align(16)) //设置(int i)也就是是 i 这个成员变量的起始偏移地址为16个字节的整数倍。
int i ;
double d;
};
#pragma pack(show) //此时显示的字节对齐参数为2.
#pragma pack(pop) //取消字节对齐参数为2的设置,还原到原来的字节对齐参数为8的状态。
struct Test2
{
char c ;
int i ;
double d ;
};
#pragma pack(show) //显示字节对齐参数为8.
下面是显示结构体变量的地址的代码:
Test1 test1;
std::cout<<"****************"<<sizeof(Test1)<<"*************\n";
std::cout<<"test1.c:"<<&test1<<"\n";
std::cout<<"test1.i:"<<&test1.i<<"\n";
std::cout<<"test1.d:"<<&test1.d<<"\n";
运行结果如下:
接下来我们来分析为什么是这个输出结果:
首先由于#pragma pack(push,2) 的原因,Test1结构体的字节对齐参数是2个字节。
test1.c不用说是起始地址。
test1.i受两个条件的影响:
条件1:#pragma pack(push,2)
由于i是int类型,所以i的地址应该是4的整数倍(这个是默认的也是最基本的地址)。由于有了#pragma pack(push,2),取4和2的最小值为2,所以test1.i的地址为2的整数倍。这里最终结果取2.
条件2: __declspec(align(16))
这个条件会产生两个影响(1:test1.i的地址为16字节的整数倍。 2:Test1 这整个结构体的大小为16字节的整数倍)。最终test1.i的地址为16.
接下来就综合条件1和条件2(也就是#pragma pack(push,2)和__declspec(align(16)) 的影响)取他们的最小公倍数。2和16的最小公倍数为16,所以test1.i的最终地址为16. (0017F710 - 0017F700 = 16)
test1.d只受#pragma pack(push,2)的影响,不受__declspec(align(16)) 的影响。所以默认的地址为8的倍数(因为是double类型),取2和8的最小值,所以最终test1.d的地址是2的整数倍。16(i的地址) + 4 (i的大小)= 20(刚好为2的整数倍),所以最终test1.d的地址为20.(0017F714 - 0017F700 = 20)
接下来是test1这整个结构体变量的大小:
默认的大小是结构体内最大的成员变量的大小的整数倍(也就是(double类型)8的整数倍)。
受#pragma pack(push,2)的影响,结构体大小是2的整数倍。取2和8的最小值为2.
受__declspec(align(16)) 的影响,结构体大小是16的整数倍。取上一步得到的值2和16的最小公倍数16.
所以最终结构体的大小是16的整数倍。
20(d的地址) + 8(d的大小) = 28(再取大于28的最小的16的整数倍) = 32
所以最终test1结构体变量的大小为32个字节。
方法总结:
1.先算默认的。
2.再算#pragma pack(push,n)的。
3.取默认和n的最小值,假设为Min.
4.如果有__declspec(align(#)) ,再取#和Min的最小公倍数,这就是计算结果了。
不管是计算结构体内部的成员变量地址还是计算整个结构体的大小,都是采用上面的计算顺序和方法。
(简单点说就是先取默认和#pragma pack(push,n)的最小值,再取这个最小值和__declspec(align(#))的最小公倍数)
注意:如果__declspec(align(#)) 出现的位置是结构体的外面,那么结构体内部的变量的地址不受外部的__declspec(align(#))的影响,只是整个结构体的大小会受到影响。如果结构体作为其他结构体的成员变量,那么结构体的地址也会受到外部__declspec(align(#))的影响。示例如下:
__declspec(align(#)) struct Test2
{
char c ;
int i ;
double d ; //c,i,d的地址都不会受到__declspec(align(#))影响,只不过Test2整个结构体的大小会受到影响。
};
struct Test3
{
int i;
Test2 test2; //test2的地址会受到__declspec(align(#))影响。
}
注意:网上有其他的一些计算方法,说是__declspec(align(#))的优先级要高于#pragma pack(push,n)。这个目前我还没有弄懂。不过经过我的测试,我上面总结的方法也是可行的,可以计算出正确的成员变量地址和结构体的大小。
下面是其他的测试代码和输出结果:
#pragma pack(push,16)
struct Test1
{
char c ;
__declspec(align(2))
int i ;
double d;
};
#pragma pack(show)
#pragma pack(pop)
#pragma pack(push,2)
struct Test1
{
char c ;
__declspec(align(1))
int i ;
double d;
};
#pragma pack(show)
#pragma pack(pop)