基于用户输入的汇编多位数加法代码结果异常求排查
求助:x86汇编多位数加法结果不正确,求排查问题
我最近写了一段x86汇编代码实现多位数加法,但运行后总是得不到正确的计算结果,麻烦帮忙看看哪里出问题了?我的代码如下:
SYS_EXIT equ 1 SYS_READ equ 3 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 segment .data msg db "The sum is: " len equ $-msg msg2 db "Input num1: " len2 equ $-msg2 msg3 db "Input num2:" len3 equ $-msg3 lf db 0x0A segment .bss sum resb 40 ; 为ASCII字符串分配40字节 sum_len resd 1 number1 resb 40 number2 resb 40 num1_len resd 1 num2_len resd 1 section .text global _start _start: ; 输出“输入num1” mov ecx, msg2 mov edx, len2 mov ebx, 1 mov eax, 4 int 0x80 ; 读取并存储输入1 mov eax, SYS_READ mov ebx, STDIN mov ecx, number1 mov edx, 3 int 0x80 lea esi,[number1] mov ecx,4 call string_to_int ; 输出“输入num2” mov ecx, msg3 mov edx, len3 mov ebx, 1 mov eax, 4 int 0x80 ; 读取并存储输入2 mov eax, SYS_READ mov ebx, STDIN mov ecx, number2 mov edx, 3 int 0x80 lea esi,[number2] mov ecx,4 call string_to_int ; 加法运算 mov eax, [number1] mov ebx, [number2] add eax, ebx ; 将数字转换为字符串 mov edi, sum ; 参数:目标字符串地址 call int2str ; 获取EAX的数字并存储为ASCII sub edi, sum mov [sum_len], edi ; 输出“结果为:” mov ecx, msg mov edx, len mov ebx, 1 mov eax, 4 int 0x80 ; 输出求和结果 mov eax, 4 mov ebx, 1 mov ecx, sum mov edx, [sum_len] int 0x80 ; 输出换行 mov eax, 4 mov ebx, 1 mov ecx, lf mov edx, 1 int 0x80 ; 退出程序 mov eax, 1 mov ebx, 0 int 0x80 ; 输入: ; ESI = 待转换字符串的指针 ; ECX = 字符串的位数(必须>0) ; 输出: ; EAX = 整数值 string_to_int: xor ebx,ebx ; 清空ebx .next_digit: movzx eax,byte[esi] inc esi sub al,'0' ; 从ASCII转换为数字 imul ebx,10 add ebx,eax ; ebx = ebx*10 + eax loop .next_digit ; 循环(--ecx) mov eax,ebx ret int2str: ; 将EAX中的正整数转换为EDI指向的字符串 xor ecx, ecx mov ebx, 10 .LL1: ; 第一循环:保存余数 xor edx, edx ; 清空EDX用于除法 div ebx ; EDX:EAX/EBX -> EAX为商,EDX为余数 push dx ; 保存余数 inc ecx ; 增加入栈计数器 test eax, eax ; 还有可除的数吗? jnz .LL1 ; 是:继续循环 .LL2: ; 第二循环:取出余数 pop dx ; DL中是余数 or dl, '0' ; 转换为ASCII mov [edi], dl ; 保存到字符串 inc edi ; 移动字符串指针 loop .LL2 ; 循环ECX次 mov byte [edi], 0 ; 终止字符 ret
我排查出几个关键问题:
- 输入处理逻辑错误:调用
SYS_READ时edx设为3,只能读取3个字符,但用户输入数字后按回车,实际输入包含数字和换行符;而且调用string_to_int时硬设ecx=4,会越界读取不确定的内存,导致转换出错误的整数。 - 数值存储对象错误:
string_to_int返回的转换后整数存在eax里,但代码里没有把这个值存到专门的变量,反而直接取[number1](这是ASCII字符串的内存)来做加法,完全搞错了操作对象。 - 输入长度未正确处理:没有保存
SYS_READ返回的实际读取字节数,也没去掉换行符,导致转换时把换行符当成数字处理(换行符ASCII是0x0A,减去'0'后是10,会被当成数字计算)。
修复后的代码:
我调整了变量定义,正确处理输入长度和数值存储,修复后的代码如下:
SYS_EXIT equ 1 SYS_READ equ 3 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 segment .data msg db "The sum is: " len equ $-msg msg2 db "Input num1: " len2 equ $-msg2 msg3 db "Input num2: " len3 equ $-msg3 lf db 0x0A segment .bss sum resb 40 ; 为ASCII字符串分配40字节 sum_len resd 1 number1 resb 40 number2 resb 40 num1_val resd 1 ; 存储转换后的第一个整数 num2_val resd 1 ; 存储转换后的第二个整数 num1_len resd 1 num2_len resd 1 section .text global _start _start: ; 输出“输入num1” mov ecx, msg2 mov edx, len2 mov ebx, STDOUT mov eax, SYS_WRITE int 0x80 ; 读取并存储输入1 mov eax, SYS_READ mov ebx, STDIN mov ecx, number1 mov edx, 40 ; 最多读取40字节 int 0x80 mov [num1_len], eax ; 保存实际读取的字节数 lea esi, [number1] mov ecx, eax dec ecx ; 减去换行符的长度(最后一个字符是0x0A) call string_to_int mov [num1_val], eax ; 把转换后的整数存起来 ; 输出“输入num2” mov ecx, msg3 mov edx, len3 mov ebx, STDOUT mov eax, SYS_WRITE int 0x80 ; 读取并存储输入2 mov eax, SYS_READ mov ebx, STDIN mov ecx, number2 mov edx, 40 int 0x80 mov [num2_len], eax lea esi, [number2] mov ecx, eax dec ecx ; 去掉换行符 call string_to_int mov [num2_val], eax ; 加法运算 mov eax, [num1_val] mov ebx, [num2_val] add eax, ebx ; 将数字转换为字符串 mov edi, sum ; 参数:目标字符串地址 call int2str ; 获取字符串长度 sub edi, sum mov [sum_len], edi ; 输出“结果为:” mov ecx, msg mov edx, len mov ebx, STDOUT mov eax, SYS_WRITE int 0x80 ; 输出求和结果 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, sum mov edx, [sum_len] int 0x80 ; 输出换行 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, lf mov edx, 1 int 0x80 ; 退出程序 mov eax, SYS_EXIT mov ebx, 0 int 0x80 ; 输入: ; ESI = 待转换字符串的指针 ; ECX = 字符串的位数(必须>0) ; 输出: ; EAX = 整数值 string_to_int: xor ebx, ebx ; 清空ebx用来累加数值 .next_digit: movzx eax, byte[esi] inc esi sub al, '0' ; 从ASCII转换为数字 imul ebx, 10 add ebx, eax ; ebx = ebx*10 + 当前数字 loop .next_digit ; 循环处理每一位 mov eax, ebx ; 把结果放到eax返回 ret int2str: ; 将EAX中的正整数转换为EDI指向的字符串 xor ecx, ecx mov ebx, 10 .LL1: ; 第一循环:分解数字,余数入栈 xor edx, edx ; 清空EDX,因为div是EDX:EAX除以EBX div ebx ; EAX=商,EDX=余数 push dx ; 保存余数(DL是余数的ASCII对应值) inc ecx ; 记录入栈次数 test eax, eax ; 判断商是否为0 jnz .LL1 ; 不为0继续循环 .LL2: ; 第二循环:出栈转换为ASCII pop dx or dl, '0' ; 余数转成ASCII字符 mov [edi], dl inc edi loop .LL2 mov byte [edi], 0 ; 字符串结束符 ret
修复说明:
- 新增了
num1_val和num2_val变量,专门存储转换后的整数,避免直接操作字符串内存。 - 读取输入时设置足够大的
edx(40字节),保存实际读取的长度,调用string_to_int时减去换行符的长度,避免把换行符当成数字处理。 - 统一使用宏定义的常量(比如
SYS_WRITE代替直接写4),让代码更清晰易读。
内容的提问来源于stack exchange,提问作者er 21
相关产品推荐
相关产品推荐

