問題現象
char c = 128;
printf("%d\n", c); // 輸出 -128
printf("%d\n", 128); // 輸出 128
核心原因:char 有符號性 + 整型提升 + 補碼表示
1. 內存佈局 (假設 8-bit char、32-bit int、二進制補碼)
| 變量 | 類型 | 二進制 (內存/寄存器) | 十進制解釋 |
| `c` | `char` (有符號) | `1000 0000` (1 字節) | -128 (補碼: 符號位1, 數值位全0 → -128) |
| `128` | `int` (字面量) | `0000 0000 0000 0000 0000 0000 1000 0000` (4 字節) | 128 |
2. `printf("%d", c)` 發生了什麼?
- 整型提升:`char` 作為變長參數傳遞,**自動提升為 `int`**。
- 符號擴展:因 `char` 是**有符號類型** (大多數編譯器默認),高位用**符號位填充** (1) → `1111 1111 1111 1111 1111 1111 1000 0000` (0xFFFFFF80)。
- `%d` 解釋:按**有符號 int** 讀取上述 32 位模式 → **-128**。
3. `printf("%d", 128)` 發生了什麼?
- 字面量 `128` 類型為 `int` (無符號擴展問題),直接傳遞 32 位 `0x00000080`。
- `%d` 解釋為 **128**。
關鍵知識點總結
| 概念 | 說明 |
| `char` 有符號性 | C 標準**未規定** `char` 是否有符號。x86/ARM 多默認**有符號** (-128~127),部分編譯器可選 `-funsigned-char` 改為無符號 (0~255)。 |
| 整型提升 | 小於 `int` 的整數類型 (`char`/`short`/`bool`) 作為參數傳遞/運算時,**自動提升為 `int` (或 `unsigned int`)**。 |
| 符號擴展 | 有符號類型提升時,高位填充**符號位** (0 或 1);無符號類型高位填充 0。 |
| 補碼表示 | 有符號整數最高位為符號位。`1000 0000` (8位) 表示 -128。擴展為 32 位仍保持數值不變。 |
| `printf` 類型不匹配 | `%d` 對應 `int`。傳入 `char` 經提升為 `int` 後匹配,但數值已經過符號擴展。 |
驗證代碼
#include
#include
int main() {
printf("CHAR_MIN=%d, CHAR_MAX=%d\n", CHAR_MIN, CHAR_MAX); // 查看 char 範圍
// 輸出: CHAR_MIN=-128, CHAR_MAX=127 → char 為有符號
char c = 128; // 實際存儲位模式 0x80
printf("c (%%d) = %d\n", c); // -128 (符號擴展)
printf("c (%%u) = %u\n", c); // 4294967168 (無符號解釋擴展後值)
printf("(unsigned char)c (%%d) = %d\n", (unsigned char)c); // 128 (強制無符號擴展)
return 0;
}
避坑指南
- 不明確 `char` 有符號性時,不直接用於數值運算/打印**。
- 需存儲 0~255 數值:用 `unsigned char` / `uint8_t` (stdint.h)。
- 打印 `char` 數值:`printf("%d", (unsigned char)c)` 或 `printf("%hhu", c)` (C99 `%hhu` 專打印 `unsigned char`)。
- 位操作/標志位:用 `unsigned char` 或 `uint8_t`,避免符號位干擾。
總結
`char c = 128` 存儲位模式 `0x80`。`printf("%d", c)` 觸發**有符號 char → int 符號擴展**,高位填 1 變 `0xFFFFFF80`,`%d` 解釋為 **-128**。而字面量 `128` 為 `int` 類型,無擴展問題,輸出 **128**。**理解「類型提升+符號擴展+補碼」三位一體**,是掌握 C 整數行為的關鍵。