C/C++自定义结构体的内存分配大小
1、空结构体的size为1,因为必须保证结构体的每一个实例都在内存中有独一无二的地址;
struct {
static int s1;
}st1;
sizeof(st1) = 1;
2、结构体的静态成员不会对结构体的大小产生影响,因为静态变量的存储位置与结构体的实例地址无关;
struct {
char a;
static int s2;
}st2;
sizeof(st2) = 1;
3、根据变量的对齐规则(“对齐”行为由编译器实施,使得变量的起始地址具有某些特性,例如,4字节的int型变量,其起始地址应该位于4个字节的边界上,即起始地址能够被4整除),
以32位系统为例,变量的对齐规则如下:
char在字节边界上对齐;
short在双字节边界上对齐;
int 和long 在4个字节边界上对齐;
float在4个字节边界上对齐;
double在8个字节边界上对齐。
当结构体中含有不同类型变量时,以结构体中占据最大内存字节数的类型的字节数作为结构体的字节对齐数,
struct {
char a;
int y;
}st3;
sizeof(st3) = 8;
但是,结构体中变量的顺序同时也会影响内存分配大小:
struct {
char b;
char b1;
int y;
double x;
}st;
此时,sizeof(st) = 16;
当改变变量顺序为:
struct {
char b;
double x;
int y;
}st;
此时,sizeof(st) = 32;
下面是转自:http://hi.baidu.com/andey0307/item/f206eeb4517771941846977e 的一篇文章,感觉讲的很详细。
所谓内存对齐,就是内存中对结构体变量的放置参考占内存最大的那个变量《Inside the C++ Object Model》
sizeof(short)<=sizeof(int)<=sizeof(long)
Visual C++6.0的默认对齐系数是8字节!!!你可以查看或者重新设置你想要的对齐系数,Project-->Settings-->C/C++-->Category下拉列表选择Code Generation-->选择Struct member alignment的值(1Byte、2Bytes、8Bytes*、16Bytes,其中8Bytes上带个*表示是编译器默认的对齐方式)
32位处理器下,各种数据类型所占的字节数:
short:2
int: 4
long: 4
char: 1
float: 4
double: 8
class A
{ A(){};
int m1;
int m2;
~A();
};
sizeof(A)=8
------------------------------------------------------------------------------------------------------------------------------------
一、内存对齐的原因
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。二、对齐规则 每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。 规则: 1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。如:对齐系数为2,即#pragma pack(2),char a,sizeof(a)=1<2,按1对齐。 2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。 3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。 Win32平台下的微软C编译器(cl.exe for 80×86)的对齐策略: 1) 结构体变量的首地址是其最长基本类型成员的整数倍; 备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能是该基本数据类型的整倍的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。 2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。 3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。 备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。 4) 结构体内类型相同的连续元素将在连续的空间内,和数组一样。 5) 如果结构体内存在长度大于处理器位数的元素,那么就以处理器的倍数为对齐单位;否则,如果结构体内的元素的长度都小于处理器的倍数的时候,便以结构体里面最长的数据元素为对齐单位。 三、试验 我们通过一系列例子的详细说明来证明这个规则吧! 我试验用的编译器包括GCC 3.4.2和VC6.0的C编译器,平台为Windows XP + Sp2。 我们将用典型的struct对齐来说明。首先我们定义一个struct: #pragma pack(n) /* n = 1, 2, 4, 8, 16 */ struct test_t { int a; char b; short c; char d[6]; }; #pragma pack(n) 首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为: sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 我们的试验过程如下:通过#pragma pack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。 1、1字节对齐(#pragma pack(1)) 输出结果:sizeof(struct test_t) = 13 [两个编译器输出一致] 分析过程: 1) 成员数据对齐 #pragma pack(1) struct test_t { int a; /* int型,长度4 < 1 按1对齐;起始offset=0 0%1=0;存放位置区间[0,3] */ char b; /* char型,长度1 = 1 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */ short c; /* short型,长度2 > 1 按1对齐;起始offset=5 5%1=0;存放位置区间[5,6] */ char d[6]; /* char型,长度1 = 1 按1对齐;起始offset=7 7%1=0;存放位置区间[7,C] */ };/*char d[6]要看成6个char型变量*/ #pragma pack() 成员总大小=13 2) 整体对齐 整体对齐系数 = min((max(int,short,char), 1) = 1 整体大小(size)=(成员总大小) 按能整除(整体对齐系数) 取 = 13 /*13%1=0*/ [注1]
2、2字节对齐(#pragma pack(2))
输出结果:sizeof(struct test_t) = 14 [两个编译器输出一致] 分析过程: 1) 成员数据对齐 #pragma pack(2) struct test_t { int a; /* int型,长度4 > 2 按2对齐;起始offset=0 0%2=0;存放位置区间[0,3] */ char b; /* char型,长度1 < 2 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
/*注意,这里起始地址为5的空间不存数,因为5%2!=0,填充,c从6开始存放*/
short c; /* short型,长度2 = 2 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */ char d[6]; /* char型,长度1 < 2 按1对齐;起始offset=8 8%1=0;存放位置区间[8,D] */ }; #pragma pack() 成员总大小=14 2) 整体对齐 整体对齐系数 = min((max(int,short,char), 2) = 2 整体大小(size)=(成员总大小) 按 能整除(整体对齐系数) 取 = 14 /* 14%2=0 */ [注释2]
3、4字节对齐(#pragma pack(4)) 输出结果:sizeof(struct test_t) = 16 [两个编译器输出一致] 分析过程: 1) 成员数据对齐 #pragma pack(4) struct test_t { int a; /* int型,长度4 = 4 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */ char b; /* char型,长度1 < 4 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */ short c; /*short型, 长度2 < 4 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */ char d[6]; /* char型,长度1 < 4 按1对齐;起始offset=8 8%1=0;存放位置区间[8,D] */ }; #pragma pack() 成员总大小=14 2) 整体对齐 整体对齐系数 = min((max(int,short,char), 4) = 4 整体大小(size)=$(成员总大小) 按能整除 $(整体对齐系数) 取 = 16 /*16%4=0*/ [注释3]
4、8字节对齐(#pragma pack(8)) 输出结果:sizeof(struct test_t) = 16 [两个编译器输出一致] 分析过程: 1) 成员数据对齐 #pragma pack(8) struct test_t { int a; /* int型,长度4 < 8 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */ char b; /* char型,长度1 < 8 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */ short c; /* short型,长度2 < 8 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */ char d; /* char型,长度1 < 8 按1对齐;起始offset=8 8%1=0;存放位置区间[8,D] */ }; #pragma pack() 成员总大小=14 2) 整体对齐 整体对齐系数 = min((max(int,short,char), 8) = 4 整体大小(size)=$(成员总大小) 按 能整除$(整体对齐系数)取 = 16 /*16%4=0*/ 5、16字节对齐(#pragma pack(16)) 输出结果:sizeof(struct test_t) = 16 [两个编译器输出一致] 分析过程: 1) 成员数据对齐 #pragma pack(16) struct test_t { int a; /* int型,长度4 < 16 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */ char b; /* char型,长度1 < 16 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */ short c; /* short型,长度2 < 16 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */ char d; /* char型,长度1 < 16 按1对齐;起始offset=8 8%1=0;存放位置区间[8,D] */ }; #pragma pack() 成员总大小=14 2) 整体对齐 整体对齐系数 = min((max(int,short,char), 16) = 4 整体大小(size)=(成员总大小) 按能整除(整体对齐系数) 取 = 16 /*16%4=0*/ [注释4] 四、结论 8字节和16字节对齐试验证明了“规则”的第3点:“当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果”。另外内存对齐是个很复杂的东西,读者不妨把上述结构体中加个double型成员进去练习一下,上面所说的在有些时候也可能不正确。呵呵^_^
-------------------------------------------------------------------------------------------------------------------
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
class A
{
double d;
int i1;
int i2;
};
sizeof(A)=16
class B
{
int i1;
double d;
int i2;
}
sizeof(B)=24
类A:
+--------+
| d |
+--------+
| i1 | i2 |
+--------+
类B:
+--------+
| i1 | |
+--------+
| d |
+--------+
| i2 | |
+--------+
内存对齐的缘故,
当double d之后最大单元为8个字节,所以会按照8个字节的形式对齐,
所以i1和i2会占用8个字节(后面的为空)
class B
{
int i1;
double d;
int i2;
char c1;
}
sizeof(B)=24