型別轉換是 C 語言靜態型別系統中最微妙也最易產生 Bug 的領域。從簡單的 (double)sum / count 到複雜的 unsigned int + signed int 混合運算,編譯器遵循嚴格的「整數提升 → 算術轉換」規則。本文系統梳理隱式/顯式轉換機制,揭示經典陷阱,提供防禦性程式設計指南。
(type_name) expression
經典用例:整數除法產生浮點數結果
int sum = 17, count = 5;
double mean = (double)sum / count; // 3.4
// 錯誤寫法:double mean = sum / count; // 結果 3.0 (整數除法)
優先級提醒:轉換運算子優先級 高於 乘除。(double)sum / count 等價於 ((double)sum) / count。
小於 int 的整數型別(char、short、bool、位欄)自動提升為 int 或 unsigned int。
char c = 'c'; // ASCII 99
int i = 17;
int sum = i + c; // c 提升為 int(99),sum = 116
雙運算元經整數提升後,若型別不同,依照等級從高到低轉換:
long double → double → float → unsigned long long → long long → unsigned long → long → unsigned int → int
關鍵規則:
unsigned 等級高於對應 signedunsigned 勝出
int a = -1;
unsigned int b = 1;
if (a > b) printf("a > b"); // 輸出 "a > b"!
// 原因:a 轉為 unsigned int → 4294967295 (假設 32-bit)
防禦:編譯器警告 -Wsign-compare,或統一使用 int64_t 進行比較。
32767 + 1 // int (若 16-bit int 則溢位)
32767u + 1 // unsigned int
2147483647 + 1 // long 或 long long (視平台)
2147483647u + 1 // unsigned long
unsigned char c = 0xFF;
~c // 結果非 0x00!c 先提升為 int(0x000000FF),~ 得 0xFFFFFF00
// 正確:(unsigned char)~c 或 ~c & 0xFF
| 轉換類型 | 允許性 | 風險 |
|---|---|---|
void* ↔ 物件指標 | 隱式允許 | 低 |
| 物件指標 ↔ 不同型別物件指標 | 需強制轉換 | 嚴格別名違規、對齊錯誤 |
函式指標 ↔ void* | 標準禁止 | POSIX 允許 (dlsym),但非可攜 |
| 整數 ↔ 指標 | 需 intptr_t/uintptr_t | 資訊遺失、未定義行為 |
-Wall -Wextra -Wconversion -Wsign-conversion (GCC/Clang) / /W4 (MSVC)。<stdint.h> 的 int32_t, uint64_t 替代 int/long,消除平台差異。signed 或 unsigned;需混合時顯式轉換並加註釋。(int)3.9、四捨五入 lround(3.9)、向下取整 floor(3.9)。__attribute__((may_alias)) 或 union 類型雙關。assert(value >= INT_MIN && value <= INT_MAX); 在縮窄轉換前驗證範圍。[[nodiscard]] 屬性可標記轉換函式,防止忽略回傳值。stdckdint.h 提供檢查溢位的整數運算函式(ckd_add 等)。typeof() / typeof_unqual() 簡化泛型巨集中的型別推導。C 語言型別轉換規則雖繁複,但遵循「整數提升 → 算術轉換」雙階段模型即可推導 99% 場景。真正的專業在於:將隱式轉換顯式化、將平台相關型別固定寬度化、將危險轉換隔離封裝化。配合現代編譯器的靜態分析能力,型別轉換 Bug 完全可在編譯期消滅,讓執行期專注於業務邏輯。