- C程式設計教程
- C語言 - 首頁
- C語言基礎
- C語言 - 概述
- C語言 - 特性
- C語言 - 歷史
- C語言 - 環境搭建
- C語言 - 程式結構
- C語言 - Hello World
- C語言 - 編譯過程
- C語言 - 註釋
- C語言 - 詞法單元
- C語言 - 關鍵字
- C語言 - 識別符號
- C語言 - 使用者輸入
- C語言 - 基本語法
- C語言 - 資料型別
- C語言 - 變數
- C語言 - 整數提升
- C語言 - 型別轉換
- C語言 - 型別強制轉換
- C語言 - 布林值
- C語言中的常量和字面量
- C語言 - 常量
- C語言 - 字面量
- C語言 - 轉義序列
- C語言 - 格式說明符
- C語言中的運算子
- C語言 - 運算子
- C語言 - 算術運算子
- C語言 - 關係運算符
- C語言 - 邏輯運算子
- C語言 - 位運算子
- C語言 - 賦值運算子
- C語言 - 一元運算子
- C語言 - 自增和自減運算子
- C語言 - 三元運算子
- C語言 - sizeof 運算子
- C語言 - 運算子優先順序
- C語言 - 其他運算子
- C語言中的決策語句
- C語言 - 決策語句
- C語言 - if 語句
- C語言 - if...else 語句
- C語言 - 巢狀 if 語句
- C語言 - switch 語句
- C語言 - 巢狀 switch 語句
- C語言中的迴圈
- C語言 - 迴圈
- C語言 - while 迴圈
- C語言 - for 迴圈
- C語言 - do...while 迴圈
- C語言 - 巢狀迴圈
- C語言 - 無限迴圈
- C語言 - break 語句
- C語言 - continue 語句
- C語言 - goto 語句
- C語言中的函式
- C語言 - 函式
- C語言 - 主函式
- C語言 - 按值呼叫函式
- C語言 - 按引用呼叫函式
- C語言 - 巢狀函式
- C語言 - 可變引數函式
- C語言 - 使用者自定義函式
- C語言 - 回撥函式
- C語言 - 返回語句
- C語言 - 遞迴
- C語言中的作用域規則
- C語言 - 作用域規則
- C語言 - 靜態變數
- C語言 - 全域性變數
- C語言中的陣列
- C語言 - 陣列
- C語言 - 陣列的特性
- C語言 - 多維陣列
- C語言 - 向函式傳遞陣列
- C語言 - 從函式返回陣列
- C語言 - 變長陣列
- C語言中的指標
- C語言 - 指標
- C語言 - 指標和陣列
- C語言 - 指標的應用
- C語言 - 指標運算
- C語言 - 指標陣列
- C語言 - 指向指標的指標
- C語言 - 向函式傳遞指標
- C語言 - 從函式返回指標
- C語言 - 函式指標
- C語言 - 指向陣列的指標
- C語言 - 指向結構體的指標
- C語言 - 指標鏈
- C語言 - 指標與陣列的區別
- C語言 - 字元指標和函式
- C語言 - 空指標
- C語言 - void 指標
- C語言 - 懸空指標
- C語言 - 解引用指標
- C語言 - 近指標、遠指標和巨指標
- C語言 - 指標陣列的初始化
- C語言 - 指標與多維陣列的區別
- C語言中的字串
- C語言 - 字串
- C語言 - 字串陣列
- C語言 - 特殊字元
- C語言中的結構體和聯合體
- C語言 - 結構體
- C語言 - 結構體和函式
- C語言 - 結構體陣列
- C語言 - 自引用結構體
- C語言 - 查詢表
- C語言 - 點(.)運算子
- C語言 - 列舉 (enum)
- C語言 - 結構體填充和打包
- C語言 - 巢狀結構體
- C語言 - 匿名結構體和聯合體
- C語言 - 聯合體
- C語言 - 位域
- C語言 - Typedef
- C語言中的檔案處理
- C語言 - 輸入 & 輸出
- C語言 - 檔案I/O (檔案處理)
- C語言預處理器
- C語言 - 預處理器
- C語言 - 預處理指令
- C語言 - 預處理器運算子
- C語言 - 宏
- C語言 - 標頭檔案
- C語言中的記憶體管理
- C語言 - 記憶體管理
- C語言 - 記憶體地址
- C語言 - 儲存類
- 其他主題
- C語言 - 錯誤處理
- C語言 - 可變引數
- C語言 - 命令執行
- C語言 - 數學函式
- C語言 - static 關鍵字
- C語言 - 隨機數生成
- C語言 - 命令列引數
- C語言程式設計資源
- C語言 - 問答
- C語言 - 快速指南
- C語言 - 速查表
- C語言 - 有用資源
- C語言 - 討論
C語言中的結構體填充和打包
什麼是C語言中的結構體填充?
C語言中的結構體填充是由CPU架構處理的過程。結構體填充在結構體中新增一定數量的空位元組,以便資料成員在記憶體中自然對齊。對齊要求由處理器架構決定,而不是由語言本身決定。當然,對齊要求會根據某個CPU架構的資料匯流排大小或其他架構方面的考慮而改變。
透過示例理解結構體填充
讓我們定義如下結構體型別:
struct struct1 {
char x;
int y;
char z;
};
示例1
讓我們檢查這種型別變數所需的位元組大小:
#include <stdio.h>
struct struct1{
char a;
char b;
int c;
};
int main(){
printf("Size: %d", sizeof(struct struct1));
return 0;
}
輸出
執行此程式碼時,將產生以下輸出:
Size: 8
結果與預期相反。
考慮到char型別需要1個位元組,int型別需要4個位元組,人們可能會認為輸出應該是“1 + 1 + 4 = 6位元組”。
但是,CPU架構需要更改此結構。考慮到我們使用的是具有32位處理器的CPU,它一次讀取4個位元組,這意味著一個字等於4個位元組。
在一個CPU週期中,它訪問char "a",然後是char "b"和int "c"的前兩個位元組。在第二個週期中,訪問其他兩個位元組。
即使我們只想讀取“c”,也需要兩個CPU週期。為此,CPU在儲存“c”值的位元組之前新增兩個空位元組。這種機制稱為填充。
現在解釋了我們上面獲得的結果,即結構體型別的尺寸為8位元組。
示例2
讓我們更改上述結構體成員的順序,並設定“b”和“c”的型別。
#include <stdio.h>
struct struct1{
char a;
int b;
char c;
};
int main(){
printf("size: %d", sizeof(struct struct1));
return 0;
}
輸出
執行程式碼並檢查其輸出:
size: 12
在第一個字的4個位元組中,第一個位元組分配給char "a",後面跟著三個空位元組。
構成下一個字的接下來的4個位元組用於儲存int "b"。隨後,在接下來的4個位元組組中,只有一個用於“c”。但是,結構體大小為12。
什麼是C語言中的結構體打包?
另一方面,結構體打包是一種最小化填充影響的機制,從而嘗試減少浪費的記憶體空間。我們可以使用某些pragma指令和屬性來實現打包。
透過示例理解結構體打包
由CPU架構強制執行的填充是不可避免的,但是有一些方法可以最大限度地減少填充。可以使用以下方法:
- 使用 #pragma pack(1) 指令
- 使用 packed 屬性
使用 #pragma pack(1) 指令
#pragma pack(1) 預處理器指令強制編譯器忽略填充,並在記憶體分配過程中將結構體成員端對端對齊。
示例
讓我們將此指令新增到前面使用的程式碼頂部,並檢視結果:
#include <stdio.h>
#pragma pack(1)
struct struct1{
char a;
int b;
char c;
};
int main(){
printf("size: %d", sizeof(struct struct1));
return 0;
}
輸出
執行程式碼並檢查其輸出:
size: 6
我們可以看到結構體填充已被避免,並減少了記憶體浪費。
使用 __attribute__((packed))
使用GCC,我們可以使用屬性來指定結構體和聯合體型別的各種特殊屬性。這些屬性是:aligned, deprecated, packed, transparent_union, unused 和 visibility。應用這些屬性的語法是“__attribute__ ((...))”。
示例
在這裡,我們將使用packed屬性在我們的結構體型別定義中。
#include <stdio.h>
struct __attribute__((packed)) struct1{
char a;
int b;
char c;
};
int main(){
printf("size: %d", sizeof(struct struct1));
return 0;
}
輸出
執行程式碼並檢查其輸出:
size: 6
此方法也避免了填充的影響。