说明:暑假中看书想到的一点东西,因当时没有电脑,无法验证,所以到学校后验证一下才发。
先看一个例子,有三个结构体的定义如下:
struct a
{
bool b1;
bool b2;
int i1;
int i2;
int i3;
};
struct b
{
int i1;
int i2;
int i3;
bool b1;
bool b2;
};
struct c
{
int ii1;
bool bb1;
int ii2;
bool bb2;
int ii3;
};
对于上面定义的两个结构体,考虑下面的两个问题:
1.三个结构体大小的理论值和实际值各是多少?
2.三个结构体所占的内存空间大小(实际所占非理论值)是否相同?为什么?
对于1我们很容易得出结果,三个结构体大小的理论值都是14。因为在32位的系统中int的大小是4byte,bool的大小为1byte,而结构体的大小为各元素所占大小的和,所以理论值为14,至于实际大小我们也可以很容易的得到:
#include<iostream>
using namespace std;
int main()
{
cout<<"a's size:\t"<<sizeof(a)<<endl;
cout<<"b's size:\t"<<sizeof(b)<<endl;
cout<<"c's size:\t"<<sizeof(c)<<endl;
return 0;
}
可以得到大小依次是:16,16,20,这样第一个问题就解决了,第二个问题也解决了一部分。至于问什么这样呢,这与微机系统的内存组织有关。
在微机系统中,存储器是分体的,具体的说16位的系统分为2个存储体,32位的系统分为4存储体。因为存储器是分体的,在存储数据时应考虑数据存储的位置尽量以偶地址(16位)或4的倍数(32位)开始,也就是所谓的“对准”。如果在16位的系统中存放一个字(2byte)的数据时,如果以奇地址开始,那么在读一个字的时候就需要2个时钟周期,而以偶地址开始存储是指需要一个时钟周期。因此所谓的对准也就是用空间换取时间的一种方法。我们可以猜测在32位的编译器中,便量存放的地址应该是以4的地址的倍数开始的,这是可以验证的:
#include<iostream>
using namespace std;
#include<iostream>
using namespace std;
int main()
{
int a;
bool b1,b2;
float c;
double d;
cout<<"variable\t"<<"Address"<<endl;
cout<<"a\t\t"<<&a<<endl;
cout<<"b1\t\t"<<&b1<<endl;
cout<<"b2\t\t"<<&b2<<endl;
cout<<"c\t\t"<<&c<<endl;
cout<<"d\t\t"<<&d<<endl;
return 0;
}
输出结果如下:
variable Address
a 0012FF7C
b1 0012FF78
b2 0012FF74
c 0012FF70
d 0012FF68
下面看一下上面定义的结构体的各成员的地址分配:
int main()
{
a aa;
b bb;
c cc;
cout<<"int size:\t"<<sizeof(int)<<"byte"<<endl;
cout<<"bool size:\t"<<sizeof(bool)<<"byte"<<endl;
cout<<"size a :"<<sizeof(a)<<endl;
cout<<"Address of element in aa:"<<endl;
cout<<"b1's address\t\t"<<&(aa.b1)<<endl;
cout<<"b2's address\t\t"<<&(aa.b2)<<endl;
cout<<"i1's address\t\t"<<&(aa.i1)<<endl;
cout<<"i2's address\t\t"<<&(aa.i2)<<endl;
cout<<"i3's address\t\t"<<&(aa.i3)<<endl;
cout<<"size b :"<<sizeof(b)<<endl;
cout<<"Address of element in bb:"<<endl;
cout<<"i1's address\t\t"<<&(bb.i1)<<endl;
cout<<"i2's address\t\t"<<&(bb.i2)<<endl;
cout<<"i3's address\t\t"<<&(bb.i3)<<endl;
cout<<"b1's address\t\t"<<&(bb.b1)<<endl;
cout<<"b2's address\t\t"<<&(bb.b2)<<endl;
cout<<"size b:"<<sizeof(c)<<endl;
cout<<"Address of element in cc:"<<endl;
cout<<"i1's address\t\t"<<&(cc.i1)<<endl;
cout<<"b1's address\t\t"<<&(cc.b1)<<endl;
cout<<"i2's address\t\t"<<&(cc.i2)<<endl;
cout<<"b2's address\t\t"<<&(cc.b2)<<endl;
cout<<"i3's address\t\t"<<&(cc.i3)<<endl;
return 0;
}
运行结果:
int size: 4byte
bool size: 1byte
size a :16
Address of element in aa:
b1's address 0012FF70
b2's address 0012FF71
i1's address 0012FF74
i2's address 0012FF78
i3's address 0012FF7C
size b :16
Address of element in bb:
i1's address 0012FF60
i2's address 0012FF64
i3's address 0012FF68
b1's address 0012FF6C
b2's address 0012FF6D
size b:20
Address of element in cc:
i1's address 0012FF4C
b1's address 0012FF50
i2's address 0012FF54
b2's address 0012FF58
i3's address 0012FF5C
通过结果就可以说明为什么结构体的大小因元素的位置不一样而大小不一样了。因为编译器在存储是采用了对齐(对准),所以大小有所不同。结构体a中两个布尔变量b2的地址并不是4的倍数,这并不矛盾,因为在这里把结构体看作是像内部类型一样的类型,其内部元素对外是作为一个整体的。
因此,在声明一个结构体时,尽量按一顶的顺序(从大到小或从小到大)来排列各元素,这样就可以减少其所占的空间,不过现在内存空间已不再是着重考虑的问题,因为也可以从便于阅读的方面去排列。
PS. 以上是我关于结构体大小的一点的一点想法,不知正确与否,欢迎批评指正。