測試程式碼
#include
#include
struct bitint {
int a:2; // 2 位元有號位域
int b:2; // 2 位元有號位域
int c:1; // 1 位元有號位域
};
int main() {
struct bitint test;
test.a = 1;
test.b = 3;
test.c = 1;
printf("a is %d b is %d c is %d\n", test.a, test.b, test.c);
return 0;
}
執行結果
a is 1 b is -1 c is -1
深度解析:有號位域數值範圍
位域寬度與有號數表示範圍
| 位域寬度 | 無號範圍 | 有號範圍 (二補數) |
| 1 bit | 0 ~ 1 | -1 ~ 0 |
| 2 bits | 0 ~ 3 | -2 ~ 1 |
| 3 bits | 0 ~ 7 | -4 ~ 3 |
| n bits | 0 ~ 2ⁿ-1 | -2ⁿ⁻¹ ~ 2ⁿ⁻¹-1 |
各欄位詳細分析
a:2 (有號 2 位元)
- 範圍:-2 ~ 1
- 指派 1 → 二進位
01 → 正數 1 ✓
- 輸出
1 正確
b:2 (有號 2 位元)
- 範圍:-2 ~ 1
- 指派 3 → 二進位
11 → 有號解讀為 -1(二補數)
- 輸出
-1 — 溢位截斷導致語意改變
c:1 (有號 1 位元)
- 範圍:-1 ~ 0
- 指派 1 → 二進位
1 → 有號解讀為 -1
- 輸出
-1 — 1 位元有號位域無法表示正數 1
關鍵結論
- 有號位域極小範圍:1 位元僅能存 -1/0,2 位元存 -2~1
- 指派超出範圍值會截斷高位,按二補數重新解讀,導致正變負
- 預設 int 位域為有號(C 標準規定),若需無號必須明確
unsigned int
- 位域不可跨位元組邊界(編譯器實作相關)
- 位域地址不可取:
&test.a 非法
修正版:使用無號位域
struct bitint_fixed {
unsigned int a:2; // 0~3
unsigned int b:2; // 0~3
unsigned int c:1; // 0~1
};
int main() {
struct bitint_fixed test;
test.a = 1; // 01 → 1
test.b = 3; // 11 → 3
test.c = 1; // 1 → 1
printf("a=%u b=%u c=%u\n", test.a, test.b, test.c); // 1 3 1
return 0;
}
最佳實踐建議
- 明確指定 signed/unsigned:
signed int / unsigned int,不依賴預設
- 位域寬度 ≥ 最大值所需位元數 + 1(符號位)
- 避免 1 位元有號位域:無法表達正數,極易出錯
- 旗標/布林值用 unsigned int:1:0/1 語意清晰
- 位域僅用於儲存優化,非效能優化(存取需位運算,反而較慢)
- 跨平台注意:位域配置順序、對齊、簽名預設皆為實作定義
標準 C 位域規範提醒
- 型態限定:
int、signed int、unsigned int、_Bool(C99)、實作定義型態
- 寬度為 0 的無名位域:強制下一位域對齊到下一儲存單元
- 無名位域:
int :3; 填充/對齊用,不可存取
- 上一篇文章: KEIL ARM 編譯器 RW/ZI 段處理機制分析:未初始化全域變數歸屬探討
- 下一篇文章: C語言指標長度與類型完整解析:可移植性、預定義類型與指標運算
Copyright ©
程式碼之家 All Rights Reserved