首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言位域詳細解析:定義、存取、記憶體佈局與實戰

     編輯:關於C

位域:在結構體中按位分配儲存空間

位域允許在結構體成員中指定佔用的位數,適用於旗標集合、硬體暫存器映射、通訊協定解析等場景。

基本語法

struct BitField {
    unsigned int flag1 : 1;  // 1 位
    unsigned int flag2 : 1;
    unsigned int value : 6;  // 6 位 (0-63)
    unsigned int : 0;        // 強制對齊到下一個 int 邊界
    unsigned int flag3 : 1;
};

記憶體佈局規則

  1. 依宣告順序分配:從低位向高位 (小端序機器)
  2. 同類型相鄰可合併:共用同一個底層儲存單元
  3. 跨越邊界自動換行:不足容納時移至下一個儲存單元
  4. 未命名位域:unsigned int : 3; 僅作填充/對齊,不可存取
  5. 零寬度位域:unsigned int : 0; 強制下一成員對齊到新單元

實際佈局範例 (假設 int 32 位)

struct Status {
    unsigned int mode : 2;     // bit 0-1
    unsigned int fan : 2;      // bit 2-3
    unsigned int swing : 1;    // bit 4
    unsigned int : 3;          // bit 5-7 (填充)
    unsigned int temp : 8;     // bit 8-15 (下一個 byte)
    unsigned int : 0;          // 強制對齊到下一個 int (bit 32)
    unsigned int err : 4;      // bit 32-35
};
// 總大小:8 bytes (2 個 int)

存取與轉換

struct Status s;
s.mode = 2;      // 直接賦值,編譯器自動處理位移/遮罩
s.fan = 3;
s.swing = 1;

// 整數與位域互轉 (需相同大小)
uint8_t byte = 0x6A; // 0110 1010
struct Status *ps = (struct Status*)&byte;
// ps->mode = 2 (bit 0-1: 10)
// ps->fan  = 2 (bit 2-3: 10)
// ps->swing= 0 (bit 4: 0)
// 填充位 = 011 (bit 5-7)

關鍵限制與陷阱

限制說明對策
不可取址&s.mode 非法用整數暫存變數中轉
不可建立陣列struct { int a:1; } arr[10]; 非法用整數陣列 + 位運算
跨平台不可移植位序、對齊、填充順序由實作定義協定解析用手動位運算
有號位域陷阱int a:3; 範圍 -4~3 而非 0~7統一用 unsigned
sizeof 不可用於位域sizeof(s.mode) 非法用 sizeof(s)

實戰場景

1. 硬體暫存器映射

// UART 控制暫存器
struct UART_CTRL {
    unsigned int en    : 1; // 啟用
    unsigned int tx_en : 1; // 發送啟用
    unsigned int rx_en : 1; // 接收啟用
    unsigned int parity: 2; // 0=無, 1=奇, 2=偶
    unsigned int stop  : 1; // 停止位
    unsigned int : 2;       // 保留
    unsigned int baud  : 8; // 波特率分頻
};

// 寫入暫存器
volatile struct UART_CTRL *uart = (void*)0x40004000;
uart->en = 1;
uart->baud = 0x68; // 9600 @ 16MHz

2. 通訊協定封包解析

struct PacketHeader {
    uint8_t  sync : 2;     // 同步碼
    uint8_t  ver  : 3;     // 版本
    uint8_t  type : 3;     // 封包類型
    uint16_t len  : 10;    // 長度
    uint16_t : 6;          // 保留
    uint16_t crc  : 10;    // CRC
    uint16_t : 6;          // 保留
};
// 注意:跨平台解析建議用手動位運算

3. 壓縮旗標儲存

struct FileFlags {
    unsigned int readonly : 1;
    unsigned int hidden   : 1;
    unsigned int system   : 1;
    unsigned int archive  : 1;
    unsigned int compressed: 1;
    unsigned int encrypted : 1;
    unsigned int : 2; // 保留
};
// 1 byte 儲存 6 個旗標,比 6 個 bool 省 5 bytes

編譯器實作差異 (GCC vs MSVC vs Keil)

  • 位分配方向:GCC/Clang 通常低位優先;MSVC 同;Keil C51 視記憶體模式而定
  • 儲存單元:通常為 int,但可用 char/short/long 指定
  • 零寬度對齊:皆支援 :0 強制對齊

現代替代方案

需跨平台、可維護、型別安全時,建議使用:

  • 手動位運算:GET_BIT(reg, 3), SET_BIT(reg, 3, val) 宏
  • C++ bitset / bit_field:型別安全、可移植
  • 結構體 + 靜態斷言:static_assert(sizeof(Reg) == 4) 確保佈局
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved