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; 再判斷。// 編譯時加 -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;
}
✅ 零成本、編譯器優化友好、支援所有整數類型。
#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("溢出!");
gcc -fsanitize=undefined -g,運行時自動報告溢出位置。valgrind --tool=memcheck ./prog(主要查內存錯誤,配合 UBSan 更強)。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! 溢出。
__builtin_*_overflow 覆蓋加減乘除移位。-fsanitize=undefined。INT_MIN, INT_MAX, 0, ±1 必測。C 語言整數溢出「不報錯」是語言設計權衡。現代開發應 「編譯期靜態分析 + 運行期 UBSan + 關鍵處內建檢查」 三層防護,而非靠經驗裸奔。掌握 __builtin_*_overflow 系列是 C 程序員進階的必修課。