当前位置: 首页 > news >正文

算术操作符 逆向汇编二

文章目录

    • 有符号除法
      • 代码
      • 汇编
      • ​**​汇编代码分析(有符号除法,32位 x86,MSVC 编译器)​**​
    • ​**​1. 函数入口和栈帧初始化​**​
    • ​**​2. 正数除法(`a / b`)​**​
    • ​**​3. 负数除法(`c / d`和 `e / f`)​**​
      • ​**​(1) `c / d`(-10 / 3 = -3)​**​
      • ​**​(2) `e / f`(10 / -3 = -3)​**​
    • ​**​4. 函数返回​**​
    • ​**​关键结论​**​
      • ​**​优化对比​**​
    • 无符号除法
      • 代码
      • 汇编
      • ​**​汇编代码分析(无符号除法,32位 x86,MSVC 编译器)​**​
    • ​**​1. 函数入口和栈帧初始化​**​
    • ​**​2. 无符号除法(`a / b`和 `c / d`)​**​
      • ​**​(1) `a / b`(10 / 3 = 3)​**​
      • ​**​(2) `c / d`(15 / 4 = 3)​**​
    • ​**​3. 函数返回​**​
    • ​**​关键结论​**​
      • ​**​优化对比​**​
      • ​**​总结​**​
    • 有符号取模
      • 代码
      • 汇编代码
      • ​**​汇编代码分析(有符号取模运算,32位 x86,MSVC 编译器)​**​
    • ​**​1. 函数入口和栈帧初始化​**​
    • ​**​2. 有符号取模运算(`idiv`+ `edx`存储余数)​**​
      • ​**​(1) `a % b`(10 % 3 = 1)​**​
      • ​**​(2) `c % d`(-10 % 3 = -1)​**​
      • ​**​(3) `e % f`(10 % -3 = 1)​**​
      • ​**​(4) `g % h`(-10 % -3 = -1)​**​
    • ​**​3. 函数返回​**​
    • ​**​关键结论​**​
      • ​**​总结​**​
    • 无符号取模
      • 代码
      • 汇编
      • ​**​汇编代码分析(无符号取模运算,32位 x86,MSVC 编译器)​**​
    • ​**​1. 函数入口和栈帧初始化​**​
    • ​**​2. 无符号取模运算(`div`+ `edx`存储余数)​**​
      • ​**​(1) `a % b`(10 % 3 = 1)​**​
      • ​**​(2) `c % d`(15 % 4 = 3)​**​
    • ​**​3. 函数返回​**​
    • ​**​关键结论​**​
      • ​**​总结​**​

有符号除法

代码

#include <stdio.h>int main() {// 正数除法int a = 10, b = 3;int signed_div1 = a / b;  // 10 / 3 = 3// 负数除法(向零截断)int c = -10, d = 3;int signed_div2 = c / d;  // -10 / 3 = -3int e = 10, f = -3;int signed_div3 = e / f;  // 10 / -3 = -3return 0;
}

汇编


int main() {
00412590  push        ebp  
00412591  mov         ebp,esp  
00412593  sub         esp,12Ch  
00412599  push        ebx  
0041259A  push        esi  
0041259B  push        edi  
0041259C  lea         edi,[ebp-6Ch]  
0041259F  mov         ecx,1Bh  
004125A4  mov         eax,0CCCCCCCCh  
004125A9  rep stos    dword ptr es:[edi]  
004125AB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
004125B0  call        @__CheckForDebuggerJustMyCode@4 (041131Bh)  
004125B5  nop  // 正数除法int a = 10, b = 3;
004125B6  mov         dword ptr [a],0Ah  
004125BD  mov         dword ptr [b],3  int signed_div1 = a / b;  // 10 / 3 = 3
004125C4  mov         eax,dword ptr [a]  
004125C7  cdq  
004125C8  idiv        eax,dword ptr [b]  
004125CB  mov         dword ptr [signed_div1],eax  // 负数除法(向零截断)int c = -10, d = 3;
004125CE  mov         dword ptr [c],0FFFFFFF6h  
004125D5  mov         dword ptr [d],3  int signed_div2 = c / d;  // -10 / 3 = -3
004125DC  mov         eax,dword ptr [c]  
004125DF  cdq  
004125E0  idiv        eax,dword ptr [d]  
004125E3  mov         dword ptr [signed_div2],eax  int e = 10, f = -3;
004125E6  mov         dword ptr [e],0Ah  
004125ED  mov         dword ptr [f],0FFFFFFFDh  int signed_div3 = e / f;  // 10 / -3 = -3
004125F4  mov         eax,dword ptr [e]  
004125F7  cdq  
004125F8  idiv        eax,dword ptr [f]  
004125FB  mov         dword ptr [signed_div3],eax  return 0;
004125FE  xor         eax,eax  
}
00412600  pop         edi  
00412601  pop         esi  
00412602  pop         ebx  
00412603  add         esp,12Ch  
00412609  cmp         ebp,esp  
0041260B  call        __RTC_CheckEsp (041123Fh)  
00412610  mov         esp,ebp  
00412612  pop         ebp  
00412613  ret  

