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

C語言整型提升與溢出陷阱:從一個乘法看編譯器行為

     編輯:關於C語言

看似簡單的乘法:long long res = num * num 為什麼會溢出?

現象:long num = 203879; long long res = num * num; 結果錯誤(溢出),但 long long n = 203879; n * n 正確。

根本原因:整型提升規則

C 標準規定:運算在賦值前完成,運算類型由操作數決定,不受左值影響。

  • num 是 long(4 字節),num * num 以 long 類型計算 → 32 位溢出
  • 溢出後的結果再轉為 long long 存入 → 錯誤值被保留

匯編級證據

// 情況1:字面量直接相乘(編譯期常量折疊)
movl $0xad90b171, 0x18(%esp)   // 低32位
movl $0xffffffff, 0x1c(%esp)   // 高32位符號擴展 → 錯誤!

// 情況2:long 變量相乘
mov 0xc(%esp), %eax            // 載入 num
imul 0xc(%esp), %eax           // 32位有符號乘法 → 溢出
sar $0x1f, %edx                // 符號擴展到 edx
mov %eax, 0x20(%esp)           // 存低32位
mov %edx, 0x24(%esp)           // 存高32位(全1) → 錯誤!

// 情況3:long long 變量相乘
mov 0x14(%esp), %eax           // 載入高32位(0)
mov 0x10(%esp), %ecx           // 載入低32位
imul 0x10(%esp), %ecx          // 64位乘法指令序列 → 正確!

三種寫法對比

修正方案

// 方案1:強制提升
long long res = 1LL * num * num;

// 方案2:變量定義為 long long
long long num = 203879;
long long res = num * num;

// 方案3:轉型
long long res = (long long)num * num;

核心原則

  1. 運算類型由操作數最高等級決定,左值類型不參與運算類型推導
  2. 字面量類型:無後綴為 int,LL 為 long long
  3. 隱式轉換發生在運算前,而非賦值時
  4. 嵌入式開發中務必顯式指定運算精度

延伸:有符號溢出是未定義行為

C 標準規定有符號整數溢出為 UB,編譯器可假設不發生溢出進行優化,導致不可預測結果。無符號溢出則定義為模 2^n 環繞。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
代碼運算類型結果
203879 * 203879int (編譯期常量折疊)溢出
long n = 203879; n * nlong (32位)溢出
long long n = 203879; n * nlong long (64位)正確
long n = 203879; 1LL * n * nlong long (提升)正確