内存大小和系统有关,如32位系统和64位系统,同一数据类型,内存分配大小不一
假设这台机器是sizeof(char)=1 sizeof(int)=4 sizeof(double)=8
枚举类型只为最宽的数据分配内存,在不同的时候,用的是同一块内存;在默认情况下,规定各成员变量的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(32位系统),变量存放的起始地址相对于结构的起始地址的偏移量。
1、Char偏移量必须为sizeof(char)即1的倍数
2、Short偏移量必须为sizeof(short)即2的倍数
3、int偏移量必须为sizeof(int)即4的倍数
4、float偏移量必须为sizeof(float)即4的倍数
5、double偏移量比较特殊,sizeof(double)=8,但是偏移量是4
各成员变量在存放的时候根据在结构体中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置。空缺的字节会自动填充,同时为了确保结构的大小为结构的字节边界数(即结构体中占偏移量最大的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
结构体的内存分配依赖于结构成员的类型:
1、struct s{
int i;
char c;
}
第一个成员偏移量为0,是4的倍数,所以可以为其分配四个字节的空间
第二个成员偏移量为4,是1的倍数,所以可以为其分配1个字节的空间,现在结构体的大小应该是5(4+1)
但是结构体的总大小为结构体最宽基本数据成员大小的整数倍,如有需要编译器会再最末一个成员之后加上填充字节,所以编译器会在后面填充3个字节,因此结构体的实际 大小sizeof(struct s)=8
再看结构体struct s{
int i;
char c;
double d;
}
由于成员c相对于相对于结构体的偏移量为4,现在结构体大小是5(4+1),那么成员d的偏移量就应该是5,不是8的倍数,会在成员c之后填充3个字节,以使d的偏移量达到8的 整数倍,然后d分配8个字节,因此现在结构体的大小是16,是最大类型字节数的整数倍。
struct s{
int i;
char c;
double d;
char* e;
}的大小为20.注意double大小是8,偏移量是4
2、struct s1{
char a;
struct s b;
char c;
}的大小为28=1+3+20+1+3
字节对齐的细节和编译器实现相关,满足三个准则:
1)、结构体变量的首地址能够被其最大偏移量类型成员的偏移量整除
2)、结构体每个成员相对于结构体首地址的偏移量都是成员偏移量的整数倍,如有需要编译器会在成员之间加上填充字节
3)、结构体的总大小为结构体最大偏移量类型成员的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
3、下面做一些补充:
1、什么是字节对齐,为什么要对齐
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际情况是在访问特定类型变量的时候经常在特定的内 存定制访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上又很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没 有进行对齐的变量的时候会发生错误,那么在这种架构下变成必须保证字节对齐。但是最常见的如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。
2、编译器是按照什么样的原则进行对齐的?
先了解4个重要的基本概念:
1、数据类型自身的对齐值
对char数据类型,其自身对齐值为1,对于short型为2,对int, float, double类型,其自身对齐值为4,单位为字节
2、结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值
3、指定对齐值:#pragma pack(value)时的指定对齐值value
4、数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小得那个值