Linux 如何使用NASM查找字符串的长度?
我正在尝试使用NASM制作一个程序,该程序从命令行参数获取输入。因为没有提供字符串长度,所以我尝试创建一个函数来计算我自己的长度。下面是我的尝试,它获取指向Linux 如何使用NASM查找字符串的长度?,linux,string,assembly,x86,nasm,Linux,String,Assembly,X86,Nasm,我正在尝试使用NASM制作一个程序,该程序从命令行参数获取输入。因为没有提供字符串长度,所以我尝试创建一个函数来计算我自己的长度。下面是我的尝试,它获取指向ebx寄存器中字符串的指针,并返回ecx中字符串的长度: len: push ebx mov ecx,0 dec ebx count: inc ecx inc ebx cmp ebx,0 jnz count dec ecx pop
ebx
寄存器中字符串的指针,并返回ecx
中字符串的长度:
len:
push ebx
mov ecx,0
dec ebx
count:
inc ecx
inc ebx
cmp ebx,0
jnz count
dec ecx
pop ebx
ret
我的方法是逐个字符检查字符串,并检查它是否为null。如果不是,我将递增ecx并转到下一个字符。我认为问题在于
cmpebx,0
对于我正在尝试的操作是不正确的。如何正确地检查字符是否为空?另外,还有其他我可以做得更好的事情吗?您正在将ebx
中的值与0进行比较,这不是您想要的。ebx
中的值是内存中字符的地址,因此应按如下方式取消引用:
cmp byte[ebx], 0
另外,最后一个
推ebx
应该是pop ebx
这里我将如何编码它
len:
push ebx
mov eax, ebx
lp:
cmp byte [eax], 0
jz lpend
inc eax
jmp lp
lpend:
sub eax, ebx
pop ebx
ret
(结果在eax中)。可能还有更好的方法。下面是我如何在检查
argv[1]
的64位Linux可执行文件中执行的。内核使用堆栈上的argc
和argv[]
启动一个新进程,如x86-64 System V ABI中所述
_start:
pop rsi ; number of arguments (argc)
pop rsi ; argv[0] the command itself (or program name)
pop rsi ; rsi = argv[1], a pointer to a string
mov ecx, 0 ; counter
.repeat:
lodsb ; byte in AL
test al,al ; check if zero
jz .done ; if zero then we're done
inc ecx ; increment counter
jmp .repeat ; repeat until zero
.done:
; string is unchanged, ecx contains the length of the string
; unused, we look at command line args instead
section .rodata
asciiz: db "This is a string with 36 characters.", 0
这是缓慢和低效的,但容易理解
为了提高效率,你应该
- 循环中只有1个分支()
- 通过使用
加载,而不是合并到以前的RAX值()中,避免错误的依赖关系movzx
- 减去循环后的指针,而不是增加循环内的计数器
当然,SSE2在x86-64中总是可用的,所以我们应该使用它来签入16字节的块(在到达对齐边界之后)。请参阅glibc中的优化手写
strlen
实现。()。cmp-ebx,0
是错误的,并且末尾的推送ebx
可能是弹出ebx
(否则会导致堆栈溢出!)。糟糕。我误将pop ebx
手动复制为push ebx
。试着养成复制和粘贴实际代码的习惯,而不是重新键入代码。另外,请编辑您的问题,使其与实际代码相匹配。@paul-r这是我通常做的。我在VirtualBox中工作,剪贴板共享设置不正确。指针增量更好,但糟糕的循环结构是许多CPU上的瓶颈。在每个循环迭代中有两个分支(一个执行,一个未执行),因此它只能在Haswell之前的CPU上每2个时钟运行一次迭代,即编写本文时存在的分支。(当然,对于使用SSE2的现代x86来说,一次只检查一个字节是非常糟糕的。)