大小端模式和結構體中成員變量的內存分佈

一.大端模式和小端模式

計算機系統中,每個地址單元對應一個字節(8bit),一種數據類型可能佔用多個字節,如何安排這種數據類型中的每個字節,哪個字節在低地址,哪個在高地址,以及每個字節中比特序在內存中的位置,這就涉及到大小端模式。一般情況下,字節序和比特序的排列規則是一致的。我們在書寫和計算中數據數據分爲高位和低位,高位在左邊,低位在右邊,例如0x6401中,0x64爲高位,0x01爲低位。

大端模式:低位(字節/比特)放在高地址中,高位(字節/比特)放在低地址中。

小端模式:低位(字節/比特)放在低地址中,高位(字節/比特)放在高地址中。

示例:以整數0x0a0b0c0d爲例來說明該數據在大端和小端系統中內存的位置

大端模式系統:
byte addr        0                     1                 2                3
bit offset   01234567  01234567  01234567  01234567
    binary   00001010  00001011  00001100  00001101
        hex          0a                   0b              0c              0d

小端模式系統:
byte addr          3                  2                1                 0
bit offset   76543210  76543210  76543210  76543210
    binary   00001010  00001011  00001100  00001101
        hex            0a               0b              0c             0d

二、檢測大端模式和小端模式

C語言聯合體中各個成員共享同一段內存空間,一個聯合體變量的內存長度等於各成員中內存最長的長度。而且聯合體和結構體的成員變量的內存都是按照成員變量順序從低地址到高地址分佈。可以利用聯合體的這些特性來檢測系統使大端模式還是小端模式。

檢測代碼如下:

BOOL IsBigEndian()    
{    
    union NUM    
    {    
        int a;    
        char b;    
    }num;    
    num.a = 0x1234;    
    if( num.b == 0x12 )    
    {    
        return TRUE;    
    }    
    return FALSE;    
}  

 三、常見的大小端

一般操作系統都是小端模式;而通訊協議是大端模式;java和平臺無關,默認是大端模式。常見的cpu的大小端:

大端:PowerPC、IBM、Sun

小端:x86

ARM既可以工作在大端模式,也可以工作在小端模式

四、結構體內成員的內存分佈

結構體內成員的內存分佈即和編譯器相關,也和硬件cpu相關。編譯器有時爲了優化CPU訪問內存的效率,在生成結構體內成員起始地址時遵循特定的規則,即結構體成員的“內存對齊”。硬件CPU會影響結構體成員的是採用大端模式還是小端模式來進行內存分配。

五、結構體中普通成員變量的內存分佈規則

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;   //1byte
    double    d;   //8byte
    short     s;   //2byte
    int       i;   //4byte
};
int main(int argc, char*argv[])
{
    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

六、結構體中特殊成員變量的內存分佈規則

1、結構體成員變量爲數組時,是將數組的每個元素當一個成員來分配,並不是將整個數組當成一個成員來對待。其他成員的分配規則按照上述規則。

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

示例1:

struct bit  
{  
    int a:3;
    int b:2;
    int c:3;
};
int main(int argc,char* argv[])    
{    
    bit s;    
    char *c = (char*)&s;    
    *c = 0x98;    
    cout<<s.a<<endl<<s.b<<endl<<s.c<<endl;  
    return 0;    
}

輸出結果:  0   -1   -4  

分析:

hex           0x98

bin           100  11   000

                  c      b       a

內存地址   高   -->     低

cpu爲小端模式,高位比特存儲在高地址中,低位比特存儲在低地址中。而且計算機中存儲的是數的補碼形式。

示例2:

struct   bit  
{  
    char   a:5;
    char   b:4;
    char   c:7;
};
int main(int argc,char* argv[])    
{    
    bit s;    
    int *c = (int*)&s;    
    *c = 0x99E00000;    
    cout<<sizeof(bit)<<endl<<(int)s.a<<endl<<(int)s.b<<endl<<(int)s.c<<endl;  
    return   0;    
}

輸出結果:3 0 0 -32

hex    0x99          0xE0         0x00           0x00

bin   10011001  11100000  00000000  00000000

                                 c                        b                a

內存地址:高          <--------------------               低

 七、通信協議中的數據採用結構體進行定義

網絡通信或者音視頻協議中往往會定義一些數據結構,數據結構中包含多個字段,在C語言中可以通過結構體來定義這些數據。例如下圖所示,爲H.265視頻流在RTP中的負載頭部NALHeader,可以看到LayerId在書寫和計算中爲連續的比特序列,但是在內存中該字段卻是不連續的比特序列。

在x86的cpu架構下,數據存儲爲小端模式,低位字節和比特對應低地址,高位對應高地址。首先NALHeader的第一個字節(高位)存儲在低地址中,第二個字節(低位)存儲在高地址中。而每個字節中高位比特序存儲在低地址中,低位比特序存儲在高地址中,如下圖所示:

可以看到LayerId在書寫和計算中爲連續的數據位,但是存儲位置卻是不連續的,該結構在C語言的結構體中定義爲(結構體中成員變量的存儲位置爲按照成員的先後順序由低地址到高地址):

struct H265NALHeader
{
	unsigned short layerid1 : 1;
	unsigned short naltype : 6;
	unsigned short f_bit : 1;
	unsigned short tid : 3;
	unsigned short layerid2 : 5;
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章