除法已经说完了,来说一下取模。
代码
汇编
wingw
main(int, char**):
push %rbp
mov %rsp,%rbp
sub $0x20,%rsp
mov %ecx,0x10(%rbp)
mov %rdx,0x18(%rbp)
call 0x7ff7410d1837 ; <__main> mov 0x10(%rbp),%eax ;eax = argc
cltd
shr $0x1d,%edx ;edx >> 29(这里相当于判断)
add %edx,%eax ;eax = argc + (argc <0 ? 7 : 0)
and $0x7,%eax ;eax = eax & 7
sub %edx,%eax ;减回修正量
mov %eax,%edx
lea 0x88f5(%rip),%rax ; 0x7ff7410da000 mov %rax,%rcx
call 0x7ff7410d8e40 ; <printf(char const*, ...)> mov 0x10(%rbp),%edx ;edx = argc
movslq %edx,%rax
imul $0x38e38e39,%rax,%rax ;rax = argc * 0x38e38e39
shr $0x20,%rax ;rax = rax >> 32(逻辑)
sar %eax ;eax = eax >> 1(算数)
mov %edx,%ecx ;ecx = argc >> 31
sar $0x1f,%ecx ;ecx >> 1
sub %ecx,%eax ;ecx = ecx -- eax(%eax = argc / 9)
mov %eax,%ecx ;ecx = eax
shl $0x3,%ecx ;ecx >> 3
add %eax,%ecx ;ecx = eax + ecx(%ecx = (%eax << 3) + %eax = 8*q + q = 9*q)
mov %edx,%eax
sub %ecx,%eax ;eax = argc - 9 * (argc / 9)
mov %eax,%edx
lea 0x88bf(%rip),%rax ; 0x7ff7410da000 mov %rax,%rcx
call 0x7ff7410d8e40 ; <printf(char const*, ...)> mov $0x0,%eax
add $0x20,%rsp
pop %rbp
ret
msvc
64->64
main(int,char **):
movq %rdx, 0x10(%rsp)
movl %ecx, 0x8(%rsp)
pushq %rdi
subq $0x20, %rsp
movl 0x30(%rsp), %eax ; eax = argc
cltd ;扩展eax
andl $0x7, %edx ;edx = edx & 7
addl %edx, %eax ;edx = edx & eax
andl $0x7, %eax ;eax = eax = & 7
subl %edx, %eax ;(argc + (argc<0?7:0)) & 7 - (argc<0?7:0)
movl %eax, %edx
leaq 0x86ca(%rip), %rcx
callq 0x140001195 ; printf movl 0x30(%rsp), %eax ;eax = aegc
cltd ;扩展
movl $0x9, %ecx ;eax = argc / 9
idivl %ecx ;eax = argc /9,edx = argc % 9
movl %edx, %eax
movl %eax, %edx
leaq 0x86b6(%rip), %rcx
callq 0x140001195 ; printf xorl %eax, %eax
addq $0x20, %rsp
popq %rdi
retq
64->32
main(int,char **):
pushl %ebp
movl %esp, %ebp
movl 0x8(%ebp), %eax
andl $0x80000007, %eax ; imm = 0x80000007 jns 0x4014d2 ; <+18> at main.cpp:4(%eax 的符号位为 0(argc >= 0)时跳转) decl %eax
orl $-0x8, %eax
incl %eax ;负数取模修正
pushl %eax
pushl 0x606b30 *; imm = 0x606B30* calll 0x40119a *; _printf* addl 0x8, %esp
movl 0x8(%ebp), %eax ;eax = argc
cltd ;扩展(后面是正常的除法操作)
movl $0x9, %ecx
idivl %ecx
pushl %edx
pushl 0x606b38 *; imm = 0x606B38* calll 0x40119a *; _printf* addl 0x8, %esp
xorl %eax, %eax
cmpl %esp, %ebp
calll 0x4010ff ; __RTC_CheckEsp popl %ebp
retl
对于2的幂的取模有一下四种方案:
方案1
对2的k次方取余,余数只需要被除数低k位的值就可以,如果是负数需要在k位之前补1。
代码模板:
mov reg, 被除数
and reg , 80000007h
jns lab1
or reg,0ffffff8h
lab1:
余数的值非0没有问题;如果为0他就是错误的。需要进行调整。方法是在or运算之前-1,or算完了再+1。如果余数不为0不影响计算结果。
代码模板:
mov reg,被除数
and reg,80000007h(这个数由2^k决定)
jns lab1
dec reg
or reg,fffffff8
inc reg
lab1:
碰到这个就能判定是取模代码,模型是变量对2k
执行取模运算。还原就直接去掉高位保留低位代码,统计地位有多少个1就能得到k值。
方案2
对2的k次方取余对于正数使用方案1,负数可以使用下面的调整公式。
x% 2 k = x+ 2 k - 1 & 2 k -1 - 2 k-1 
遇到这个公式页可以判定是取模代码,原型是变量对2k
执行取模运算。还原就是直接看and reg,0fffffff8统计低位一共保留有多少0即可得到k的值。
方案3
对2的k次方取余,对正数使用以下调整公式
x% 2 k =x- x&~ 2 k-1 
对负数使用以下调整公式
x% 2 k =x- x+ 2 k -1 &~ 2 k-1 
遇到这个公式页可以判定是取模代码,原型是变量对2k
执行取模运算。还原就是直接看and reg,0fffffff8统计低位一共保留有多少0即可得到k的值。
方案4
对非2的k次方取余,
r=a%b 
根据性质可以得到
r=a-q*b=a- a b*b 
碰到这个很简单直接看b就可以了。