在缺省对齐下,且结构体中只有基本类型时
【1】结构体变量的首地址能够被其最宽基本类型成员的大小所整除
【2】结构体每个成员相对于结构体首地址的偏移量是成员大小的整数倍
【3】结构体的总大小为结构体最宽基本类型成员大小的整数倍
当有嵌套复合成员时,【2】改为:复合成员相对于结构体首地址偏移量是复合成员最宽基本类型大小的整数倍
说明一下对齐的概念。结构体成员的对齐是指成员相对于结构体首地址的偏移量(offset)应该被2^n整除,n通常取0、1、2、3、4,称此时的对齐方式为2^n-byte对齐。例如2-byte对齐的时候,成员的偏移量只能是0、2、4……而4-byte对齐的时候偏移量只能是0、4、8……,缺省为4BYTES对齐。
例子1
struct A
{
int a; 0 - 3
int b; 4 - 7
double c; 8 -15
}; //
sizeof(struct A) =16
例子 2
struct B
{
double c; 0 -7
int a; 8 -11
int b; 12 -15
};
sizeof(struct B) = 16
例子3
struct C
{
int a; 0 - 7
double c; 8 - 15
int b;16 -19
};
sizeof(struct C ) = 24
例子 4
struct D
{
int a[100]; 0 - 399
char c; 400 - 403
};
sizeof(D) = 404
GNU GCC编译器中,遵循的准则有些区别,对齐模数不是像上面所述的那样,根据最宽的基本数据类型来定。在GCC中,对齐模数的准则是:对齐模数最大只能是4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。
看如下例子:
{
char ch ; 0 - 3
double d ;4 -11
} ;
那么在GCC下,sizeof(T)应该等于12个字节。在非GCC下的话,就是 16个字节。
如果结构体中含有位域(bit-field),那么VC中准则又要有所更改:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),GCC都采取压缩方式;
备注:当两字段类型不一样的时候,对于不压缩方式,例如:
{
char c : 2 ;
int i : 4 ;
} ;
依然要满足不含位域结构体内存对齐准则第2条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节;而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第2条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC或者Dev-C++中所占空间应该是4个字节。
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
备注:
结构体
{
char c : 2 ;
double i ;
int c2 : 4 ;
} N3 ;
在GCC下占据的空间为16字节,在VC下占据的空间应该是24个字节。
ps:
- 对齐模数的选择只能是根据基本数据类型,所以对于结构体中嵌套结构体,只能考虑其拆分的基本数据类型。而对于对齐准则中的第2条,确是要将整个结构体看成是一个成员,成员大小按照该结构体根据对齐准则判断所得的大小。
- 类对象在内存中存放的方式和结构体类似,这里就不再说明。需要指出的是,类对象的大小只是包括类中非静态成员变量所占的空间,如果有虚函数,那么再另外增加一个指针所占的空间即可。