​汇编代码分析(有符号除法,32位 x86,MSVC 编译器)​

这段代码演示了 ​​C 语言中的有符号整数除法​​,并通过汇编展示了 idiv指令的使用方式。以下是详细分析:


​1. 函数入口和栈帧初始化​

00412590  push        ebp             ; 保存旧的基址指针
00412591  mov         ebp,esp         ; 设置新的栈帧
00412593  sub         esp,12Ch        ; 分配栈空间 (0x12C 字节)
00412599  push        ebx             ; 保存寄存器
0041259A  push        esi  
0041259B  push        edi  
0041259C  lea         edi,[ebp-6Ch]   ; 初始化栈空间起始地址
0041259F  mov         ecx,1Bh         ; 循环次数 (27次)
004125A4  mov         eax,0CCCCCCCCh  ; 调试模式填充值 (0xCCCCCCCC)
004125A9  rep stos    dword ptr es:[edi] ; 用 0xCCCCCCCC 填充栈
004125AB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
004125B0  call        @__CheckForDebuggerJustMyCode@4 (041131Bh) ; 调试检查
004125B5  nop                          ; 空操作(对齐)
  • sub esp, 12Ch​:为局部变量分配栈空间(abcdef等)。

  • rep stos​:在调试模式下用 0xCCCCCCCC填充栈(检测未初始化内存)。

  • @__CheckForDebuggerJustMyCode@4​:调试模式下的安全检查(MSVC 特有)。


​2. 正数除法(a / b)​

004125B6  mov         dword ptr [a],0Ah      ; a = 10 (0xA)
004125BD  mov         dword ptr [b],3        ; b = 3
004125C4  mov         eax,dword ptr [a]      ; eax = a
004125C7  cdq                                ; 扩展 eax -> edx:eax(符号扩展)
004125C8  idiv        eax,dword ptr [b]      ; eax = edx:eax / b,edx = 余数
004125CB  mov         dword ptr [signed_div1],eax ; signed_div1 = eax
  • cdq指令​​:

    • eax符号扩展到 edx:eaxedx填充 eax的符号位)。

    • 例如:

      • 如果 eax = 10(正数),则 edx = 0

      • 如果 eax = -10(负数),则 edx = 0xFFFFFFFF

  • idiv指令​​:

    • 计算 edx:eax / [b],商存储在 eax,余数存储在 edx

    • 10 / 3 = 3​(商 eax = 3,余数 edx = 1)。


​3. 负数除法(c / de / f)​

​(1) c / d(-10 / 3 = -3)​

004125CE  mov         dword ptr [c],0FFFFFFF6h ; c = -10 (补码: 0xFFFFFFF6)
004125D5  mov         dword ptr [d],3        ; d = 3
004125DC  mov         eax,dword ptr [c]      ; eax = c (-10)
004125DF  cdq                                ; edx:eax = 0xFFFFFFFF:0xFFFFFFF6
004125E0  idiv        eax,dword ptr [d]      ; eax = -10 / 3 = -3
004125E3  mov         dword ptr [signed_div2],eax ; signed_div2 = -3
  • cdq​:

    • eax = 0xFFFFFFF6(-10),所以 edx = 0xFFFFFFFF(符号扩展)。
  • idiv​:

    • edx:eax = 0xFFFFFFFF:0xFFFFFFF6(即 -10)。

    • -10 / 3 = -3(商 eax = -3,余数 edx = -1)。

​(2) e / f(10 / -3 = -3)​

004125E6  mov         dword ptr [e],0Ah      ; e = 10 (0xA)
004125ED  mov         dword ptr [f],0FFFFFFFDh ; f = -3 (补码: 0xFFFFFFFD)
004125F4  mov         eax,dword ptr [e]      ; eax = 10
004125F7  cdq                                ; edx:eax = 0:10
004125F8  idiv        eax,dword ptr [f]      ; eax = 10 / -3 = -3
004125FB  mov         dword ptr [signed_div3],eax ; signed_div3 = -3
  • cdq​:

    • eax = 10(正数),所以 edx = 0
  • idiv​:

    • edx:eax = 0:10(即 10)。

    • 10 / -3 = -3(商 eax = -3,余数 edx = 1)。


