我想要写一个程序,输入一些内容,找到字符串中奇数位置的字符并打印出来。比如你输入'somewords',程序会打印出'oeod'。
我最终使用了一个循环遍历了整个字符串,然后将计数器除以2,并且如果余数不为0,则打印出该位置上的字符。
但是程序在遇到单个字符时无法输出任何结果。
以下是完整代码:
但是它会打印除第一个字符以外的所有内容:
我卡住了,不知道我的错误在哪里。 编辑后的最终代码:
我最终使用了一个循环遍历了整个字符串,然后将计数器除以2,并且如果余数不为0,则打印出该位置上的字符。
但是程序在遇到单个字符时无法输出任何结果。
以下是完整代码:
SECTION .bss
inp: resb 255
SECTION .data
msg db "Enter the string: ", 0h
SECTION .text
global _start
_start:
mov eax, msg
call stprint
mov edx, 255 ; take user input
mov ecx, inp
mov ebx, 0
mov eax, 3
int 80h
call findodd
mov ebx, 0
mov eax, 1
int 80h
findodd:
push eax
push ecx
push edx
push esi
push ebx
mov ecx, 0 ; counter
mov esi, 2 ; divider
.iterstring:
mov eax, inp ; move input to eax
cmp byte [eax+ecx], 0 ; check for end of the string in position
je .finish ; if equal, finish
inc ecx
push eax
mov eax, ecx ; move counter to eax
xor edx, edx ; divide it by 2
idiv esi
pop eax
cmp edx, 0 ; check the remainder
jnz .printchar ; print character if != 0
jmp .iterstring
.printchar:
push eax
push ebx
movzx ebx, byte [eax+ecx] ; move single byte to ebx
push ecx
mov ecx, ebx ; move ebx to print
mov edx, 1 ; print the character
mov ebx, 1
mov eax, 4
int 80h
pop ecx
pop eax
pop ebx
jmp .iterstring
.finish:
pop eax
pop ecx
pop edx
pop esi
pop ebx
ret
; print string function (taken from tutorial)
; if I try to print single character with it I get SEGFAULT
stprint:
push edx
push ecx
push ebx
push eax
call stlen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
stlen:
push ebx
mov ebx, eax
nextch:
cmp byte [eax], 0
jz finish
inc eax
jmp nextch
finish:
sub eax, ebx
pop ebx
ret
我尝试使用bl、al和cl,但没有成功。我还尝试了一些检查。例如,在.iterstring中打印计数器:
nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: test
1
2
3
4
5
看起来迭代工作正常。
我在类似问题的回答中(如何在Linux x86 NASM中打印字符?)通过对代码进行以下更改获得了最大的运气:
.printchar:
push eax
push ebx
push esi
mov eax, inp
movzx ebx, byte [eax+ecx]
mov esi, ecx ; see below
push ecx
push ebx
mov ecx, esp
mov edx, 1 ; print the character
mov ebx, 1
mov eax, 4
int 80h
pop ecx
pop ebx
pop eax
pop ebx
mov ecx, esi ; without this it just prints 1 character and ends
pop esi ; so ecx is not restored with pop for some reason?
jmp .iterstring
但是它会打印除第一个字符以外的所有内容:
nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
mewords
我卡住了,不知道我的错误在哪里。 编辑后的最终代码:
findodd:
push eax
push ecx
push edx
push esi
push ebx
mov esi, 0 ; counter
mov eax, inp
.iterstring:
inc esi
cmp byte [eax+esi], 0
jz .finish
test esi,1
jz .iterstring
movzx ecx, byte [eax+esi]
push ecx
mov ecx, esp
mov edx, 1
mov ebx, 1
push eax
mov eax, 4
int 80h
pop eax
pop ecx
jmp .iterstring
.finish:
pop eax
pop ecx
pop edx
pop esi
pop ebx
ret
现在它按预期工作:
nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
oeod
我不得不删除更多的push-pop指令,并且把计数器移到esi中,因为推送和弹出寄存器并不能总是恢复它们在栈中的值,这对我来说很奇怪。
当我尝试将byte [eax+ecx]
的地址移入到ecx中时,它可以工作,但是当我将其改为byte [eax+1]
时,它会导致段错误,因为在弹出后恢复eax会破坏它。当我将ecx推入打印消息并将其弹回时,它最终导致了段错误,并且gdb显示在弹出ecx后内部有垃圾代码。
不过,使用当前的代码就可以正常工作。
CONFIG_IA32_EMULATION
构建它时才会出现问题,例如在WSL(Windows Subsystem for Linux)上。这些nasm + ld命令将创建一个32位可执行文件,该文件将以32位模式运行。因此,没有IA32支持的内核实际上根本无法运行它。 64位内核不是症状的可能解释,因为OP提供了一个很好的[mcve],而不仅仅是“不能工作” :) - Peter Cordesiterstring
循环如果在底部使用jz .iterstring
而不是跳过相反条件的jmp
,将会更有效率。那只是不必要地过于复杂,与惯用的do{}while()
循环风格相比。此外,使用div
除以2的幂是可怕的:使用AND获取余数,即低位(们)。或者更好的方法是使用test al, 1
测试奇偶性,直接检查EAX的低位。 - Peter Cordes