1. 程式人生 > >C/C++ 結構體字節對齊

C/C++ 結構體字節對齊

sizeof 情況下 logs 微軟 變量的存儲 add cout 數據 syntax

在用sizeof運算符求算某結構體所占空間時,並不是簡單地將結構體中所有元素各自占的空間相加,這裏涉及到內存字節對齊的問題。從理論上講,對於任何 變量的訪問都可以從任何地址開始訪問,但是事實上不是如此,實際上訪問特定類型的變量只能在特定的地址訪問,這就需要各個變量在空間上按一定的規則排列, 而不是簡單地順序排列,這就是內存對齊。

內存對齊的原因:

1)某些平臺只能在特定的地址處訪問特定類型的數據;

2)提高存取數據的速度。比如有的平臺每次都是從偶地址處讀取數據,對於一個int型的變量,若從偶地址單元處存放,則只需一個讀取周期即可讀取該變量;但是若從奇地址單元處存放,則需要2個讀取周期讀取該變量。

win32平臺下的微軟C編譯器對齊策略:

1)結構體變量的首地址能夠被其最寬數據類型成員的大小整除。編譯器在為結構體變量開辟空間時,首先找到結構體中最寬的數據類型,然後尋找內存地址能被該數據類型大小整除的位置,這個位置作為結構體變量的首地址。而將最寬數據類型的大小作為對齊標準。

2)結構體每個成員相對結構體首地址的偏移量(offset)都是每個成員本身大小的整數倍,如有需要會在成員之間填充字節。編譯器在為結構體成員開辟空 間時,首先檢查預開辟空間的地址相對於結構體首地址的偏移量是否為該成員大小的整數倍,若是,則存放該成員;若不是,則填充若幹字節,以達到整數倍的要 求。

3)結構體變量所占空間的大小必定是最寬數據類型大小的整數倍。如有需要會在最後一個成員末尾填充若幹字節使得所占空間大小是最寬數據類型大小的整數倍。

下面看一下sizeof在計算結構體大小的時候具體是怎樣計算的

test 空結構體

typedef struct node { }S;

則sizeof(S)=1;或sizeof(S)=0;

在C++中占1字節,而在C中占0字節。

1.test1

typedef struct node1 { int a; char b; short c; }S1;

則sizeof(S1)=8。這是因為結構體node1中最長的數據類型是int,占4個字節,因此以4字節對齊,則該結構體在內存中存放方式為

|--------int--------| 4字節

|char|----|--short-| 4字節

總共占8字節

2.test2

typedef struct node2 { char a; int b; short c; }S2;

則siezof(S3)=12.最長數據類型為int,占4個字節。因此以4字節對齊,其在內存空間存放方式如下:

|char|----|----|----| 4字節

|--------int--------| 4字節

|--short--|----|----| 4字節

總共占12個字節

3.test3 含有靜態數據成員

typedef struct node3 { int a; short b; static int c; }S3;

則sizeof(S3)=8.這裏結構體中包含靜態數據成員,而靜態數據成員的存放位置與結構體實例的存儲地址無關(註意只有在C++中結構體中才能含有靜態數據成員,而C中結構體中是不允許含有靜態數據成員的)。其在內存中存儲方式如下:

|--------int--------| 4字節

|--short-|----|----| 4字節

而變量c是單獨存放在靜態數據區的,因此用siezof計算其大小時沒有將c所占的空間計算進來。

4.test4 結構體中含有結構體

typedef struct node4 { bool a; S1 s1; short b; }S4;

則sizeof(S4)=16。是因為s1占8字節,而s1中最長數據類型為int,占4個字節,bool類型1個字節,short占2字節,因此以4字節對齊,則存儲方式為

|-------bool--------| 4字節

|-------s1----------| 8字節

|-------short-------| 4字節

5.test5

typedef struct node5 { bool a; S1 s1; double b; int c; }S5;

則sizeof(S5)=32。是因為s1占8字節,而s1中最長數據類型為int,占4字節,而double占8字節,因此以8字節對齊,則存放方式為:

|--------bool--------| 8字節

|---------s1---------| 8字節

|--------double------| 8字節

|----int----|---------| 8字節

6.test6

若在程序中使用了#pragma pack(n)命令強制以n字節對齊時,默認情況下n為8.

則比較n和結構體中最長數據類型所占的字節大小,取兩者中小的一個作為對齊標準。

若需取消強制對齊方式,則可用命令#pragma pack()

如果在程序開頭使用命令#pragma pack(4),對於下面的結構體

typedef struct node5 { bool a; S1 s1; double b; int c; }S5;

則sizeof(S5)=24.因為強制以4字節對齊,而S5中最長數據類型為double,占8字節,因此以4字節對齊。在內存中存放方式為:

|-----------a--------| 4字節

|--------s1----------| 4字節

|--------s1----------| 4字節

|--------b-----------| 4字節

|--------b-----------| 4字節

|---------c----------| 4字節

總結一下,在計算sizeof時主要註意一下幾點:

1)若為空結構體,則只占1個字節的單元

2)若結構體中所有數據類型都相同,則其所占空間為 成員數據類型長度×成員個數

若結構體中數據類型不同,則取最長數據類型成員所占的空間為對齊標準,數據成員包含另一個結構體變量t的話,則取t中最 長數據類型與其他數據成員比較,取最長的作為對齊標準,但是t存放時看做一個單位存放,只需看其他成員即可。

3)若使用了#pragma pack(n)命令強制對齊標準,則取n和結構體中最長數據類型占的字節數兩者之中的小者作為對齊標準。

另外除了結構體中存在對齊之外,普通的變量存儲也存在字節對齊的情況,即自身對齊。編譯器規定:普通變量的存儲首地址必須能被該變量的數據類型寬度整除。

/*測試sizeof運算符  2017.10.25*/
 
#include <iostream>
using namespace std;
//#pragma pack(4)    //設置4字節對齊
//#pragma pack()     //取消4字節對齊
 
typedef struct node
{
     
}S;
 
typedef struct node1
{
    int a;
    char b;
    short c;
}S1;
 
typedef struct node2
{
    char a;
    int b;
    short c;
}S2;
 
typedef struct node3
{
    int a;
    short b;
    static int c;
}S3;
 
typedef struct node4
{
    bool a;
    S1 s1;
    short b;
}S4;
 
typedef struct node5
{
    bool a;
    S1 s1;
    double b;
    int c;
}S5;
int main(int argc, char *argv[]) { cout <<sizeof(char)<<" "<<sizeof(short)<<" "<<sizeof(int)<<" "<<sizeof(float)<<" "<<sizeof(double)<<endl; S s; S1 s1; S2 s2; S3 s3; S4 s4; S5 s5; cout<<sizeof(S3)<<endl; cout<<sizeof(s)<<" "<<sizeof(s1)<<" "<<sizeof(s2)<<" "<<sizeof(s3)<<" "<<sizeof(s4)<<" "<<sizeof(s5)<<endl; return 0; }

C/C++ 結構體字節對齊