结构体的大小
1字节是8位,一般32位机子上各个数据类型所占的存储空间如下:
char:8位
short:16位
int:32位
long:32位
unsigned long:32位
long long:64位
float:32位
double:64位
long double:64位
指针:32位
64位机器上各个数据类型所占的存储空间如下:
char:8位
short:16位
int:32位
long:64位
unsigned long:64位
long long:64位
float:32位
double:64位
long double:128位
指针:64位
结构体的大小
因为结构体有时候需要字节对齐。一般而言,struct的sizeof是所有成员字节对齐后长度相加,而union的sizeof是取最大的成员长度。
在默认情况下,编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变默认的对界条件:
(1) 使用伪指令#pragma pack(n),C编译器将按照n个字节对齐。
(2) 使用伪指令#pragma pack(),取消自定义字节对齐方式。
字节对齐的细节和编译器实现相关,但一般而言,满足以下3个准则:
(1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
(2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节。
(3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
需要注意的是,基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型。如果一个结构体中包含另外一个结构体成员,那么此时最宽基本类型成员不是该结构体成员,而是取基本类型的最宽值。
sizeof()计算结构体的大小
简要说明:结构体成员按照定义时的顺序依次存储在连续的内存空间,但是结构体的大小并不是简单的把所有成员大小相加,而是遵循一定的规则,需要考虑到系统在存储结构体变量时的地址对齐问题。
一、没有成员的结构体占用的空间是多少个字节?
答案是:1个字节。
这就是实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类或空结构体(C++中结构体也可看为类)隐含的加一个字节,这样空类或空结构体在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。
二、首先介绍一个相关的概念——偏移量
struct stru
{
int a; //start address is 0
char b; //start address is 4
int c; //start address is 8
};
偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。比如上面的结构体,第一个成员a的偏移量为0。第二个成员b的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员c的偏移量是第二个成员的偏移量应该是加上第二个成员的大小(4+1)。
三、在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。
例子1:
struct stru1
{
int a; //start address is 0
char b; //start address is 4
int c; //start address is 8
};
PS:用sizeof求该结构体的大小,发现值为12。int占4个字节,char占1个字节,结果应该是9个字节才对啊,为什么呢?这个例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。
例子2:
struct stru2
{
int i; //start address is 0
short m; //start address is 4
};
PS:成员i的偏移量为0;成员m的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员m大小的整数倍。因此,编译器会在成员m后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。
例子3、4:
struct stru3
{
char i; //start address is 0
int m; //start address is 4
char n; //start address is 8
};
struct stru4
{
char i; //start address is 0
char n; //start address is 1
int m; //start address is 4
};
虽然结构体stru3和stru4中成员都一样,但sizeof(struct stru3)的值为12而sizeof(struct stru4)的值为8。
由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。
四、对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:
(1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
(2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。
例子1:
struct stru5
{
short i;
struct
{
char c;
int j;
} tt;
int k;
};
结构体stru5的成员tt.c的偏移量应该是4,而不是2。整个结构体大小应该是16。
例子2:
struct stru6
{
char i;
struct
{
char c;
int j;
} tt;
char a;
char b;
char d;
char e;
int f;
};
结构体tt单独计算占用空间为8,而stru6的sizeof则是20,不是8的整数倍,这说明在计算sizeof(stru6)时,将嵌套的结构体ss展开了,这样stu6中最大的成员为tt.j,占用4个字节,20为4的整数倍。如果将tt当做一个整体,结果应该是24了。