在 C 語言中使用結體構對齊和填充

Jinku Hu 2023年1月30日 2021年2月28日
  1. 瞭解 C 語言中對齊和填充的基礎知識
  2. 在 C 語言中使用成員重新排序技術來節省物件的空間
在 C 語言中使用結體構對齊和填充

本文將介紹幾種在 C 語言中使用結構體來對齊和填充的方法。

瞭解 C 語言中對齊和填充的基礎知識

記憶體中的所有物件都以主要資料型別表示,如:charshortintlongpointer 等。charshortintlongpointer 等。這些資料型別在記憶體中都有其相應的大小。在大多數當代 64 位桌面處理器上,char 的大小是 1 個位元組,short 是 2 個位元組,int 是 4 個位元組,pointer 是 8 個位元組,以此類推。請注意,這些都不是保證大小(除了 char),但可以使用 sizeof 操作符檢索物件的大小。現在,對齊是編譯器用來放置記憶體中的變數的方法,意味著每個基本資料型別都儲存在被相應大小除以的地址上。

通常,利用對齊來更快、更有效地訪問資料物件。對齊迫使連續宣告的不同資料型別在其地址之間包含一些間距。也就是說,如果我們宣告一個結構 st1,其中有一個指標和一個 char,如下面的例子所示,它總共會佔用 16 個位元組。不過要注意,一個指標需要 8 個位元組,一個 char 需要一個位元組,所以人們會認為 st1 結構一定會佔用 9 個位元組。但它的表現就像所有成員都是按照最大成員的大小(即 8 個位元組)對齊的。st2 結構展示了一個類似的結構,它佔據了同樣的記憶體量,只是它有一個 7 個 char 成員的陣列。

#include <stdio.h>
#include <stdlib.h>

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

    typedef struct {
        char *p;
        char c2;
    } st1;

    typedef struct {
        char *p;
        char c2;
        char padding[7];
    } st2;

    printf("sizeof st1 = %zu\n", sizeof(st1));
    printf("sizeof st2 = %zu\n", sizeof(st2));

    exit(EXIT_SUCCESS);
}

輸出:

sizeof st1 = 16
sizeof st2 = 16

在 C 語言中使用成員重新排序技術來節省物件的空間

前面的例子表明,當結構包括不同型別,並且沒有填滿對齊邊界時,會有一些記憶體的浪費。雖然,在某些情況下,可能會重新排列結構成員,節省額外的空間。

接下來的示例程式碼中定義了 foo1 結構,它的中間是最大的成員(char *),而 foo2 結構的成員與第一個結構相同。這兩個物件的大小是不同的–24 位元組和 16 位元組。這與資料成員的排序有關。在 foo1 結構中,p 需要在被 8 整除的地址上對齊,所以前面的 intshort 總共會佔用 8 個位元組,後面的兩個 char 也會佔用這 8 個位元組。雖然,如果我們把 p 移到第一位,下面的成員將擠進 8 個位元組,也滿足對齊規則。這樣,foo2 的大小一共是 16 個位元組,就叫打包 struct。請注意,gcc 編譯器有特殊的 __attribute__((packed)) 指定符,該說明符甚至可以強制打包無序的 struct 成員。

#include <stdio.h>
#include <stdlib.h>

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

    typedef struct {
        int n1;
        short s1;
        char *p;
        char c1;
        char c2;
    } foo1;

    typedef struct {
        char *p;
        int n1;
        short s1;
        char c1;
        char c2;
    } foo2;

    typedef struct {
        int n1;
        short s1;
        char *p;
        char c1;
        char c2;
    } __attribute__((packed)) foo3;

    printf("sizeof foo1 = %zu\n", sizeof(foo1));
    printf("sizeof foo2 = %zu\n", sizeof(foo2));
    printf("sizeof foo3 = %zu\n", sizeof(foo3));

    exit(EXIT_SUCCESS);
}

輸出:

sizeof foo1 = 24
sizeof foo2 = 16
sizeof foo3 = 16
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相關文章 - C Struct