​4. 函数返回​

004125FE  xor         eax,eax          ; eax = 0(返回值)
00412600  pop         edi              ; 恢复寄存器
00412601  pop         esi  
00412602  pop         ebx  
00412603  add         esp,12Ch          ; 释放栈空间
00412609  cmp         ebp,esp          ; 检查栈平衡
0041260B  call        __RTC_CheckEsp   ; 调试模式栈检查
00412610  mov         esp,ebp          ; 恢复 esp
00412612  pop         ebp              ; 恢复 ebp
00412613  ret                          ; 返回
  • xor eax, eax​:设置返回值 0(C 语言的 return 0)。

  • __RTC_CheckEsp​:调试模式下检查栈是否平衡(防止栈溢出)。


​关键结论​

  1. ​有符号除法的汇编实现​​:

    • cdq​:符号扩展 eaxedx:eax(准备 64 位被除数)。

    • idiv​:计算 edx:eax / src,商存储在 eax,余数存储在 edx

  2. ​除法结果的截断方式​​:

    • C 语言的有符号除法 ​​向零截断​​(-10 / 3 = -310 / -3 = -3)。

    • 余数的符号与被除数相同(-10 % 3 = -110 % -3 = 1)。

  3. ​调试模式的影响​​:

    • 额外的栈填充 (0xCCCCCCCC) 和检查 (__RTC_CheckEsp) 会增加开销。

​优化对比​

如果开启编译器优化(如 -O2),未使用的变量会被删除,代码可能简化为:

main:xor eax, eax    ; return 0ret

但除法运算的逻辑在未优化模式下已经非常清晰。

无符号除法

代码

#include <stdio.h>int main() {unsigned int a = 10, b = 3;unsigned int unsigned_div1 = a / b;  // 10 / 3 = 3unsigned int c = 15, d = 4;unsigned int unsigned_div2 = c / d;  // 15 / 4 = 3return 0;
}

汇编


int main() {
00412590  push        ebp  
00412591  mov         ebp,esp  
00412593  sub         esp,108h  
00412599  push        ebx  
0041259A  push        esi  
0041259B  push        edi  
0041259C  lea         edi,[ebp-48h]  
0041259F  mov         ecx,12h  
004125A4  mov         eax,0CCCCCCCCh  
004125A9  rep stos    dword ptr es:[edi]  
004125AB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
004125B0  call        @__CheckForDebuggerJustMyCode@4 (041131Bh)  
004125B5  nop  unsigned int a = 10, b = 3;
004125B6  mov         dword ptr [a],0Ah  
004125BD  mov         dword ptr [b],3  unsigned int unsigned_div1 = a / b;  // 10 / 3 = 3
004125C4  mov         eax,dword ptr [a]  
004125C7  xor         edx,edx  
004125C9  div         eax,dword ptr [b]  
004125CC  mov         dword ptr [unsigned_div1],eax  unsigned int c = 15, d = 4;
004125CF  mov         dword ptr [c],0Fh  
004125D6  mov         dword ptr [d],4  unsigned int unsigned_div2 = c / d;  // 15 / 4 = 3
004125DD  mov         eax,dword ptr [c]  
004125E0  xor         edx,edx  
004125E2  div         eax,dword ptr [d]  
004125E5  mov         dword ptr [unsigned_div2],eax  return 0;
004125E8  xor         eax,eax  
}

​汇编代码分析(无符号除法,32位 x86,MSVC 编译器)​

这段代码演示了 ​​C 语言中的无符号整数除法​​,并通过汇编展示了 div指令的使用方式。以下是详细分析:


​1. 函数入口和栈帧初始化​

00412590  push        ebp             ; 保存旧的基址指针
00412591  mov         ebp,esp         ; 设置新的栈帧
00412593  sub         esp,108h        ; 分配栈空间 (0x108 字节)
00412599  push        ebx             ; 保存寄存器
0041259A  push        esi  
0041259B  push        edi  
0041259C  lea         edi,[ebp-48h]   ; 初始化栈空间起始地址
0041259F  mov         ecx,12h         ; 循环次数 (18次)
004125A4  mov         eax,0CCCCCCCCh  ; 调试模式填充值 (0xCCCCCCCC)
004125A9  rep stos    dword ptr es:[edi] ; 用 0xCCCCCCCC 填充栈
004125AB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
004125B0  call        @__CheckForDebuggerJustMyCode@4 (041131Bh) ; 调试检查
004125B5  nop                          ; 空操作(对齐)
  • sub esp, 108h​:为局部变量分配栈空间(abcdunsigned_div1unsigned_div2等)。

  • rep stos​:在调试模式下用 0xCCCCCCCC填充栈(检测未初始化内存)。

  • @__CheckForDebuggerJustMyCode@4​:调试模式下的安全检查(MSVC 特有)。


