首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言問答

C語言整數溢出檢測:從經驗到工程化方案

     編輯:C 語言問答

為什麼編譯器不報錯?

C 標準規定 有符號整數溢出屬於未定義行為(UB),編譯器可假定溢出不發生從而優化代碼;無符號整數溢出則定義為模 2ⁿ 環繞。因此編譯器默認不插入檢查指令,全靠程序員保證。

經驗法則(新手階段)

  • 預估最大值:int 約 ±21 億,long long 約 ±9×10¹⁸。
  • 乘法前檢查:if (a > INT_MAX / b) overflow();
  • 加法前檢查:if (b > 0 && a > INT_MAX - b) overflow();
  • 使用更寬類型:long long sum = (long long)a + b; 再判斷。

工程化檢測方案

1. GCC/Clang 內建函數(推薦)

// 編譯時加 -fsanitize=undefined 可運行期捕獲
#include <limits.h>

bool safe_add(int a, int b, int *res) {
    if (__builtin_add_overflow(a, b, res)) return false;
    return true;
}

bool safe_mul(int a, int b, int *res) {
    if (__builtin_mul_overflow(a, b, res)) return false;
    return true;
}

✅ 零成本、編譯器優化友好、支援所有整數類型。

2. 手寫通用宏(可移植)

#define SAFE_ADD(t, a, b, res) \
    ((b) >= 0 ? (a) <= (t##_MAX - (b)) : (a) >= (t##_MIN - (b))) ? \
    (*(res) = (a)+(b), 1) : 0

#define SAFE_MUL(t, a, b, res) \
    ((a) == 0 || (b) == 0) ? (*(res)=0,1) : \
    ((a) > 0 ? ((b) > 0 ? (a) <= (t##_MAX/(b)) : (b) >= (t##_MIN/(a))) : \
               ((b) > 0 ? (a) >= (t##_MIN/(b)) : (a) != 0 && (b) >= (t##_MAX/(a)))) ? \
    (*(res)=(a)*(b),1) : 0

// 使用
int r;
if (SAFE_ADD(INT, a, b, &r)) printf("和=%d\n", r);
else puts("溢出!");

3. 運行期檢測工具

  • UBSan:gcc -fsanitize=undefined -g,運行時自動報告溢出位置。
  • Valgrind:valgrind --tool=memcheck ./prog(主要查內存錯誤,配合 UBSan 更強)。
  • Clang Static Analyzer:scan-build make 靜態分析潛在溢出路徑。

實戰案例:階乘溢出防護

#include <stdio.h>
#include <limits.h>
#include <stdbool.h>

bool factorial_safe(int n, long long *out) {
    *out = 1;
    for (int i = 2; i <= n; i++) {
        if (__builtin_mul_overflow(*out, i, out)) return false;
    }
    return true;
}

int main() {
    long long r;
    for (int n = 1; n <= 25; n++) {
        if (factorial_safe(n, &r))
            printf("%2d! = %lld\n", n, r);
        else {
            printf("%2d! 溢出 (超過 %lld)\n", n, LLONG_MAX);
            break;
        }
    }
    return 0;
}

輸出顯示 20! 仍在 long long 範圍,21! 溢出。

最佳實踐清單

  1. 優先用內建函數:__builtin_*_overflow 覆蓋加減乘除移位。
  2. 關鍵路徑加 UBSan:CI 流程跑測試時開啟 -fsanitize=undefined。
  3. 避免有符號溢出 UB:改用無符號類型或顯式檢查。
  4. 文檔化範圍假設:函數註釋標明輸入參數合法範圍。
  5. 單元測試邊界值:INT_MIN, INT_MAX, 0, ±1 必測。

總結

C 語言整數溢出「不報錯」是語言設計權衡。現代開發應 「編譯期靜態分析 + 運行期 UBSan + 關鍵處內建檢查」 三層防護,而非靠經驗裸奔。掌握 __builtin_*_overflow 系列是 C 程序員進階的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved