1

这是一个非常基本的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与与此问题无关的文件一起编译,生成的 GCC 代码lx_strlen被内联到main(at -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。知道它是如何工作的吗?

4

1 回答 1

2

该代码正在使用对bnezl延迟槽指令进行特殊处理的指令:仅在采用分支时才执行。因此,您的代码将始终使用$a0上一次迭代中的 at ,因为subu a0,v0,a1at400530不会为退出循环的最后一个执行。请注意,400520 $a0对于零长度字符串的情况,at 为零。

于 2019-03-11T02:04:47.963 回答