​2. 无符号除法(a / bc / d)​

​(1) a / b(10 / 3 = 3)​

004125B6  mov         dword ptr [a],0Ah      ; a = 10 (0xA)
004125BD  mov         dword ptr [b],3        ; b = 3
004125C4  mov         eax,dword ptr [a]      ; eax = a
004125C7  xor         edx,edx                ; edx = 0(无符号扩展)
004125C9  div         eax,dword ptr [b]      ; eax = edx:eax / b(使用 div)
004125CC  mov         dword ptr [unsigned_div1],eax ; 存储商
  • xor edx, edx​:

    • 在无符号除法前,必须将 edx清零(因为无符号数的高位扩展是 0)。
  • div指令​​:

    • 计算 edx:eax / [b],商存储在 eax,余数存储在 edx

    • 10 / 3 = 3​(商 eax = 3,余数 edx = 1)。

​(2) c / d(15 / 4 = 3)​

004125CF  mov         dword ptr [c],0Fh      ; c = 15 (0xF)
004125D6  mov         dword ptr [d],4        ; d = 4
004125DD  mov         eax,dword ptr [c]      ; eax = c
004125E0  xor         edx,edx                ; edx = 0
004125E2  div         eax,dword ptr [d]      ; eax = edx:eax / d
004125E5  mov         dword ptr [unsigned_div2],eax ; 存储商
  • div指令​​:

    • 15 / 4 = 3(商 eax = 3,余数 edx = 3)。

​3. 函数返回​

004125E8  xor         eax,eax          ; eax = 0(返回值)
004125EA  pop         edi              ; 恢复寄存器
004125EB  pop         esi  
004125EC  pop         ebx  
004125ED  add         esp,108h          ; 释放栈空间
004125F3  cmp         ebp,esp          ; 检查栈平衡
004125F5  call        __RTC_CheckEsp   ; 调试模式栈检查
004125FA  mov         esp,ebp          ; 恢复 esp
004125FC  pop         ebp              ; 恢复 ebp
004125FD  ret                          ; 返回
  • xor eax, eax​:设置返回值 0(C 语言的 return 0)。

  • __RTC_CheckEsp​:调试模式下检查栈是否平衡(防止栈溢出)。


​关键结论​

  1. ​无符号除法的汇编实现​​:

    • xor edx, edx​:清零 edx(无符号数的高位扩展是 0)。

    • div指令​​:

      • 被除数:edx:eax(64 位)。

      • 除数:[b][d](32 位)。

      • 商:eax,余数:edx

  2. ​与有符号除法的区别​​:

    • 有符号除法使用 idiv,并通过 cdq进行符号扩展。

    • 无符号除法使用 div,并通过 xor edx, edx清零高位。

  3. ​调试模式的影响​​:

    • 额外的栈填充 (0xCCCCCCCC) 和检查 (__RTC_CheckEsp) 会增加开销。

​优化对比​

如果开启编译器优化(如 -O2),未使用的变量会被删除,代码可能简化为:

main:xor eax, eax    ; return 0ret

但除法运算的逻辑在未优化模式下已经非常清晰。


​总结​

场景指令关键操作注意事项
无符号除法(32位)divxor edx, edx+ div [src]必须清零 edx
有符号除法(32位)idivcdq+ idiv [src]cdq扩展符号位
防止优化volatile+ printf强制生成实际指令避免常量折叠和死代码删除

通过这种方式,可以确保编译器生成 div指令,从而准确观察无符号除法的汇编行为。

有符号取模

代码

#include <stdio.h>int main() {// 正数取模int a = 10, b = 3;int signed_mod1 = a % b;  // 10 % 3 = 1// 被除数为负数(结果符号与被除数相同)int c = -10, d = 3;int signed_mod2 = c % d;  // -10 % 3 = -1// 除数为负数(结果符号与被除数相同)int e = 10, f = -3;int signed_mod3 = e % f;  // 10 % -3 = 1// 两个都为负数int g = -10, h = -3;int signed_mod4 = g % h;  // -10 % -3 = -1return 0;
}

