有符號整數溢出在 C/C++ 中屬於 未定義行為,編譯器可假設溢出不發生並進行激進優化,導致看似合理的檢測邏輯失效。本文以《程序猿之---C語言細節7》為基礎,系統梳理溢出檢測的演進邏輯。
int a = 123456789, b = 2147483000;
// 錯誤寫法
if (a + b < 0) printf("overflow\n");
失效原因:當目標架構的加法指令不設定符號標誌(如某些 DSP、或啟用 -fwrapv 以外的優化),編譯器可能直接優化掉判斷,或溢出時標誌位為「溢出」而非「負號」,導致條件不成立。
#include
if ((unsigned)a + (unsigned)b > INT_MAX) {
printf("overflow\n");
}
原理:無符號整數運算在 C 標準中定義為模 2^N 環繞,無未定義行為。將兩個運算元強制轉為 unsigned 後相加,結果若超過 INT_MAX 必定發生環繞,此時無符號值會小於任一運算元,但直接與 INT_MAX 比較更直觀:(unsigned)a + (unsigned)b 的數學值等於真實和,若真實和 > INT_MAX,則無符號結果也 > INT_MAX(因為無符號最大值 UINT_MAX >= INT_MAX)。
if (a > INT_MAX - b) {
printf("overflow\n");
}
優勢:完全在有符號範圍內運算,無需類型轉換,邏輯等價於 a + b > INT_MAX 但規避了溢出風險。同理,檢測下溢:a < INT_MIN - b。
#include
#include
bool safe_add_int(int a, int b, int* result) {
if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
return false; // 溢出
}
*result = a + b;
return true;
}
__builtin_add_overflow(a, b, &result) 返回是否溢出,生成高效指令(如 x86 的 jo 跳轉)。-fsanitize=undefined,運行時自動捕獲有符號溢出、移位超寬等 UB。整數溢出檢測的核心原則是 「先判斷,後運算」。推薦優先使用編譯器內建函數,次選減法比較法,嚴禁依賴未定義行為的事後檢查。養成使用 int32_t/uint32_t 等固定寬度類型的習慣,配合靜態分析工具,從源頭消除隱患。