一、为什么结构体计算这么乱?
答案是字节对齐,计算机存储系统中以Byte为单位存储数据,不同数据类型所占的空间不同,如:整型(int)数据占4个字节,字符型(char)数据占一个字 节,
短整型(short)数据占两个字节,等等。计算机为了快速的读写数据,默认情况下将数据存放在某个地址的起始位置,如:整型数据(int)默认存储 在地址能被
4整除的起始位置,字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这样字节对齐有助于加快
计算机的取数速度,否则就得多花指令周期了。
二、字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2. 结构体每个成员相对于结构体首地址的偏移量都是当前成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
说明:1、基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型;
2、对于复合数据类型,如结构体嵌套结构体,那么基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型;
3、我认为计算结构体大小的时候,主要用到准则2和准则3,对于准则1是编译器自动完成的,不需要过多理会。
4、C++中类的可以看做是特殊的结构体,所以类的sizeof的计算和结构体是一样的。
三、下面拿具体的程序来详细说明:
类型一:
// sizeof(结构体).cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
using namespace std;
struct A
{
char a;
int b;
};
struct B
{
char a;
A a1;
char b;
};
int _tmain(int argc, _TCHAR* argv[])
{
cout<<sizeof(A)<<endl<<sizeof(B);
return 0;
}
上述程序的输出结果是8和16.
下面结合前面给出的准则具体分析一下。
对于结构体A其基本成员类型有char和int两种,最宽的是int占用4个字节,那么根据准则1,编译器会自动为结果体A分配一个能被4整除的首地址,A的第一个成员char的首地址就是结构体A的首地址,即偏移量为0,接下来,下一个成员变量int首地址,如果不做处理的话,应该是相对于A的偏移量是1,这就不满足准则2了,所以编译器开始在char的后面填充3个字节,使得int相对于A的偏移量是4,来满足准则2,然后结构体的总大小就是1(char)+3(填充)+4(int)=8,同时满足了准则3,不用再填充了,所以sizeof(A)=8。
对于结构体B,其中包括了一个复合类型,查看基本类型的时候,要将其中的结构体A拆分成char和int两种类型来看,所以结构体B中的基本数据类型是char,char,int,char,最宽的数据类型是int,编译器会自动为B分配一个能被4整除的首地址,B的第一个成员char的首地址就是结构体B的首地址,即偏移量为0,接下来,下一个成员变量A的首地址,如果不做处理的话,应该是相对于B的偏移量是1,这就不满足准则2了,所以编译器开始在char的后面填充3个字节,使得成员A相对于所在结构体B的地址偏移量是4,来满足准则2,这时加上B的长度8,B中最后一个成员char相对于B来说地址偏移量是8,能满足准则2,所以不需要在A的后面填充字节,这时B的总长度是1(char)+3(填充)+8(A)+1(char)=13,不满足准则3,所以还需要在最后一个char后面再添加3个字节,最后得到B的总大小是1(char)+3(填充)+8(A)+1(char)+3(填充)=16,所以sizeof(B)=16。
看了对于结构体B的解析有点懵,反反复复看了好几遍才弄懂上面遵循的三个原则什么意思,
首先第一条解释(根据上面的代码示例):char a的长度应该是拆开结构体A的基本类型和B中基本类型最宽基本类型成员的整数倍,也就是4.
第二:A是结构体,有自己的对齐字节数,sizeof(A)=8.
第三:最后的char b,字节长度是1,所以总长度是:4+8+1=13,但是要遵循是最宽基本类型成员的整数倍,所以要再填充3个字节,就是16.
类型二:结构体中嵌套一个结构体和变量。
struct stu5
{
char i; //1
struct //2
{
char c;
int j;
} ss;
char a; //3
char b; //4
char d; //5
char e; //6
char f; //7
}
解析:stu5结构体在内存的分布如下图,经过在vs中测试,正确。
类型三、含有数组结构体
struct ss
{
float f;
char p;
int adf[3];
};
cout<<sizeof(ss)<<endl;
解析:sizeof(ss)大小是20,结构体ss中三种基本类型:float/char/int,sizeof(f)=4,sizeof(p)=1,sizeof(adf)=4*3=12;所以总的大小是17,根据上面的原则3,是最宽字节的整数倍,所以是20字节。
类型四、嵌套两个结构体和成员变量
struct stu1
{
int i;
char c;
int j;
};
struct ss
{
float f;
char p;
int adf[3];
};
struct MyStruct
{
char a;
stu1 b;
ss c;
int d;
char e;
};
cout << sizeof(MyStruct) << endl;
解析:char a大小还是4,查看资料发现结构体第一个变量的首地址是该结构体最宽字节的整数倍,stu1和ss还是根据结构体的内部对齐原理,所以sizeof(stu1)=44.