x86-32汇编:字符串转整数后输出异常的解决方法
问题描述
需要编写x86-32位汇编程序,实现以下功能:读取包含3个整数的输入字符串(如“30 40 2”),将其转换为整数并存入独立寄存器,最后按number 1: (num1), number 2: (num2), number 3: (num3).格式输出。
现有代码在输入“100 999 1”时,输出结果为“100 999 99 1”,存在异常。原代码如下:
section .data num db '0' ; Initialize for number with value 0 of ASCII res db 0 ; decalre a byte res with value 0 numStrSize equ 12 section .bss input resb 30 ; Reserves a buffer input of 10 bytes for user input. num1 resd 1 num2 resd 1 opCode resd 1 str_buffer resb numStrSize section .text global _start _start: ; Read input from user mov eax, 3 ; Sets up a sys_read system call (system call number 3). mov ebx, 0 ; Specifies file descriptor 0 (standard input). mov ecx, input ; Points ecx to the input buffer. mov edx, 10 ; Increase to match buffer size int 80h ; Executes the system call (reads user input into input buffer). ; Convert string to integer mov esi, input ; ESI points to the start of input call convertToInt mov [num1], eax call NextInt call convertToInt mov [num2], eax call NextInt call convertToInt mov [opCode], eax mov eax, [num1] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer mov edx, numStrSize int 80h mov eax, [num2] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer mov edx, numStrSize int 80h mov eax, [opCode] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer mov edx, numStrSize int 80h exit: mov eax, 1 xor ebx, ebx int 80h convertToInt: xor eax, eax ; Clear EAX for storing result .Loop: movzx ebx, byte [esi] ; Move one byte of input into EBX test ebx, ebx ; Test if byte is null terminator (end of string) jz .end cmp ebx, ' ' ; Check if the character is a whitespace je .end ; If whitespace, jump to skip_character sub ebx, '0' ; Convert ASCII to integer (subtract ASCII value of '0') imul eax, eax, 10 ; Multiply current result by 10 (shift left by one decimal place) add eax, ebx ; Add new digit to result inc esi jmp .Loop .end: ret IntegerToString: mov ebx, 10 ; Set divisor to 10 mov edi, ecx ; Set EDI to point to the start of the buffer mov byte [edi], 0 ; Null-terminate the string .convert: add edi, numStrSize - 1 ; Move EDI to the end of the buffer .convert_loop: dec edi ; Move back one byte in the buffer xor edx, edx ; Clear EDX for division div ebx ; Divide EAX by 10, result in EAX, remainder in EDX add dl, '0' ; Convert remainder to ASCII character mov [edi], dl ; Store ASCII character in buffer test eax, eax ; Check if EAX is zero jnz .convert_loop ; If not zero, continue loop .reverse: mov esi, ecx dec edi ; Decrement EDI to exclude null terminator jmp .start_reverse .start_reverse: .reverse_loop: cmp esi, edi ; Check if pointers have crossed jge .done ; If so, the string is reversed mov al, [esi] ; Swap the characters mov bl, [edi] mov [esi], bl mov [edi], al inc esi ; Move to next characters dec edi jmp .reverse_loop .done: ret NextInt: .loop: movzx ebx, byte [esi] test ebx, ebx jz .end cmp ebx, ' ' je .skipSpace inc esi jmp .loop .skipSpace: inc esi jmp .end .end: ret
问题分析
- 输入处理不规范:
sys_read读取的输入包含换行符,且未添加null终止符,可能导致字符串转换时读取到缓冲区垃圾数据;同时读取长度设置过小(10字节),无法容纳较长输入。 - 字符串转换逻辑错误:
IntegerToString函数反转步骤中多余的dec edi导致反转范围错误,包含缓冲区中未初始化的垃圾数据或之前转换的残留内容。 - 输出逻辑错误:固定输出整个缓冲区大小(12字节),会将缓冲区中未使用的无效内容一并输出,导致显示异常。
- 空格跳过逻辑缺陷:
NextInt只能跳过单个空格,无法处理连续空格的情况,且逻辑冗余。
修复方案
以下是修复后的完整代码:
section .data prompt1 db 'number 1: ' prompt1_len equ $ - prompt1 prompt2 db ', number 2: ' prompt2_len equ $ - prompt2 prompt3 db ', number 3: ' prompt3_len equ $ - prompt3 prompt_end db '.' prompt_end_len equ $ - prompt_end numStrSize equ 12 section .bss input resb 30 ; 预留30字节输入缓冲区 num1 resd 1 num2 resd 1 opCode resd 1 str_buffer resb numStrSize section .text global _start _start: ; 读取用户输入,最多29字节(留1字节存null) mov eax, 3 mov ebx, 0 mov ecx, input mov edx, 29 int 80h ; 将换行符替换为null终止符 mov byte [input + eax], 0 ; 转换第一个整数 mov esi, input call convertToInt mov [num1], eax ; 跳转到第二个整数 call NextInt call convertToInt mov [num2], eax ; 跳转到第三个整数 call NextInt call convertToInt mov [opCode], eax ; 按指定格式输出 ; 输出number 1: mov eax, 4 mov ebx, 1 mov ecx, prompt1 mov edx, prompt1_len int 80h ; 输出num1 mov eax, [num1] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer int 80h ; 输出, number 2: mov eax, 4 mov ebx, 1 mov ecx, prompt2 mov edx, prompt2_len int 80h ; 输出num2 mov eax, [num2] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer int 80h ; 输出, number 3: mov eax, 4 mov ebx, 1 mov ecx, prompt3 mov edx, prompt3_len int 80h ; 输出opCode mov eax, [opCode] mov ecx, str_buffer call IntegerToString mov eax, 4 mov ebx, 1 mov ecx, str_buffer int 80h ; 输出结尾的点 mov eax, 4 mov ebx, 1 mov ecx, prompt_end mov edx, prompt_end_len int 80h exit: mov eax, 1 xor ebx, ebx int 80h ; 字符串转整数:ESI指向输入字符串,返回值在EAX convertToInt: xor eax, eax .Loop: movzx ebx, byte [esi] test ebx, ebx jz .end cmp ebx, ' ' je .end sub ebx, '0' imul eax, eax, 10 add eax, ebx inc esi jmp .Loop .end: ret ; 整数转字符串:EAX为整数,ECX指向输出缓冲区,返回时EDX为字符串长度 IntegerToString: ; 清空缓冲区 mov edi, ecx mov ecx, numStrSize xor eax, eax rep stosb mov ecx, edi ; 恢复ECX为缓冲区地址 mov ebx, 10 mov edi, ecx + numStrSize - 2 ; 从倒数第二个字节开始存(最后一个留作null) mov byte [ecx + numStrSize - 1], 0 ; 末尾设为null .convert_loop: xor edx, edx div ebx add dl, '0' mov [edi], dl dec edi test eax, eax jnz .convert_loop ; 将数字移到缓冲区开头 mov esi, edi + 1 mov edi, ecx .copy_loop: mov al, [esi] mov [edi], al test al, al jz .copy_done inc esi inc edi jmp .copy_loop .copy_done: ; 计算字符串长度 mov edx, edi sub edx, ecx ret ; 跳过所有连续空格,ESI指向第一个非空格字符或null NextInt: .skip_spaces: movzx ebx, byte [esi] test ebx, ebx jz .end cmp ebx, ' ' jne .end inc esi jmp .skip_spaces .end: ret
关键修复点说明
- 输入处理:
- 调整
sys_read的读取长度为29,预留1字节存储null终止符; - 读取后手动添加null终止符,替换输入中的换行符,确保字符串转换时能正确识别结束位置。
- 调整
- 整数转字符串:
- 每次转换前清空缓冲区,避免残留旧数据;
- 移除反转步骤中多余的
dec edi,改为将数字从缓冲区末尾复制到开头,简化逻辑并避免范围错误; - 返回实际字符串长度,输出时仅输出有效内容,避免无效字符。
- 空格跳过逻辑:
- 简化
NextInt函数,改为循环跳过所有连续空格,兼容多空格分隔的输入。
- 简化
- 格式输出:
- 添加格式字符串,严格按照要求的格式输出内容。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

