简析结构体的存储分配
结构与数组类似,但是两者之间存在着很大的差别。数组是通过下标进行访问,而结构是通过其成员的名字进行访问的。其次结构体内部成员更加灵活,那么结构体在内存中的存储是怎么分配的?
来看下面代码:
#include <stdio.h> struct S1 { int a; char c; }; struct S2 { char c; int a; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); system("pause"); return 0; }
程序运行结果为:
关于这段代码中的结构体内存分配,看下图:
我们可以通过
宏offsetof来验证一下,结构体成员在内存中的分配是否如上图所示.
#define offsetof(struct_t,member) ((size_t)(char
*)&((struct_t *)0)->member)
offsetof返回的是结构体成员在结构体内存中的偏移量。
其函数原型在MSDN中为下:
看代码如下:
#include <stdio.h>
#include<stddef.h>
struct S1
{
int a;
char c;
};
struct S2
{
char c;
int a;
};
int main()
{
printf("%d ", offsetof(struct S1, a));
printf("%d\n", offsetof(struct S1, c));
printf("%d ", offsetof(struct S2, a));
printf("%d\n", offsetof(struct S2, c));
system("pause");
return 0;
}
程序运行结果为:
这个即说明:
1. 结构体的第一个成员在与结构体变量偏移量为0的地址处。
2. 结构体在内存中存储时会发生对齐,其对齐规则为:其他成员变量要对齐到对齐数的整数倍的地址处。
//对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认对齐数的值为8,linux中的默认值为4)
再看下面代码:
#include <stdio.h>
struct S
{
double a;
char c;
float d;
short e[2];
};
int main()
{
printf("%d\n", sizeof(struct S));
system("pause");
return 0;
}
此程序运行结果为多少呢?
根据上面的规则来分析,a占据前8个字节,c占据第9个字节,d根据对齐占据第13~16这4个字节,e[2]占据17~20这四个字节。那么结构体总大小应为20。然而结构却不是如此。
此段代码运行的结果为:24。也就是说在存储完e[2]后,结构体又多占据了四个字节的空间。这是什么情况?
因为:
结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
而上面代码中结构体最大对齐数为8,而最后算的为20,不是8的倍数,所以再多开辟4个字节的空间,即24个字节的空间。
了解了这个规则后,来看这个代码:
#include <stdio.h>
struct S
{
int a;
char c;
float d;
short e;
};
int main()
{
printf("%d\n", sizeof(struct S));
system("pause");
return 0;
}
可以很容易的分析有:a占据前四个字节,c占据第5个字节,d占据第9~12个字节,e占据第13~14个字节,又因为此结构体最大对齐数为4,所以结构体大小要为四的倍数,即要再开辟两个自己的空间,因此,此结构体空间大小为:16。
程序运行结果:
那么如果结构体嵌套了,其内存又是如何分配的?
看代码:
#include <stdio.h>
struct S1
{
char c;
int a;
};
struct S2
{
char d;
struct S1 s1;
int e;
double f;
short h[2];
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
system("pause");
return 0;
}
此段代码运行结果如下:
分析,其在内存中的存储如下:
则分析有:
如果存在嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
如果结构体中包含了指针的话,其内存分配如下:
#include <stdio.h>
struct S1
{
char c;
int a;
int* p;
char* q;
};
int main()
{
printf("%d\n", sizeof(struct S1));
system("pause");
return 0;
}
程序运行结果为:
分析,c占据第一个字节,a占据第5~8个字节,而无论是int指针还是char指针都占据了4个字节。
所以:
结构体中包含指针,无论指针是何种类型,其占据内存均为4个字节(64位机器上8个字节)。
关于结构体内存对齐的原因:
1、平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
以上即为对结构体存储分配的简单解析,如有不足,还请大家指出!