1. 程式人生 > >結構體(或者聯合體)變數的成員在記憶體裡是如何分佈的(轉)

結構體(或者聯合體)變數的成員在記憶體裡是如何分佈的(轉)

1、概述

結構體變數的成員在記憶體裡是如何分佈的、成員先後順序始怎樣的、成員之間是連續的還是分散的、還是其他的什麼形式?這些問題既和軟體相關又和硬體相關。所謂軟體相關主要是指和具體的程式語言的編譯器的特性相關,編譯器為了優化CPU訪問記憶體的效率,在生成結構體成員的起始地址時遵循著某種特定的規則,這就是所謂的“結構體成員對齊”;所謂硬體相關主要是指CPU的“位元組序”問題,也就是大於一個位元組型別的資料,如int型別、short型別等,在記憶體中的存放順序,即單個位元組與高低地址的對應關係。

2、分佈規則

(1)為了提高CPU訪問記憶體的效率,程式語言的編譯器在做變數的儲存分配時進行了優化處理,處理的原則是:對於n位元組元素,它的首地址能被n整除,這種原則稱為“對齊”。

(2)結構體(聯合體)的成員所佔記憶體地址依次增高,第一個成員位於低地址處,最後一個成員位於高地址處,但是結構體成員的記憶體並不是連續的,編譯器會對成員做上述對齊處理。

(3)通常編譯器可以設定一個對齊引數n,結構體中每個成員實際對齊引數n根據n=min(sizeof(成員型別),n)來得到。結構體成員的記憶體偏移地址x,滿足條件x%N=0;

(4)結構體所有成員的對齊引數N的最大值稱為結構體的對齊引數。整個結構的長度必須是結構體對齊引數的最小整數倍,不夠補0.

簡單來說:

1)結構體的其實地址為X,X%sizeof(結構體)=0;

2)結構體每個成員相對於其實地址偏移地址y,y%n=0;

3)結構體長度等於結構體對齊引數的最小整數倍

注:編譯器對齊引數可以通過指令控制,例如#param pack(2)兩位元組對齊。vs2010IDE還可以通過“專案屬性”->“C/C++”->“程式碼生成”->“結構體成員對齊”來設定。編譯器預設對齊引數為8個位元組。

例:

struct A 

{

  char c;

  double  d;

  short  s;

  int  i;

};

int main(int argc, char * argv[])

{

  struct A strua;

  printf("len:%d\n",sizeof(A));

  printf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i);

return 0;

}

 

輸出結果為:

len:24

1506156,1506164,1506172,1506176

3、特殊成員

(1)結構體成員為陣列時,是將陣列的每個元素當一個成員來分配地址,並不是將整個陣列當成一個成員來對待。其他成員的分配原則按照上述規則。

(2)當結構體成員是位段時,儲存是按其型別分配空間的,如int型的位段就分配4個位元組的儲存單元。相鄰的同類型的兩個位段,如果該型別的長度夠用,就將兩位段連續存放,共用儲存單元,如果不夠用,就另起一個該型別長度的儲存空間。相鄰的不同型別的兩個位段,分別為這兩個位段分配他們所屬型別長度的儲存空間。其他成員的分配規則按照上述規則。

1)例1:

struct bit
{
int a:3;
int b:2;
int c:3;
};

int main(void){
bit s;
char *c = (char*)&s;
*c = 0x99;
cout<<s.a<<endl<<s.b<<endl<<s.c<<endl;
return 0;
}

輸出結果:

1

-1

-4

分析:

hex  0x99

bin  100 11 001

CPU為小端模式,高位位元儲存在高位地址,低位位元儲存在低位地址中,因為計算中儲存的是數的補碼形式,所以結果為1,-1,-4.

2)例2:

struct bit
{
char a:5;
char b:4;
char c:7;
};

int main(void){
bit s;
int *c = (int*)&s;
*c = 0x99E00000;
cout<<sizeof(bit)<<endl<<(int)s.a<<endl<<(int)s.b<<endl<<(int)s.c<<endl;
system("pause");
return 0;
}

輸出結果:

3

0

0

-32

分析:

hex 0x99  0xE0  0x00  0x00

bin 10011001  11100000  00000000  00000000

c  b  a

地址 高——>低