1. 程式人生 > >C進階之:記憶體對齊

C進階之:記憶體對齊

什麼是記憶體對齊? 
記憶體對齊可以用一句話來概括: 
“資料項只能儲存在地址是資料項大小的整數倍的記憶體位置上”。例如int型別佔用4個位元組,地址只能在0,4,8等位置上。 
不同型別的資料在記憶體中按照一定的規則排列,而不一定是順序的一個接一個的排列,這就是所謂的記憶體對齊。如下Test1和Test2所佔的記憶體空間是不同的。

struct Test1
{
    char c1;
    short s;
    char c2;
    int i;
}:
struct Test2
{
    char c1;
    char c2;
    short s;
    int i;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

為什麼需要記憶體對齊? 
CPU對記憶體的讀取不是連續的,而是分成塊讀取的,塊的大小隻能是1、2、4、8、16..位元組;當讀取操作的資料未對齊,則需要兩次匯流排週期來訪問記憶體,因此效能會大打折扣;某些硬體平臺只能從規定的相對地址處讀取特定型別的資料,否則產生硬體異常。 
記憶體對齊目的:是為了對齊資料以提高儲存器的效能,加快記憶體的讀取速度,提高定址效率。

#pragma pack用於指定記憶體對齊方式
#pragma pack能夠改變編譯器的預設對齊方式
1
2
struct佔用的記憶體大小的計算方法: 
A,第一個成員起始於0偏移處。 
B,每個成員按其型別大小和pack引數中較小的一個進行對齊(偏移地址必須能被對齊引數整除;結構體成員的大小取其內部長度最大的資料成員作為其大小)。 
C,結構體總長度必須為所有對齊引數的整數倍。編譯器在預設情況下按照4位元組對齊。

#pragma pack(4)
struct Test1
{       //對齊引數 偏移地址 大小
char c1; // 1     0       1  (1 < 4 所以對齊引數是1)
short s; // 2     2       2  (2 < 4 所以對齊引數是2;偏移地址必須能被對齊引數整除,所以偏移地址是2)
char c2; // 1     4       1  (1 < 4 所以對齊引數是1;偏移地址必須能被對齊引數整除,所以偏移地址是4)
int i;  //  4     8       4  (4 = 4 所以對齊引數是4;偏移地址必須能被對齊引數整除,所以偏移地址是8)
};    
// 所以結構體總的記憶體大小為:8 + 4 = 12
// 驗算:12是1,2,4的整數倍,滿足:結構體總長度必須為所有對齊引數的整數倍。
#pragma pack()

#pragma pack(4)
struct Test2
{            // 對齊引數        偏移地址    大小
char c1;     //  1             0           1
char c2;     //  1             1           1
short s;     //  2             2           2 
int i;       //  4             4           4
};      
// 所以結構體總的記憶體大小為:4 + 4 = 8
// 驗算:8是1,2,4的整數倍,滿足:結構體總長度必須為所有對齊引數的整數倍。
#pragma pack()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
例項:結構體大小計算 
練習一:

#pragma pack(1)
struct Test1
{           // 對齊引數         偏移地址   大小
char c1;    //   1              0          1
short s;    //   1              1          2
char c2;    //   1              3          1
int i;      //   1              4          4
}:
#pragma pack()

#pragma pack(1)
struct Test2
{            // 對齊引數        偏移地址    大小
char c1;     //   1            0           1
char c2;     //   1            1           1
short s;     //   1            2           2
int i;       //   1            4           4
};
#pragma pack()

sizeof(struct Test1) = 8
sizeof(struct Test2) = 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
練習二:

#include <stdio.h>

#pragma pack(2)
struct Test1
{                // 對齊引數        偏移地址    大小
    char  c1;    //  1            0            1
    short s;     //  2            2            2
    char  c2;    //  1            4            1
    int   i;     //  2            6            4
};    
// 所以結構體總的記憶體大小為:6 + 4 = 10
// 驗算:10是1,2的整數倍,滿足:結構體總長度必須為所有對齊引數的整數倍。
#pragma pack()

#pragma pack(4)
struct Test2
{            // 對齊引數        偏移地址    大小
char c1;     //  1             0           1
char c2;     //  1             1           1
short s;     //  2             2           2 
int i;       //  4             4           4
};      
// 所以結構體總的記憶體大小為:4 + 4 = 8
// 驗算:8是1,2,4的整數倍,滿足:結構體總長度必須為所有對齊引數的整數倍。
#pragma pack()

int main()
{
    printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
    printf("sizeof(Test2) = %d\n", sizeof(struct Test2));

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
練習三:

#include <stdio.h>

#pragma pack(8)
struct S1
{             // 對齊引數       偏移地址    大小
    short a;  //  2            0           2
    long b;   //  4            4           4      8
};

struct S2   // ( 結構體成員的大小取其內部長度最大的資料成員作為其大小 )
{                // 對齊引數        偏移地址    大小
    char c;      //  1             0           1
    struct S1 d; //  4             4           8      
    double e;    //  8 (4)       16  (12)   8     24
};
// 結構體成員的大小取其內部長度最大的資料成員4; 4 < 8 所以對齊引數是4。上面已算出struct S1 d的大小為8.    
// 注意:gcc不支援8位元組對齊,#pragma pack(8)   ; 所以結果應該是12 + 8 = 20
#pragma pack()

int main()
{
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
記憶體對齊之結構體中新增位段:

struct B
{
    char c1:6; 
    char c2:1;
    int in:16;
};         
// sizeof (b) = 4
1
2
3
4
5
6
7
標記 c1,c2,in 分別需要 6位 1位 16位 一個位元組8位 
補充說明:

struct packed_struct
{
    unsigned intf1 :1;
    unsigned intf2 :1;
    unsigned intf3 :1;
    unsigned inttype :4;
    unsigned intindex :7;
};  
// sizeof(structpackd_struct) = 4
1
2
3
4
5
6
7
8
9
當位段出現在結構定義中時, 至少會佔用等同於unsigned int型別的空間,當所有的位段之和超出,分配另一個unsignedint空間。unsigned char或者其他型別不必考慮。 
/**************************列舉型別***************************/

struct A
{
     enum a
     {
         INDEX1,
         INDEX2,
         INDEX3,
     };
} a ;            
// sizeof(a) = 0     列舉是型別不是變數不分配變數
1
2
3
4
5
6
7
8
9
10
/**************************聯合體****************************/

union B
{
    short s1;
    char c2;
    short s2;
    int in;
} b ; 
union B
{
    short s1:6;
    char c2:1;
    short s2:15;
    int in:16;
} b ;
// 兩個聯合體  sizeof(b) 均為4
--------------------- 
作者:碼農u號 
來源:CSDN 
原文:https://blog.csdn.net/qq_29545231/article/details/77447672 
版權宣告:本文為博主原創文章,轉載請附上博文連結!