C 数据溢出与精度丢失 有符号 short 溢出有符号整数溢出属于未定义行为#includestdio.hintmain(){shortnum0x7FFF;printf(%hd \r\n,num);num0x8000;printf(%hd \r\n,num);return0;}short2 字节 16bit默认signed有符号○ 范围-2^15 ~ 2^15-1即 -32768 ~ 32767○ 0x7FFF二进制0111 1111 1111 1111 → 32767最大值○ 0x8000二进制1000 0000 0000 0000 → -32768最小值调试自增演示shortnum32767;num;// 32767 1二进制进位最高位变成1printf(%hd,num);// 输出 -32768● 汇编movsx eax, word ptr [num]● movsx符号位扩展把 16 位 short 扩展到 32 位 eax● 最高位是 1代表负数最高位 0 代表正数⚠️重点C 标准里有符号整数溢出是 UB未定义行为只是在 VC/VS 编译器下会按补码回绕不同编译器 / 优化等级结果可能不一样无符号 unsigned int 溢出标准定义模 2^n 取余行为确定#includestdio.hintmain(){unsignedintnum4294967295;printf(%u \r\n,num);num;printf(%u \r\n,num);return0;}unsigned int4 字节 32bit无符号○ 范围0 ~2^32-1最大值0xFFFFFFFF 4294967295○ 0xFFFFFFFF 1 0x100000000○ 只有 32 位存储空间丢弃进位的最高位 1剩余0x00000000结果 0✅重点无符号整数溢出是 C 标准明确定义的自动对2^32取模一定会回绕反汇编核心片段解析mov dword ptr[num],0FFFFFFFFh;给num赋值0xFFFFFFFFmov eax,dword ptr[num]push eax push offset string%u \r\ncall _printf add esp,8mov eax,dword ptr[num]add eax,1;eaxeax1mov dword ptr[num],eax;写回栈超出32bit的进位直接丢弃● add eax,1 加法指令本身会产生进位标志 CF但存储到 32 位变量时超出的进位不会保存直接截断对比总结表类型字节溢出行为C 标准规定signed short2327671 → -32768未定义行为 UB仅 VC 下补码回绕unsigned int442949672951 → 0确定模2^32截断补充易错点不要依赖有符号数溢出回绕写代码开启 O2 优化后结果可能完全改变溢出本质固定 bit 宽度加法进位超出存储空间发生截断有符号还要额外解释最高位为符号位movsx符号扩展vs movzx零扩展○ movsx有符号类型扩展高位补符号位○ movzx无符号类型扩展高位补 0