汇编cmp指令与寻址疑问:strlen函数中不同cmp写法的异常原因解析
嘿,作为汇编新手碰到这些寄存器和内存的差异太正常了,我来帮你逐个拆解清楚每个问题的本质!
首先先确认你的正确代码逻辑:
.loop: inc rax inc rbx cmp [rbx], byte 0 jne .loop ret
这段代码能正常工作,是因为[rbx]表示取出rbx寄存器里存储的地址所指向的内存中的1个字节,和字符串末尾的byte 0(null终止符)比较,判断是否循环结束。这里的核心是你在访问内存里的字符串内容。
接下来逐个分析你的疑问:
1. 为什么cmp bl, 0会得到错误的长度?
bl是rbx寄存器的低8位部分,它存储的是指针本身的低8位数值,而不是指针指向的内存里的字符。
你的代码里rbx是字符串的指针,每次inc rbx是让指针往高地址移动(比如从0x12345678变成0x12345679),而bl只是这个地址的最后两位(比如0x78、0x79)。拿bl和0比较,完全不是在判断字符串的null终止符,自然会算出错误的长度。
2. 为什么cmp [bl], 0会报「invalid effective address」错误?
x86-64架构的汇编规则里,内存寻址不能使用8位寄存器(比如bl、al、cl等)作为基址寄存器,必须用32位(如ebx)或64位(如rbx)的通用寄存器来做内存寻址的基址。所以[bl]这种写法是非法的,汇编器直接拒绝编译。
3. 为什么cmp rbx, 0永远无法停止?
你的猜测完全正确!rbx是字符串的指针,初始值是字符串的起始地址(肯定不是0,因为0地址在系统里是无效的空指针),每次inc rbx会让指针往更高的内存地址移动,数值只会越来越大,永远不可能等于0。所以jne .loop永远成立,循环会无限跑下去。
那为什么有些人能用cmp bl, 0?
他们的代码逻辑和你的不一样!正确使用cmp bl, 0的场景,是先把内存里的字符加载到bl寄存器中,再做比较,比如:
; 假设rax初始为0,rbx指向字符串首地址 .loop: mov bl, [rbx] ; 把rbx指向的内存字节读到bl寄存器里 cmp bl, 0 ; 现在bl里存的是字符串的当前字符,和null比较 je .done ; 是null就退出循环 inc rax ; 长度+1 inc rbx ; 指针后移 jmp .loop .done: ret
这里bl里存的是字符串的字符内容,而不是指针的一部分,所以cmp bl, 0才能正确判断终止符。
总结一下核心区别:
[rbx]:访问rbx指向的内存内容rbx/bl:使用寄存器本身存储的数值- 内存寻址只能用32/64位寄存器,不能用8位寄存器
内容的提问来源于stack exchange,提问作者cassepipe