汇编代码


int main() {
004144E0  push        ebp  
004144E1  mov         ebp,esp  
004144E3  sub         esp,150h  
004144E9  push        ebx  
004144EA  push        esi  
004144EB  push        edi  
004144EC  lea         edi,[ebp-90h]  
004144F2  mov         ecx,24h  
004144F7  mov         eax,0CCCCCCCCh  
004144FC  rep stos    dword ptr es:[edi]  
004144FE  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
00414503  call        @__CheckForDebuggerJustMyCode@4 (041131Bh)  
00414508  nop  // 正数取模int a = 10, b = 3;
00414509  mov         dword ptr [a],0Ah  
00414510  mov         dword ptr [b],3  int signed_mod1 = a % b;  // 10 % 3 = 1
00414517  mov         eax,dword ptr [a]  
0041451A  cdq  
0041451B  idiv        eax,dword ptr [b]  
0041451E  mov         dword ptr [signed_mod1],edx  // 被除数为负数(结果符号与被除数相同)int c = -10, d = 3;
00414521  mov         dword ptr [c],0FFFFFFF6h  
00414528  mov         dword ptr [d],3  int signed_mod2 = c % d;  // -10 % 3 = -1
0041452F  mov         eax,dword ptr [c]  
00414532  cdq  
00414533  idiv        eax,dword ptr [d]  
00414536  mov         dword ptr [signed_mod2],edx  // 除数为负数(结果符号与被除数相同)int e = 10, f = -3;
00414539  mov         dword ptr [e],0Ah  
00414540  mov         dword ptr [f],0FFFFFFFDh  int signed_mod3 = e % f;  // 10 % -3 = 1
00414547  mov         eax,dword ptr [e]  
0041454A  cdq  
0041454B  idiv        eax,dword ptr [f]  
0041454E  mov         dword ptr [signed_mod3],edx  // 两个都为负数int g = -10, h = -3;
00414551  mov         dword ptr [g],0FFFFFFF6h  
00414558  mov         dword ptr [h],0FFFFFFFDh  int signed_mod4 = g % h;  // -10 % -3 = -1
0041455F  mov         eax,dword ptr [g]  
00414562  cdq  
00414563  idiv        eax,dword ptr [h]  
00414566  mov         dword ptr [signed_mod4],edx  return 0;
0041456C  xor         eax,eax  
}
0041456E  pop         edi  
0041456F  pop         esi  
00414570  pop         ebx  
00414571  add         esp,150h  
00414577  cmp         ebp,esp  
00414579  call        __RTC_CheckEsp (041123Fh)  
0041457E  mov         esp,ebp  
00414580  pop         ebp  
00414581  ret  

​汇编代码分析(有符号取模运算,32位 x86,MSVC 编译器)​

这段代码演示了 ​​C 语言中的有符号整数取模运算(%)​​,并通过汇编展示了 idiv指令如何计算余数。以下是详细分析:


​1. 函数入口和栈帧初始化​

004144E0  push        ebp             ; 保存旧的基址指针
004144E1  mov         ebp,esp         ; 设置新的栈帧
004144E3  sub         esp,150h        ; 分配栈空间 (0x150 字节)
004144E9  push        ebx             ; 保存寄存器
004144EA  push        esi  
004144EB  push        edi  
004144EC  lea         edi,[ebp-90h]   ; 初始化栈空间起始地址
004144F2  mov         ecx,24h        ; 循环次数 (36次)
004144F7  mov         eax,0CCCCCCCCh ; 调试模式填充值 (0xCCCCCCCC)
004144FC  rep stos    dword ptr es:[edi] ; 用 0xCCCCCCCC 填充栈
004144FE  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
00414503  call        @__CheckForDebuggerJustMyCode@4 (041131Bh) ; 调试检查
00414508  nop                          ; 空操作(对齐)
  • sub esp, 150h​:为局部变量分配栈空间(abcdefgh等)。

  • rep stos​:在调试模式下用 0xCCCCCCCC填充栈(检测未初始化内存)。

  • @__CheckForDebuggerJustMyCode@4​:调试模式下的安全检查(MSVC 特有)。


​2. 有符号取模运算(idiv+ edx存储余数)​

​(1) a % b(10 % 3 = 1)​

