「十進制255在8位中是11111111,最高位是1不應該是負數嗎?這到底是255還是-1?」這個經典疑問觸及計算機數據表示的核心——類型決定解釋。本文從位模式、補碼數學、類型系統、實際編程四個維度徹底釐清。
同一組位模式 `11111111`,作為 `unsigned char` 解釋為 255,作為 `signed char` (補碼) 解釋為 -1。 編譯器根據變量聲明類型,生成相應的指令序列來處理這組位。
8位補碼編碼空間:2^8 = 256種位模式 無符號解釋 (模256同餘環): 00000000 = 0 00000001 = 1 ... 11111110 = 254 11111111 = 255 有符號補碼解釋 (模256同餘環,語義映射): 00000000 = 0 00000001 = 1 ... 01111110 = 126 01111111 = 127 (最大正數) 10000000 = -128 (最小負數,無對應正數) 10000001 = -127 ... 11111110 = -2 11111111 = -1 (全1位模式) 補碼優勢: 1. 加減法統一:有符號/無符號加減法指令完全相同 (模2^N算術) 2. 零唯一:無符號/補碼零表示唯一 (符號大小法有+0/-0) 3. 符號位直觀:最高位1為負,0為非負 4. 範圍對稱性:-2^(N-1) 到 2^(N-1)-1
#include
#include
int main() {
// 同一位模式 0xFF (11111111)
unsigned char u = 255; // 或 0xFF
signed char s = -1; // 或 0xFF (隱式轉換)
printf("u = %hhu (無符號)\n", u); // 255
printf("s = %hhd (有符號)\n", s); // -1
// 位模式相同
printf("u bits: "); for(int i=7;i>=0;i--) printf("%d", (u>>i)&1); printf("\n");
printf("s bits: "); for(int i=7;i>=0;i--) printf("%d", (s>>i)&1); printf("\n");
// 輸出均為 11111111
// 範圍驗證
printf("UCHAR_MAX = %u\n", UCHAR_MAX); // 255
printf("SCHAR_MAX = %d\n", SCHAR_MAX); // 127
printf("SCHAR_MIN = %d\n", SCHAR_MIN); // -128
return 0;
}
| 代碼 | 行為 | 原因 |
|---|---|---|
signed char c = -1; printf("%d", c); | 輸出 -1 | 整型提升:`signed char` → `int` 符號擴展 (0xFF → 0xFFFFFFFF) |
unsigned char c = 255; printf("%d", c); | 輸出 255 | 整型提升:`unsigned char` → `int` 零擴展 (0xFF → 0x000000FF) |
char c = 200; // 假設char有符號 | 實現定義行為 | `char`是否有符號由編譯器決定,200超出-128~127範圍 |
if (c > 100) // c為signed char -1 | 條件為假 | 提升後比較:-1 > 100 為假 |
if (uc > 100) // uc為unsigned char 255 | 條件為真 | 提升後比較:255 > 100 為真 |
signed char a = 100, b = 50; a+b | 結果 -106 (溢出) | 提升為int相加得150,賦回signed char截斷為-106 |
#include // C99 定長整型
// 明確位寬與符號性
int8_t i8 = -1; // 精確8位有符號
uint8_t u8 = 255; // 精確8位無符號
int16_t i16 = -1;
uint32_t u32 = 0xFFFFFFFF;
// 打印宏 (inttypes.h)
printf("i8 = %" PRId8 "\n", i8); // -1
printf("u8 = %" PRIu8 "\n", u8); // 255
// 避免假設 int/long 位寬
// 使用 int32_t, uint64_t 等明確類型
// GDB 查看原始位
(gdb) print /x c // 十六進制
(gdb) print /t c // 二進制
(gdb) print /c c // 字符
// 代碼中通用打印位模式
void print_bits(void *ptr, size_t bytes) {
unsigned char *p = ptr;
for (size_t i = 0; i < bytes; i++) {
for (int b = 7; b >= 0; b--)
putchar((p[i] >> b) & 1 ? '1' : '0');
putchar(' ');
}
putchar('\n');
}
signed char s = -1;
unsigned char u = 255;
print_bits(&s, 1); // 11111111
print_bits(&u, 1); // 11111111
理解「類型賦予位模式語義」,你就掌握了C語言整數運算、位操作、類型轉換、移植性編程的基石。下次看到`0xFF`,請問自己:這是`uint8_t`的255,還是`int8_t`的-1?