C語言結構體,記憶體對齊

2020-08-09 23:49:12

1、爲什麼要記憶體對齊

許多計算機系統對基本數據型別的合法地址做出了一些限制,要求某種型別物件的地址必須是某個值K(通常是2,4,8,或16)的倍數。這種對齊限制簡化了形成處理器和記憶體系統之間介面的硬體設計。(來源於計算機操作系統第一部分,程式結構和執行)
其中詳細解釋參考大神部落格
https://blog.csdn.net/lgouc/article/details/8235471

2、記憶體對齊方法

1、利用預設規則
例1:

//程式碼1  共佔用 2 * sizeof(int) 個位元組
struct  test
{
    char a;
    char b;
    int num;
}

程式碼1 變數記憶體分佈如圖
預設情況下以 int 型變數 的位元組數(K值)進行對齊。
在这里插入图片描述因爲char 型別只佔用了一個位元組,所以佔1格,int 型需要4個位元組,佔了4格
所以整個test 結構體需要8個位元組。

例2:

//程式碼2   共佔用 3 * sizeof(int) 個位元組
struct  test
{
    char a;
    int num;
    char b;
    
}

程式碼2 變數記憶體分佈如圖
在这里插入图片描述

總結:
在使用gcc編譯時,由於結構體內變數按照先後順序在記憶體中儲存,且按照預設規則以4進行對齊。要求結構體起始地址必須是4的倍數。在儲存char 型別後,儲存int 型別時仍然要求起始地址爲4的倍數,所以char 型別後的地址就不滿足儲存要求。所以char 型別後空了3個位元組。所以這就是程式碼2的記憶體分佈原因。
程式碼1的在儲存char型別後,下一個還是char型別。只有一個位元組,所以第一個char 型別後的地址是滿足儲存要求的。所以分佈如程式碼1下的圖示所示。

範例展示

類似的程式碼和實際運算結果展示:

#include <stdio.h>

struct ffe
{
    char a;    //佔用4個位元組 實際使用1個位元組
    int b;     //佔用4個位元組 實際使用4個位元組 ,和char 變數共用8個位元組
    double c;  //佔用8個位元組 實際使用8個位元組
};

int main()
{
    ffe tr;
    printf("%d\n",sizeof(tr));
    printf("a = %p\n, b = %p\n, c = %p\n",&tr.a, &tr.b, &tr.c);

    return 0;
}

輸出結果:

16 a = 0x7ffcbf14ca00 , b =0x7ffcbf14ca04, c = 0x7ffcbf14ca08

記憶體中分佈大概如圖示
在这里插入图片描述
當變數位置交換後,實際佔用記憶體爲 3 * sizeof(double)。 一般爲24位元組。

struct ffe
{
    char a;    //佔用8個位元組,實際使用1個位元組
    double c;   //佔用8個位元組,實際使用8個位元組
    int b;    //佔用8個位元組,實際使用4個位元組
};   

所以根據變數位元組大小來決定變數順序可以有效減小使用記憶體。

2、設定結構體記憶體對齊K
使用如下語句
#pragma pack(k) //沒有分號
對應程式碼1,如果將k 設定爲1,則實際記憶體佔用爲6個位元組。將K設定爲2 ,結果也是一致的。因爲設定了變數的變數的對齊爲2的倍數。但整型變數實際佔用了兩個記憶體單元。會造成取值速度下降。
在这里插入图片描述
K = 4 的結果與程式碼1的結果是一致的。
當K = 8 超過了預設的 K =4 (int 4個位元組)。記憶體對齊仍然是按照 K = 4 進行記憶體對齊的。

3.位域

位域:描述的是結構體中變數佔的位。(一個位元組共有8個位)
char 型別,一個位元組 ,共有8個位,所以位域數不能超過8。
int 型別 ,4個位元組,共有32個位,所以位域數不能超過32。

具體使用

//程式碼3
struct ffe
{
    char a:2;
    char b:2;
    char c:2; 
};

由於一個位元組(8個位)是系統中最小可定址單位。所以無法對位域取地址。無法通過地址檢視相關關係。

程式碼3的結構體,佔用記憶體爲一個位元組。由於一個字元變數只使用了兩個位,表示的最大字元ASCII碼不能超過3;因爲超過了就會溢位。

但是變數仍然滿足記憶體對齊規則。程式碼4的結構體仍然佔據8個位元組。

//程式碼4
struct ffe
{
    char a:2;
    char b:2;
    char c:2; 
    int d:1;
};

只能通過大小或者賦值去判斷。

4、位域的聯合使用

struct ttr
{
    int a:8;
    int b:8;
    int c:8;
    int d:8;
};

union ffe
{
    int a;
    ttr ch;
};

整型變數a 和結構體變數ch 共同佔據同一塊記憶體,兩種型別的記憶體大小是一致的,地址是一致的。存取變數按照正常存取即可,所以可以通過兩種型別存取同一塊記憶體 。將較小的值限制在相應的小空間內。達到節約記憶體的目的。所以賦值不能超過實際的位。