NASM中统计单个字符在字符串中出现次数的实现问题
NASM汇编统计字符出现次数问题修正
需求说明
实现统计指定字符在字符串中的出现次数,例如比对字符为"a"、字符串为"haha"时,计数结果应为2。核心思路为加载字符串地址到寄存器,逐字节比对内存字符与目标字符,更新计数器后继续处理。
现有代码
SECTION .data ; Initialize data cmp_char db "a", 0h word1 db "harp", 0h word2 db "haha" , 0h SECTION .text global _start ; Initialize starting point for program _start: ; Program entry point jmp check_for_vowel ; Jumps to "check_for_vowel" function check_for_vowel: push EDX ; Preserve EDX on the stack; restore after function runs push ECX ; Preserve ECX on the stack; restore after function runs push EBX ; Preserve EBX on the stack; restore after function runs push EAX ; Preserve EAX on the stack; restore after function runs mov EDX, 0 ; Initialize char counter mov AH, [cmp_char] ; Load value of "cmp_char" into AH, an 8-bit subsection of EAX mov EBX, word1 ; Move address of word1 to EBX. loop_counter: mov AL, Byte [EBX] ; Move value of BX into AL, an 8-bit subregister of EAX cmp AL, 0h ; Check if value in AL is 0... jz finished ; ... and if it is, jump to "finished" function. cmp AL, AH ; Else, compare the Bytes in AL and AH... jz match ; ... and if they match, jump to the "match" function. inc ECX ; Otherwise, increment the loop counter... inc EBX ; ... and loop to the next character in the string... jmp loop_counter ; ... and start the loop over again. match: inc EDX ; Increment the char counter. inc EBX ; Loop to the next character in the string. jmp loop_counter ; Jump back to the loop_counter. finished: ret
问题分析
- 栈平衡与程序退出问题:
_start作为程序入口直接跳转至check_for_vowel,但该函数开头push了寄存器,结尾用ret返回——此时栈中无合法返回地址,会导致程序崩溃。Linux下程序需通过exit系统调用正常退出。 - 冗余寄存器操作:
ECX被push保存,但loop_counter中inc ECX属于无效操作,字符串地址已通过EBX递增,无需额外循环计数器。 - 缺少结果输出逻辑:代码仅完成计数,未将
EDX中的数值转换为ASCII并输出,无法看到最终结果。 - 不必要的数据定义:
cmp_char定义为db "a", 0h,实际仅需取第一个字节,末尾的0h多余(不影响功能但可简化)。
修正后的代码
SECTION .data cmp_char db "a" ; 仅存储目标字符,无需额外0 word1 db "harp", 0h word2 db "haha", 0h newline db 0xA ; 换行符用于输出格式 SECTION .text global _start _start: mov EDX, 0 ; 初始化计数器为0 mov AH, [cmp_char] ; 加载目标字符到AH mov EBX, word2 ; 可切换为word1或word2测试 count_loop: mov AL, byte [EBX] ; 读取当前字符到AL cmp AL, 0 ; 判断是否到字符串结束符 jz print_result ; 结束则跳转至输出 cmp AL, AH ; 比对当前字符与目标字符 jz increment_counter ; 匹配则跳转至计数递增 next_char: inc EBX ; 地址递增,处理下一个字符 jmp count_loop increment_counter: inc EDX ; 计数器+1 jmp next_char print_result: ; 将十进制计数转换为ASCII(支持0-9,需更大数值可扩展多位数转换) add EDX, '0' ; 数字转ASCII字符 mov [newline-1], DL ; 临时存储转换后的字符 ; 调用write系统调用输出计数 mov EAX, 4 ; sys_write系统调用号 mov EBX, 1 ; 文件描述符stdout mov ECX, newline-1 ; 输出字符地址 mov EDX, 1 ; 输出长度1 int 0x80 ; 输出换行 mov EAX, 4 mov EBX, 1 mov ECX, newline mov EDX, 1 int 0x80 ; 调用exit系统退出程序 mov EAX, 1 ; sys_exit系统调用号 xor EBX, EBX ; 退出码0 int 0x80
关键修改说明
- 移除不必要的寄存器push/pop操作,避免栈平衡问题。
- 删除冗余的
ECX递增操作,仅通过EBX管理字符串地址。 - 添加计数转ASCII的逻辑(针对0-9的小数值,需支持更大计数可扩展多位数转换)。
- 增加
write系统调用实现结果输出,以及exit系统调用保证程序正常退出。 - 简化
cmp_char的定义,去掉多余的结束符。
内容的提问来源于stack exchange,提问作者user14178703
相关产品推荐
相关产品推荐

