C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言数据溢出与精度丢失

C语言中数据溢出与精度丢失的解决方案

作者:学逆向的

还在为C语言整数溢出头疼,本文用short和unsigned int实例,带你彻底搞懂有符号溢出是未定义行为、无符号溢出为何会模2^n回绕,还有movsx与movzx指令的区别,避免优化后代码结果突变,需要的朋友可以参考下

有符号 short 溢出(有符号整数溢出属于未定义行为)

#include <stdio.h>
int main() {
    short num = 0x7FFF;
    printf("%hd \r\n", num);
    num = 0x8000;
    printf("%hd \r\n", num);
    return 0;
}
  1. short:2 字节 = 16bit,默认signed有符号
    ○ 范围:-2^15 ~ 2^15-1,即 -32768 ~ 32767
    ○ 0x7FFF:二进制0111 1111 1111 1111 → 32767(最大值)
    ○ 0x8000:二进制1000 0000 0000 0000 → -32768(最小值)
  2. 调试自增演示
short num = 32767;
num++;   // 32767 +1,二进制进位,最高位变成1
printf("%hd", num); // 输出 -32768

● 汇编:movsx eax, word ptr [num]
● movsx:符号位扩展,把 16 位 short 扩展到 32 位 eax
● 最高位是 1,代表负数;最高位 0 代表正数
⚠️重点:C 标准里有符号整数溢出是 UB(未定义行为),只是在 VC/VS 编译器下会按补码回绕,不同编译器 / 优化等级结果可能不一样

无符号 unsigned int 溢出(标准定义:模 2^n 取余,行为确定)

#include <stdio.h>
int main() {
    unsigned int num = 4294967295;
    printf("%u \r\n", num);
    num++;
    printf("%u \r\n", num);
    return 0;
}
  1. unsigned int:4 字节 = 32bit,无符号
    ○ 范围:0 ~2^32-1,最大值0xFFFFFFFF = 4294967295
    ○ 0xFFFFFFFF + 1 = 0x100000000
    ○ 只有 32 位存储空间,丢弃进位的最高位 1,剩余0x00000000,结果 = 0
    ✅重点:无符号整数溢出是 C 标准明确定义的,自动对2^32取模,一定会回绕
    反汇编核心片段解析
mov dword ptr [num],0FFFFFFFFh  ; 给num赋值0xFFFFFFFF
mov eax,dword ptr [num]
push eax
push offset string "%u \r\n"
call _printf
add esp,8

mov eax,dword ptr [num]
add eax,1          ; eax = eax + 1
mov dword ptr [num],eax  ; 写回栈,超出32bit的进位直接丢弃

● add eax,1 加法指令本身会产生进位标志 CF,但存储到 32 位变量时,超出的进位不会保存,直接截断

对比总结表

类型字节溢出行为C 标准规定
signed short232767+1 → -32768未定义行为 UB,仅 VC 下补码回绕
unsigned int44294967295+1 → 0确定,模2^32截断

补充易错点

  1. 不要依赖有符号数溢出回绕写代码,开启 O2 优化后结果可能完全改变
  2. 溢出本质:固定 bit 宽度,加法进位超出存储空间,发生截断;有符号还要额外解释最高位为符号位
  3. movsx(符号扩展)vs movzx(零扩展)
    ○ movsx:有符号类型扩展,高位补符号位
    ○ movzx:无符号类型扩展,高位补 0

以上就是C语言中数据溢出与精度丢失的解决方案的详细内容,更多关于C语言数据溢出与精度丢失的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文