說明:暑假中看書想到的一點東西,因當時沒有電腦,無法驗證,所以到學校後驗證一下才發。
先看一個例子,有三個結構體的定義如下:
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. 以上是我關於結構體大小的一點的一點想法,不知正確與否,歡迎批評指正。