有符號(`signed`)與無符號(`unsigned`)整數混合運算是 C/C++ 最隱蔽的 Bug 來源之一。編譯器會**靜默**將有符號運算元轉為無符號,導致負數變成巨大正數,比較/運算結果違背直覺。本文系統梳理轉換規則、經典陷阱、編譯器警告與防禦性編碼習慣。
**實務口訣**:「見 `unsigned` 皆轉 `unsigned`」,32/64 位平台上 `int`/`long`/`long long` 與對應 `unsigned` 混合時,有符號**總是**轉無符號。
unsigned int a = 20;
int b = -130;
if (a > b) // b 隱式轉為 unsigned → 4294967166
puts("a > b"); // ✅ 執行這裡!但邏輯錯誤
else
puts("b >= a"); // 直覺以為會進這裡
for (unsigned int i = 10; i >= 0; --i) // ❌ 無限循環!
process(i);
// i 為 unsigned,i >= 0 永真;i-- 到 0 後變 UINT_MAX
修正:`for (int i = 10; i >= 0; --i)` 或 `for (size_t i = 10; i-- > 0; )`
int findIndex(const vector<int>& v, int val) {
for (size_t i = 0; i < v.size(); ++i) // v.size() 返回 size_t (unsigned)
if (v[i] == val) return i;
return -1; // 調用端若用 unsigned 接收 → 巨大正數
}
cout << (1u > -2) << endl; // 1 (true) -2 → unsigned
cout << (1 > -2) << endl; // 1 (true) 皆 signed
cout << (1u > -2.0) << endl; // 1 (true) float 無 unsigned,有符號比較
| 工具 | 選項/規則 | 檢測內容 |
|---|---|---|
| GCC/Clang | -Wsign-compare | 有符號/無符號比較警告(-Wall 包含) |
| GCC/Clang | -Wsign-conversion | 隱式符號轉換警告(更嚴格) |
| MSVC | /W4 | C4018 (signed/unsigned mismatch) |
| Cppcheck | --enable=warning | 比較/轉換/循環邊界 |
| Clang-Tidy | bugprone-signed-char-misuse | char 符號性假設 |
if (static_cast<long>(unsignedVal) > signedVal) // 明確意圖
if (signedVal >= 0 && static_cast<unsigned>(signedVal) < unsignedVal)
for (int i = 0, n = static_cast<int>(vec.size()); i < n; ++i) // 確保 n 非負
for (auto it = vec.begin(); it != vec.end(); ++it) // 迭代器最安全
`float`/`double` **無無符號版本**,混合整數時整數轉浮點,比較語義符合數學直覺(`-2.0 < 1u` 為真)。但浮點精度問題另當別論。
「混合即轉無符號,負數變巨正;循環勿用無符號,轉型要顯式;警告全開啟,靜態查不漏。」