一、概述
一直只知道非嵌套结构体的内存分配大小,却没想过如果结构体有嵌套怎么办?星期二参加360的笔试碰到了这样的题目,自然也就答不上来。今天编码试了下,得出了结论,在此记录下。这里主要探讨两点,非嵌套结构体的内存分配问题和结构体嵌套的内存分配问题。第一点是第二点的基础,所以我们先探讨非嵌套结构体的内存分配。
二、非嵌套结构体
非嵌套结构体内存分配大致遵循两个原则:
1.内存对齐:即每个成员变量在内存中存放的起始地址相对于结构体的起始地址必须是这个成员变量类型所占字节的整数倍;之所以这么做是为了保证存取效率。
2.整个结构体的大小必须是最大成员变量类型所占字节数的整数倍。
有了这两个原则,计算结构体的大小就容易了。
假如有如下结构体:
typedef struct MyStruct
{
char a[2];
int b;
double c;
};
a是char类型的数组,char类型是1个字节,所以a就是2个字节。b是int类型,4个字节,但偏移量是2,不是4的整数倍,所以需要填充2个字节,把地址偏移到4。然后再加上b的大小,目前就是8个字节。c是double类型,8个字节,偏移地址是8,刚好是double的整数倍,所以加上c的大小,一共就是16个字节(vs2013环境)。你算对了吗?
再来一个复杂点的:
typedef struct MyStruct
{
char a[2];
int b;
double c;
int *Pint;
char d;
char*Pchar;
};
同样的分析方法,当到Pint的时候,结构体大小是16个字节,偏移是16。Pint是指针,所以无论什么类型都是4个字节,所以+4得20个字节。然后d为char类型,+1得21个字节。到Pchar的时候,由于偏移为21不是4的整数倍,需要填充3个字节到24,在加上Pchar的4个字节得28个字节。由于最大成员变量是c,8个字节,所以结构体大小必须是8的倍数,28还要填充到32个字节才行,所以该结构体最后大小是32个字节,你猜对了吗?
三、嵌套结构体
掌握了非嵌套结构体的大小计算方式,嵌套的也就迎刃而解了,采取同样方式计算即可,只是稍微要复杂点。
看一个例子:
typedef struct MyStruct
{
char a;
char b;
struct str2
{
int d;
} c;
};
a,b都是char类型,无论怎样都是对齐的,所以计算十分简单,一共是2个字节。c是struct类型,需要计算所包含的成员大小,里面只有一个d,int类型,4个字节,因为偏移地址必须是int类型大小的整数倍,所以要先填充2个字节,得4,然后加上d的大小,得8个字节。所以结果是8个字节。你猜对了吗?
再来一个复杂的例子:
typedef struct MyStruct
{
char a;
int b;
char c;
char* d;
double* e;
struct str2
{
int f;
char g;
struct str3
{
char* p;
}n;
} m;
};
a占1个字节,b因为要对齐,所以要填充3个字节,得4,然后加上b的4个字节,得8。c占1个字节,得9。d是指针,无论什么类型都是4个字节,因为要对齐,所以要填充到12,然后加上d,得16。再加上e得20。m是一个结构体,需要计算,f是int类型,加上4得24。再加上g得25。n是一个结构体,p是4个字节,因为要对齐,要先填充到28,然后加上4得32个字节。你猜对了吗?
然后再来看一个比较有迷惑性的例子:
typedef struct MyStruct
{
char a;
char b;
struct str2
{
char c;
char d;
};
};
你觉得应该是多少?4个字节?
先告诉大家结论,是2个字节。请大家仔细看,我只是定义了一个str2的结构体,并没有申明这个结构体的变量!定义是不会分配内存的,所以str2是没有大小的。那么a+b的大小就是2个字节。你猜对了吗?
注:我的测试环境是:操作系统 window 2008 r2,编译器 VS2013
参考文献:struct内存分配分析