写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧.
/******************************分割线
如果体系结构是不对齐的,A中的成员将会一个挨一个存储,从而sizeof(a)为11。显然对齐更浪费了空间。那么为什么要使用对齐呢?
体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。比如说读写时.............此处省略50万字
***********************************************************/
上面是你随便 google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则:(在没有#pragma pack宏的情况下)
1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。
2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐.
等你看完此3条原则,2分钟已经过去,抓紧时间,实战3分钟:
typedef struct bb
{
int id; //[0]....[3]
double weight; //[8].....[15] 原则1
float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3
}BB;
typedef struct aa
{
char name[2]; //[0],[1]
int id; //[4]...[7] 原则1
double score; //[8]....[15]
short grade; //[16],[17]
BB b; //[24]......[47] 原则2
}AA;
int main()
{
AA a;
cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
return 0;
}
结果是
48 24
ok,上面的全看明白了,内存对齐基本过关.
再讲讲#pragma pack().
在代码前加一句#pragma pack(1),你会很高兴的发现,上面的代码输出为
32 16
bb是4+8+4=16,aa是2+4+8+2+16=32;
这不是理想中的没有内存对齐的世界吗.没错,#pragma pack(1),告诉编译器,所有的对齐都按照1的整数倍对齐,换句话说就是没有对齐规则.
明白了不?
那#pragma pack(2)的结果又是多少呢?对不起,5分钟到了,自己去测试吧.
以上转自:http://blog.csdn.net/hairetz/archive/2009/04/16/4084088.aspx
**************************分割线*****************************************
VC 对结构的存储的特殊处理确实提高CPU 存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
VC 中提供了#pragma pack(push,n) 来设定变量以n 字节对齐方式。n 字节对齐 就是说变量存放的起始地址的偏移量 有两种情况:第一、如果n 大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n 小于该变量的类型所占用的字节数,那么偏移量为n 的倍数,不用满足默认的对齐方式。结构的总大小 也有个约束条件,分下面两种情况:如果n 大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n 的倍数。下面举例说明其用法。
#pragma pack(push,4) // 设定为4 字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)// 恢复对齐状态
以上结构的大小为16 ,下面分析其存储情况,首先为m1 分配空间,其偏移量为0 ,满足我们自己设定的对齐方式(4 字节对齐),m1 占用1 个字节。接着开始为m4 分配空间,这时其偏移量为1 ,需要补足3 个字节,这样使偏移量满足为n=4 的倍数(因为sizeof(double) 大于n ),m4 占用8 个字节。接着为m3 分配空间,这时其偏移量为12 ,满足为4 的倍数,m3 占用4 个字节。这时已经为所有成员变量分配了空间,共分配了16 个字节,满足为n 的倍数。如果把上面的#pragma pack(4) 改为#pragma pack(16) ,那么我们可以得到结构的大小为24 。(请读者自己分析)
关于pragma宏的具体解释可以参考:http://baike.baidu.com/view/1451188.html?fromTaglist
**************************分割线*****************************************
可以使用这样的而一个宏来获得struct成员的偏移量
#define offset(type,member) (size_t)(&(((type)*)0)->(member))
**************************分割线*****************************************
sizeof 用法总结
在VC 中,sizeof 有着许多的用法,而且很容易引起一些错误。下面根据sizeof 后面的参数对sizeof 的用法做个总结。
A . 参数为数据类型或者为一般变量。例如sizeof(int),sizeof(long) 等等。这种情况要注意的是不同系统系统或者不同编译器得到的结果可能是不同的。例如int 类型在16 位系统中占2 个字节,在32 位系统中占4 个字节。
B . 参数为数组或指针。下面举例说明.
C . 参数为结构或类。Sizeof 应用在类和结构的处理情况是相同的。但有两点需要注意,第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关,静态成员存储在静态存储区中。没有成员变量的结构或类的大小为1 ,因为必须保证结构或类的每一个实例在内存中都有唯一的地址。下面举例说明,
Class Test{int a;static double c};//sizeof(Test)=4.
Test *s;//sizeof(s)=4,s 为一个指针。
Class test1{ };//sizeof(test1)=1;
D . 参数为其他。下面举例说明。
int func(char s[5]);
{
cout<<sizeof(s);// 这里将输出4 ,本来s 为一个数组,但由于做为函
// 数的参数在传递的时候系统处理为一个指针,所
// 以sizeof(s) 实际上为求指针的大小。
return 1;
}
sizeof(func( “1234 ”))=4// 因为func 的返回类型为int ,所以相当于 求sizeof(int).
E .观察下面程序的输出
struct struct1{double k[5];char c;};
struct struct2{char cat;struct1 cow;char dog;};
struct struct3{char cat;double k[5];char c;char dog;};
int main(int argc, char* argv[])
{
cout<<sizeof(struct2)<<endl;
cout<<sizeof(struct3)<<endl;
}
F,观察下面程序的输出