在 C 語言開發中,常數的資料類型往往被開發者忽視,但這卻是導致整數溢出、運算結果錯誤的常見根因。編譯器預設將無後綴的整數常數視為 int(通常為 32 位),若運算中間結果超出 32 位有號整數範圍(-2,147,483,648 ~ 2,147,483,647),便會發生溢出,導致結果錯誤甚至未定義行為。
以下程式碼示範了這個經典問題:
// 編譯環境:CodeBlocks + GCC
#include <stdio.h>
#include <stdint.h>
int Fun() {
uint64_t y;
uint32_t x1, x2;
// ❌ 錯誤寫法:常數預設為 32 位,中間結果 72,000,000,000 超出 32 位範圍,發生溢出
// y = 3000 * 24000000 / 1000;
// ✅ 寫法一:強制型別轉換,將常數提升為 64 位再運算
// y = (uint64_t)3000 * (uint64_t)24000000 / 1000;
// ✅ 寫法二(推薦):使用 ULL 後綴明確指定 64 位無號長整數常數
y = 3000ULL * 24000000ULL / 1000ULL;
printf("0x%llX\n", y); // 輸出完整 64 位結果
// 將 64 位結果拆分為高低 32 位顯示
x1 = (uint32_t)(y & 0xFFFFFFFF);
x2 = (uint32_t)(y >> 32);
printf("0x%X, 0x%X\n", x1, x2);
}
int main() {
Fun();
return 0;
}
int → long → long long(依值大小自動選擇最小能容納的類型)U/u 後綴:無號類型(unsigned int → unsigned long → unsigned long long)L/l 後綴:長整數(long → long long)LL/ll 後綴:長長整數(long long,C99 標準)ULL/ull:unsigned long long(64 位無號)ULL 就能解決?當常數帶有 ULL 後綴時,編譯器直接將其視為 64 位無號整數。根據「整數提升」與「尋常算術轉換」規則,參與運算的另一個運算元也會被提升為 64 位,因此整個運算過程都在 64 位寬度下進行,避免中間結果溢出。
兩種寫法效果相同,但 後綴法更優雅、可讀性更高,且不易因漏轉某個運算元而出錯。建議養成習慣:涉及大數運算時,直接在常數上加上 ULL(或 LL)後綴。
| 後綴 | 類型 | 適用場景 |
|---|---|---|
| (無) | int / long / long long | 一般小整數 |
U / u | unsigned int / unsigned long / unsigned long long | 無號數值、位運算 |
L / l | long / long long | 較大有號整數 |
LL / ll | long long (C99) | 64 位有號整數 |
ULL / ull | unsigned long long | 大數運算、避免溢出(推薦) |
ULL(無號)或 LL(有號)。stdint.h:使用 uint64_t、int64_t 等固定寬度類型,增強跨平台可移植性。-Woverflow(GCC/Clang)或 /W4(MSVC),讓編譯器幫你發現潛在溢出。掌握常數類型規則,是寫出健壯、無溢出 C 程式的基礎功夫之一。下次寫大數運算時,別忘了給常數加上 ULL「護身符」!