具体见:http://www.cnblogs.com/0201zcr/p/4789332.html?utm_source=tuicool&utm_medium=referral
-
sizeof求解结构体大小的问题
如何给结构体变量分配空间由编译器决定,下面的情况针对的是Linux下的GCC。在Windows下的VC平台也是这样,至于其他平台,可能会有不同的处理。
-
C/C++中不同数据类型所占用的内存大小(单位都是字节)
- 32位 64位 char 1 1 int 4 大多数4,少数8 short 2 2 long 4 8 float 4 4 double 8 8 指针 4 8 - 结构体(struct):比较复杂,对齐问题需要考虑清楚
- 联合(union) :取联合的所有成员中最长的作为联合的长度
- 枚举(enum) :取决于数据类型
-
sizeof计算单层结构体大小
运算符sizeof可以计算出给定类型的大小,对于32位系统来说,sizeof(char) = 1; sizeof(int) = 4。基本数据类型的大小很好计算,我们来看一下如何计算构造数据类型的大小。
C语言中的构造数据类型有三种:数组、结构体和共用体。具体如下:-
数组
是相同类型的元素的集合,只要会计算单个元素的大小,整个数组所占空间等于基础元素大小乘上元素的个数。 -
结构体
结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。看下面这样的一个结构体:struct stu1 { int i; char c; int j; };
用sizeof求该结构体的大小,发现值为12。int占4个字节,char占1个字节,结果应该是9个字节才对啊,为什么呢?
先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。
然而,在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。
再来看另外一个例子:
struct stu2 { int k; short t; };
成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。
-
sizeof计算嵌套的结构体大小
对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:(1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
(2)结构体(最终的)大小必须是所有成员(任意成员)大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。
(3)被展开的结构体的规则同单结构体sizeof计算规则。-
看下面的例子:
struct stu5 { short i; struct { char c; int j; } ss; int k; }
结构体stu5的成员ss.c的偏移量应该是4,而不是2。整个结构体大小应该是16,即4+(4+4)+4,第二个4是
char c
的扩展。 -
下面的代码测试原则2:
struct stu5 { char i; struct { char c; int j; } ss; char a; char b; char d; char e; char f; }
结构体ss单独计算占用空间为8,而stu5的sizeof则是20(由17扩展至20,而不是扩展到24),不是8的整数倍,这说明在计算sizeof(stu5)时,将嵌套的结构体ss展开了,这样stu5中最大的成员为ss.j,占用4个字节,20为4的整数倍。如果将ss当做一个整体,结果应该是24了。
-
另一个常见的例子:
结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开
struct ss { float f; char p; int adf[3]; };
其值为20。float占4个字节,到char p时偏移量为4,p占一个字节,到int adf[3]时偏移量为5,扩展为int的整数倍,而非int adf[3]的整数倍,这样偏移量变为8,而不是12。结果是8+12=20,是最大成员float或int的大小的整数倍。
-
-
-
-
【补充】另外一个涉及数据类型以及内存存储的问题
内存中的数据并非保存在任意地址。处理器通常按照和其字大小相同的块读取内存数据;那么考虑到效率因素,编译器会按照块大小的整数倍对内存中的实体进行地址对齐。因此在 32 位的处理器上,一个 4 字节整型数据肯定存放在内存地址能被4整除的地方。
下面,假设系统中整型数据和指针大小均为 4 字节。
现在有一个指向整型的指针。如上所述,整型数据可以存放在内存地址 0×1000 或者 0×1004 或者 0×1008,但是决不会存放在 0×1001 或者0×1002 或者 0×1003 或者其他不能被4整除的任何地址。所有是4整数倍的二进制数都是以 00 结尾。实际上,这意味着对于所有指向整型的指针,它的最后两位总是 0。
因为有这样的情况发生,我们倒是可以利用
4整数倍的二进制数都是以00结尾
这样的特性,将一些临时数据存储在地址的低两位的位置,只是记得地址取数据的时候一定要恢复地址的原始值。相应的数据隐藏的处理可见:http://blog.jobbole.com/83213/?url_type=39&object_type=webpage&pos=1