题目
给定一个32位的有符号数x
,返回x
中每位上的数字反转后的结果。
假设环境不允许存储64位整数(有符号或无符号)
示例1:
输入:x = 123
输出:321
示例2:
输入:x = -123
输出:-321
示例3:
输入:x = 120
输出:21
解题代码
#include "stdafx.h"
void revert(int num){
int sign=0;
int cnt=0;
int arr[50]={0};
if(num<0){
num=-num;
sign=1;
}
__asm{
mov eax,num
_begin:
cdq
mov ecx,0Ah
div ecx
mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
test eax,eax
jnz _begin
_ret:
}
if(sign){
printf("-");
}
int i=0;
while(arr[i]==0){
i++;
}
for(;i<cnt;i++){
printf("%d",arr[i]);
}
printf("\n");
}
int main(int argc, char* argv[])
{
revert(-10086);
return 0;
}
运行结果
整体代码分析
整体代码就是做了以下几个工作:
- 判断参数是否为负数,如果是负数,则取其绝对值,并在后面输出时会先输出一个符号
- 通过汇编依次将除以10的余数存入数组中,并记录下位数
- 将数组头部为0的部分跳过,不输出
- 输出数组的剩下有效部分
汇编代码分析
__asm{
mov eax,num
_begin:
cdq
mov ecx,0Ah
div ecx
mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
test eax,eax
jnz _begin
_ret:
}
可以看到汇编的代码非常简洁,只有短短几行
来分析一下这段代码做了些什么:
首先将要被反转的数赋值给eax
mov eax,num
_begin
接着声明一个程序段_beign,用于之后跳回来实现循环
_begin:
cdq指令
接着是一个新的汇编代码:
cdq
cdq—Convert Double to Quad (386+),该指令把edx扩展为eax的高位,也就是借助两个寄存器来存储一个64位的数
以123为例,看一下cdq执行前和执行后的效果:
执行前
此时EAX为7B,也就是123的十六进制,EDX则暂不清楚数据含义
执行后
执行后,可以发现EDX被清零了,效果和movsx类似,只不过被扩展的高位存放在edx中
为什么要进行扩展?之后的div指令所要求的,且先向下看
然后是将除数10赋值给ecx,10的十六进制对应A
mov ecx,0Ah
div指令
再接着就是div指令了
先介绍一下div指令
div为无符号除法 ; idiv为有符号除法
div 指令只需要一个操作数,即除数
既然div指令只给出的除数,那么被除数呢?
被除数:
- 如果除数为8位, 被除数则为 16 位, 在 ax 中存放
- 如果除数为16位, 被除数则为 32 位, 在 dx 和 ax 中存放, dx 存放高 16 位, ax 存放低 16 位
- 如果除数为32位,被除数则为64为,在edx和eax中存放,edx存放高16位,eax存放低16位
接着看我们的代码:
div ecx
我们指定ecx为除数,除数ecx=10
依旧以123为例,查看div指令执行前后寄存器的变化
执行前
执行前EDX和EAX组成除数,0x00000000 0000007B = 123;ECX作为除数0x0A=10
执行后
执行后可以看到EAX=0x0C=12,EDX=3
得知,div指令执行后EAX存放除后的结果,EDX存放除后的余数
数据放入数组
得到除后的结果和余数以后,就将余数存放到数组即可
mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
- 这里先将cnt(用来记录被除数的位数)赋值给ebx
- 然后通过lea指令获得要存放的数组的成员位置
- 获取完成后,将cnt加1
- 然后就是将edx也就是余数存放到数组中
有关数组的知识可回顾: 逆向基础笔记十六 汇编一维数组
比较循环
test eax,eax
jnz _begin
判断eax是否为0,如果不为0,则继续跳到先前的代码继续执行循环,直到被除数为0才跳出循环
_ret
这后面并没有写代码,只是声明了一个返回的段,算是为了代码的结构完整性吧
总结
将数字顺序反转这种入门题算是十分经典了,但用汇编来完成的人却并不多,通过这个案例来巩固之前所学的汇编知识,加强理解。
PS:不同于以往对汇编冗余的观念,在某些场景使用汇编反而会更简洁,而非更加冗余,这个案例就展现出汇编的优点;但并非所有项目都适合使用汇编,不要为了用汇编而用汇编,要结合具体场景考虑