这是一个非常基本的strlen()
实现的源代码。
#include <stddef.h>
#include <stdint.h>
extern uintptr_t lx_syscall3(uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t nr);
static void lx_sys_exit(uintptr_t code)
{
lx_syscall3(code, 0, 0, 4001);
while (1);
}
static size_t lx_strlen(char const* s)
{
size_t len = 0;
while (*(s++)) {
len++;
}
return len;
}
int main() {
lx_sys_exit(lx_strlen("HELO"));
while (1);
}
与一个与此问题无关的syscall.s
文件一起编译,为lx_strlen
生成的GCC代码被内联到main
中(在-Os
处):
004004fc <main>:
4004fc: 3c1c000b lui gp,0xb
400500: 279c8154 addiu gp,gp,-32428
400504: 0399e021 addu gp,gp,t9
400508: 8f828034 lw v0,-32716(gp)
40050c: 27bdffe0 addiu sp,sp,-32
400510: 24424a64 addiu v0,v0,19044
400514: afbc0010 sw gp,16(sp)
400518: afbf001c sw ra,28(sp)
40051c: 00402825 move a1,v0
400520: 00452023 subu a0,v0,a1
# strlen loop block follows
400524: 24420001 addiu v0,v0,1
400528: 8043ffff lb v1,-1(v0)
40052c: 5460fffd bnezl v1,400524 <main+0x28>
400530: 00452023 subu a0,v0,a1
400534: 8f998118 lw t9,-32488(gp)
400538: 24070fa1 li a3,4001
40053c: 00003025 move a2,zero
400540: 04110093 bal 400790 <lx_syscall3>
400544: 00002825 move a1,zero
400548: 1000ffff b 400548 <main+0x4c>
40054c: 00000000 nop
使用qemu-mipsel
运行时,代码正确输出退出状态4
。看来一切正常,问题在于我只是不了解它如何工作。注意-1(v0)
处的偏移量400528
。因此,循环总是从v0
中存储的地址中检查前一个字节。因此,到零时,减去原始地址应该得到5
,而不是4
。知道它是如何工作的吗?
答案 0 :(得分:1)
该代码使用bnezl
指令,该指令对延迟槽指令有特殊处理:仅当采用分支时才执行。因此,您的代码将始终使用上一次迭代中的$a0
,因为subu a0,v0,a1
处的400530
不会为退出循环的最后一个执行。请注意,对于长度为零的字符串,在400520
$a0
处为零。