在 C 語言中使用結體構對齊和填充
本文將介紹幾種在 C 語言中使用結構體來對齊和填充的方法。
瞭解 C 語言中對齊和填充的基礎知識
記憶體中的所有物件都以主要資料型別表示,如:char
、short
、int
、long
、pointer
等。char
、short
、int
、long
、pointer
等。這些資料型別在記憶體中都有其相應的大小。在大多數當代 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 整除的地址上對齊,所以前面的 int
和 short
總共會佔用 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