定义一个结构体的一般形式为:
struct 结构体名{
// 类型说明符 成员名;
};
例如下面的结构体:
struct Stu
{
int id;
char sex;
float hight;
};
那么一个这样的结构体变量占多大内存呢?也就是cout<<sizeof(Stu)<<endl;
输出时多少呢?
在了解字节对齐之前会想当然的认为:sizeof(Stu) = sizeof(int)+sizeof(char)+sizeof(float) = 9.
然而并非如此。。。
在系统默认的对齐方式下:每个成员相对于这个结构体变量地址偏移量正好是该成员类型所占字节的整数倍,且最终占用字节数为成员类型中最大占用字节数的整数倍。
在这个例子中,id的偏移量为0(0=4*0),sex的偏移量为4(4=1*4),hight的偏移量为8(8=2*4),此时占用12字节,也同时满足12=3*4.所以sizeof(Stu)=12.
struct A{
int a;
char b;
};
struct B:A{
char c;
int d;
long long e;
};
基类的成员总是在派生类的前面。而且即使有字节对齐,基类对齐后派生类的成员不会占用基类填充的字节,即计算好基类所占字节数后,这些字节只能由基类拥有,不能被派生类的成员占用(即char b后面有3字节的填充,之后才有char c)。
在派生类中成员的分布只需满足每个变量起始字节序号为该类型所占字节数的整数倍且最终大小为占用字节数最大的类型对应的字节数的整数倍。排列如下:
/*
0 4 8 12 16 24
| | | | | |
aaaab---c---ddddeeeeeeee
*/
必须满足:
当然,有时候考虑到其他特殊用途,使用#pragma pack(n)来设定以n字节对齐的方式(n可取2的较小次幂,即1,2,4,8,具体取值范围以及默认值与所使用的编译器有关。笔者所测试的环境下vs/vc默认为8,gcc默认为4)。
n字节对齐就是说变量存放的起始地址的偏移量有两种情况:
如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式;
如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct Test
{
char c1;
double d;
int i;
char c2;
};
#pragma pack(pop)//恢复对齐状态
这时候编译器就会被迫使用我们约定的字节对齐方式,即4字节对齐,因此c1占4字节,d占8字节,i占4字节,c2占4字节,共20字节;
如果我们没有设置字节对齐方式,仍然使用默认对齐的话,这里sizeof(Test) = 24。
《Windows核心编程》里这样说:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数的内存地址为0时,数据是对齐的。
例如:WORD值应该是总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始,数据对齐不是内存结构的一部分,而是CPU结构的一部分。当CPU试图读取的数据没有正确的对齐时,CPU可以执行两种操作之一:
C++类中的数据成员也遵循以上对齐原则,也就是说:
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。