00414509  mov         dword ptr [a],0Ah      ; a = 10 (0xA)
00414510  mov         dword ptr [b],3        ; b = 3
00414517  mov         eax,dword ptr [a]      ; eax = a
0041451A  cdq                                ; 扩展 eax -> edx:eax(符号扩展)
0041451B  idiv        eax,dword ptr [b]      ; eax = 商, edx = 余数
0041451E  mov         dword ptr [signed_mod1],edx ; signed_mod1 = edx (余数)
  • cdq指令​​:

    • eax符号扩展到 edx:eaxedx填充 eax的符号位)。

    • 例如:

      • 如果 eax = 10(正数),则 edx = 0

      • 如果 eax = -10(负数),则 edx = 0xFFFFFFFF

  • idiv指令​​:

    • 计算 edx:eax / [b],商存储在 eax,余数存储在 edx

    • 10 / 3 = 3​(商 eax = 3,余数 edx = 1)。

    • ​取模运算的本质​​:直接取 idiv计算后的余数(edx)。

​(2) c % d(-10 % 3 = -1)​

00414521  mov         dword ptr [c],0FFFFFFF6h ; c = -10 (补码: 0xFFFFFFF6)
00414528  mov         dword ptr [d],3        ; d = 3
0041452F  mov         eax,dword ptr [c]      ; eax = c (-10)
00414532  cdq                                ; edx:eax = 0xFFFFFFFF:0xFFFFFFF6
00414533  idiv        eax,dword ptr [d]      ; eax = -3, edx = -1
00414536  mov         dword ptr [signed_mod2],edx ; signed_mod2 = -1
  • idiv计算​​:

    • -10 / 3 = -3(商 eax = -3),余数 edx = -1

    • ​余数的符号与被除数相同​​(-10 % 3 = -1)。

​(3) e % f(10 % -3 = 1)​

00414539  mov         dword ptr [e],0Ah      ; e = 10 (0xA)
00414540  mov         dword ptr [f],0FFFFFFFDh ; f = -3 (补码: 0xFFFFFFFD)
00414547  mov         eax,dword ptr [e]      ; eax = 10
0041454A  cdq                                ; edx:eax = 0:10
0041454B  idiv        eax,dword ptr [f]      ; eax = -3, edx = 1
0041454E  mov         dword ptr [signed_mod3],edx ; signed_mod3 = 1
  • idiv计算​​:

    • 10 / -3 = -3(商 eax = -3),余数 edx = 1

    • ​余数的符号与被除数相同​​(10 % -3 = 1)。

​(4) g % h(-10 % -3 = -1)​

00414551  mov         dword ptr [g],0FFFFFFF6h ; g = -10 (补码: 0xFFFFFFF6)
00414558  mov         dword ptr [h],0FFFFFFFDh ; h = -3 (补码: 0xFFFFFFFD)
0041455F  mov         eax,dword ptr [g]      ; eax = -10
00414562  cdq                                ; edx:eax = 0xFFFFFFFF:0xFFFFFFF6
00414563  idiv        eax,dword ptr [h]      ; eax = 3, edx = -1
00414566  mov         dword ptr [signed_mod4],edx ; signed_mod4 = -1
  • idiv计算​​:

    • -10 / -3 = 3(商 eax = 3),余数 edx = -1

    • ​余数的符号与被除数相同​​(-10 % -3 = -1)。


​3. 函数返回​

0041456C  xor         eax,eax          ; eax = 0(返回值)
0041456E  pop         edi              ; 恢复寄存器
0041456F  pop         esi  
00414570  pop         ebx  
00414571  add         esp,150h         ; 释放栈空间
00414577  cmp         ebp,esp          ; 检查栈平衡
00414579  call        __RTC_CheckEsp   ; 调试模式栈检查
0041457E  mov         esp,ebp          ; 恢复 esp
00414580  pop         ebp              ; 恢复 ebp
00414581  ret                          ; 返回
  • xor eax, eax​:设置返回值 0(C 语言的 return 0)。

  • __RTC_CheckEsp​:调试模式下检查栈是否平衡(防止栈溢出)。


​关键结论​

  1. ​有符号取模的实现​​:

    • 通过 idiv指令计算商和余数,​​余数存储在 edx​。

    • ​余数的符号与被除数相同​​(C 语言标准规定)。

  2. cdq的作用​​:

    • eax符号扩展到 edx:eax,确保 idiv正确处理负数。
  3. ​调试模式的影响​​:

    • 额外的栈填充 (0xCCCCCCCC) 和检查 (__RTC_CheckEsp) 会增加开销。
  4. ​与无符号取模的区别​​:

    • 无符号取模使用 div指令,且 xor edx, edx清零高位。

​总结​

