C 語言隱式類型轉換規則複雜,特別是有號/無號混合運算與整數提升,極易導致非直觀結果。本文取材自《深入理解計算機系統》,透過兩個經典案例深入剖析。
short sx = -12345;
unsigned uy = sx;
printf("uy = %u\n", uy); /* 輸出 4294954951 (0xFFFFCFC7) */
規則:轉換時先擴展位寬再改變符號性。即 `(unsigned)sx` 等價於 `(unsigned)(int)sx`,而非 `(unsigned)(unsigned short)sx`。
`-12345` (0xCFC7) → `int` 擴展為 0xFFFFCFC7 → `unsigned` 解讀為 4294954951。
若先轉 `unsigned short` 則為 53191 (0xCFC7),結果不同。C 標準強制要求前者。
-1 < 0U /* 結果為 0 (假) */
原因:`0U` 為 `unsigned int`,`-1` 隱式轉為 `unsigned int` (4294967295U),比較變為 `4294967295U < 0U` → 假。
double sum_elements(double a[], unsigned length) {
int i;
double result = 0;
for (i = 0; i <= length - 1; ++i) /* length=0 時災難 */
result += a[i];
return result;
}
分析:`length` 為 `unsigned`,`length - 1` 亦為 `unsigned`。當 `length = 0`,`0 - 1` 下溢為 `UINT_MAX` (4294967295)。
`i` (signed) 與 `UINT_MAX` 比較,`i` 隱式轉 `unsigned`,條件 `i <= UINT_MAX` 恆真 → 迴圈無限執行 → 陣列越界崩潰。
修正:改用 `i < length` 或將 `length` 宣告為 `size_t` 並配合 `i < length`,避免減 1 操作。
防禦性寫法:避免有號/無號混合、邊界條件用 `< length` 而非 `<= length-1`、關鍵處顯式轉型。