开始前先来几个题
struct A
{
int a;
int b;
}; sizeof(struct A)=?
struct B
{
char a;
char b;
int c;
}; sizeof(struct B)=?
struct C
{
char a;
int b;
double c;
int d;
}; sizeof(struct C)=?
和老师交流时,老师给我出了几道c/c++细节方面的问题,其中遇到了一道关于结构体在内存存储的问题,老师先后变换了这几种种情况,最后一种情况还是没有回答上。回去考虑了下突然想起来怎么回事。
对于结构体存储,这涉及了计算机组成原理中我们学的字节对齐,这也看出来计算机组成原理是何等的重要,所以对于操作系统,计算机组成原理这些课程一定要好好学习。
关于字节对齐:
字节对齐有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
准则
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
各类型的偏移量
在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)
类型 对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char 偏移量必须为sizeof(char)即1的倍数
int 偏移量必须为sizeof(int)即4的倍数
float 偏移量必须为sizeof(float)即4的倍数
double 偏移量必须为sizeof(double)即8的倍数
Short 偏移量必须为sizeof(short)即2的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
当然,也可以人为的使用#pragma pack(n) /* n = 1, 2, 4, 8, 16 */ 来人为的设置对齐大小。
偏移量计算
同时在stddef.h里,提供了一个宏定义用来判断结构体内某个成员的偏移量。
具体用法是:
size_t offsetof( structName, memberName );
第一个参数是结构体的名字,第二个参数是结构体成员的名字。该宏返回结构体structName中成员memberName的偏移量。偏移量是size_t类型的。
一个最简单的代码示例
#include<iostream.h>
#include<stddef.h>
struct A
{
char a;
double b;
};
int main()
{
cout<<offsetof(struct A,b)<<endl;
return 0;
}
学会不同课程之间的联通掌握,把握好他们之间的联系。