运算指令关键操作余数规则
有符号取模(%idivcdq+ idiv [src]余数符号 = 被除数符号
无符号取模(%divxor edx, edx+ div [src]余数始终为正
防止优化volatile+ printf强制生成实际指令避免常量折叠和死代码删除

通过这段汇编代码,可以清晰看到有符号取模运算的实现逻辑,以及 idiv指令如何同时计算商和余数。

无符号取模

代码

#include <stdio.h>int main() {unsigned int a = 10, b = 3;unsigned int unsigned_mod1 = a % b;  // 10 % 3 = 1unsigned int c = 15, d = 4;unsigned int unsigned_mod2 = c % d;  // 15 % 4 = 3return 0;
}

汇编


int main() {
004144E0  push        ebp  
004144E1  mov         ebp,esp  
004144E3  sub         esp,108h  
004144E9  push        ebx  
004144EA  push        esi  
004144EB  push        edi  
004144EC  lea         edi,[ebp-48h]  
004144EF  mov         ecx,12h  
004144F4  mov         eax,0CCCCCCCCh  
004144F9  rep stos    dword ptr es:[edi]  
004144FB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
00414500  call        @__CheckForDebuggerJustMyCode@4 (041131Bh)  
00414505  nop  unsigned int a = 10, b = 3;
00414506  mov         dword ptr [a],0Ah  
0041450D  mov         dword ptr [b],3  unsigned int unsigned_mod1 = a % b;  // 10 % 3 = 1
00414514  mov         eax,dword ptr [a]  
00414517  xor         edx,edx  
00414519  div         eax,dword ptr [b]  
0041451C  mov         dword ptr [unsigned_mod1],edx  unsigned int c = 15, d = 4;
0041451F  mov         dword ptr [c],0Fh  
00414526  mov         dword ptr [d],4  unsigned int unsigned_mod2 = c % d;  // 15 % 4 = 3
0041452D  mov         eax,dword ptr [c]  
00414530  xor         edx,edx  
00414532  div         eax,dword ptr [d]  
00414535  mov         dword ptr [unsigned_mod2],edx  return 0;
00414538  xor         eax,eax  
}
0041453A  pop         edi  
0041453B  pop         esi  
0041453C  pop         ebx  
0041453D  add         esp,108h  
00414543  cmp         ebp,esp  
00414545  call        __RTC_CheckEsp (041123Fh)  
0041454A  mov         esp,ebp  
0041454C  pop         ebp  
0041454D  ret  

​汇编代码分析(无符号取模运算,32位 x86,MSVC 编译器)​

这段代码演示了 ​​C 语言中的无符号整数取模运算(%)​​,并通过汇编展示了 div指令如何计算余数。以下是详细分析:


​1. 函数入口和栈帧初始化​

004144E0  push        ebp             ; 保存旧的基址指针
004144E1  mov         ebp,esp         ; 设置新的栈帧
004144E3  sub         esp,108h        ; 分配栈空间 (0x108 字节)
004144E9  push        ebx             ; 保存寄存器
004144EA  push        esi  
004144EB  push        edi  
004144EC  lea         edi,[ebp-48h]   ; 初始化栈空间起始地址
004144EF  mov         ecx,12h        ; 循环次数 (18次)
004144F4  mov         eax,0CCCCCCCCh ; 调试模式填充值 (0xCCCCCCCC)
004144F9  rep stos    dword ptr es:[edi] ; 用 0xCCCCCCCC 填充栈
004144FB  mov         ecx,offset _C66D3399_simple_cpp@cpp (041C008h)  
00414500  call        @__CheckForDebuggerJustMyCode@4 (041131Bh) ; 调试检查
00414505  nop                          ; 空操作(对齐)
  • sub esp, 108h​:为局部变量分配栈空间(abcdunsigned_mod1unsigned_mod2等)。

  • rep stos​:在调试模式下用 0xCCCCCCCC填充栈(检测未初始化内存)。

  • @__CheckForDebuggerJustMyCode@4​:调试模式下的安全检查(MSVC 特有)。


​2. 无符号取模运算(div+ edx存储余数)​

​(1) a % b(10 % 3 = 1)​

00414506  mov         dword ptr [a],0Ah      ; a = 10 (0xA)
0041450D  mov         dword ptr [b],3        ; b = 3
00414514  mov         eax,dword ptr [a]      ; eax = a
00414517  xor         edx,edx                ; edx = 0(无符号扩展)
00414519  div         eax,dword ptr [b]      ; eax = 商, edx = 余数
0041451C  mov         dword ptr [unsigned_mod1],edx ; unsigned_mod1 = edx (余数)
  • xor edx, edx​:

    • 在无符号除法前,必须将 edx清零(因为无符号数的高位扩展是 0)。
  • div指令​​:

    • 计算 edx:eax / [b],商存储在 eax,余数存储在 edx

    • 10 / 3 = 3​(商 eax = 3,余数 edx = 1)。

    • ​取模运算的本质​​:直接取 div计算后的余数(edx)。

​(2) c % d(15 % 4 = 3)​

0041451F  mov         dword ptr [c],0Fh      ; c = 15 (0xF)
00414526  mov         dword ptr [d],4        ; d = 4
0041452D  mov         eax,dword ptr [c]      ; eax = c
00414530  xor         edx,edx                ; edx = 0
00414532  div         eax,dword ptr [d]      ; eax = 3, edx = 3
00414535  mov         dword ptr [unsigned_mod2],edx ; unsigned_mod2 = 3
  • div计算​​:

    • 15 / 4 = 3(商 eax = 3),余数 edx = 3

    • ​无符号取模的结果始终为非负数​​(15 % 4 = 3)。


​3. 函数返回​

00414538  xor         eax,eax          ; eax = 0(返回值)
0041453A  pop         edi              ; 恢复寄存器
0041453B  pop         esi  
0041453C  pop         ebx  
0041453D  add         esp,108h         ; 释放栈空间
00414543  cmp         ebp,esp          ; 检查栈平衡
00414545  call        __RTC_CheckEsp   ; 调试模式栈检查
0041454A  mov         esp,ebp          ; 恢复 esp
0041454C  pop         ebp              ; 恢复 ebp
0041454D  ret                          ; 返回
  • xor eax, eax​:设置返回值 0(C 语言的 return 0)。

  • __RTC_CheckEsp​:调试模式下检查栈是否平衡(防止栈溢出)。


​关键结论​

  1. ​无符号取模的实现​​:

    • 通过 div指令计算商和余数,​​余数存储在 edx​。

    • ​余数始终为非负数​​(无符号数的特性)。

  2. xor edx, edx的作用​​:

    • 清零 edx,确保 div正确处理无符号数。
  3. ​与有符号取模的区别​​:

    • 有符号取模使用 idiv,余数符号与被除数相同。

    • 无符号取模使用 div,余数始终为正。


​总结​

运算指令关键操作余数规则
无符号取模(%divxor edx, edx+ div [src]余数始终为非负数
有符号取模(%idivcdq+ idiv [src]余数符号 = 被除数符号
防止优化volatile+ printf强制生成实际指令避免常量折叠和死代码删除

通过这段汇编代码,可以清晰看到无符号取模运算的实现逻辑,以及 div指令如何同时计算商和余数。

http://www.dtcms.com/a/453843.html

相关文章:

  • 《Vuejs设计与实现》第 5 章(非原始值响应式方案)下 Set 和 Map 的响应式代理
  • javascript基础入门菜鸟,javascript基础入门教程
  • 网站没有index.html深圳网站建设创想营销
  • 小米网站开发语言系统开发过程中的第一个文档
  • 分布式专题——35 Netty的使用和常用组件辨析
  • Java Caffeine 高性能缓存库详解与使用案例
  • 如何用凡科做自己的网站网站建设中的色彩搭配
  • RK3588:MIPI底层驱动学习——入门第五篇(一文梳理media、video、v4l-subdev关系)
  • 每日一个C语言知识:C 变量
  • 神秘迷宫探险 - 详细题解教程
  • VOCO摘要
  • 轻量级个人建站
  • DevDay 2025 开发者大会看点
  • 什么网站可以免费做视频软件做网站导航按钮怎么做
  • 网站建设的基本要素有专门做任务的网站
  • 十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解
  • WDF驱动开发-PNP和电源管理
  • 网站的标题标签一般是写在网站 未备案 支付宝
  • 募投绘蓝图-昂瑞微的成长密码与未来布局
  • 公司网络营销的方案思路seo整站优化外包服务
  • 工程建设公司网站怎么查询网站的域名备案
  • TypeScript 对比 JavaScript
  • 焦作网站设计公司自己怎么做外贸网站
  • ros2 消息订阅与发布示例 c++
  • 廊坊网站建设廊坊企业文化建设网站
  • 纸做的花朵成品网站个人简介html代码模板
  • 【精品资料鉴赏】证券数据治理项目投标技术方案
  • AI大模型核心概念
  • 企业网站模板seo公益建设网站的作用
  • 阿里巴巴1688怎么做网站自建网站阿里云备案通过后怎么做