RISC-V递归调用栈使用疑问:a0指针为何未恢复原值?
递归反转字符串中寄存器与栈存储的疑问解答
为什么需要保存a0和a1寄存器?
在RISC-V的调用规范里,a0-a7属于调用者保存寄存器(同时承担参数/返回值寄存器的作用)——被调用函数可以直接使用甚至修改这些寄存器的值。回到你的代码逻辑:
- 在
trecrev调用excharr前,你把a1从「字符串长度-1的偏移量」改成了「末尾字符的内存地址」(add a1, a1, a0); - 如果不提前把原始的
a0(字符串起始地址)和a1(偏移量)保存到栈上,调用完excharr后,你就无法找回这两个原始值,也就没法计算下一层递归的参数(addi a0, a0, 1和addi a1, a1, -2)。
简单说:保存a0/a1是为了临时修改它们作为其他函数的参数后,能恢复原始值继续执行递归逻辑。
为什么加载回a0后字符串没有恢复原状?
这是对指针和栈存储的核心误解:
- 你保存到栈里的是
a0寄存器中的指针数值(也就是字符串STR的内存地址),而非指针指向的字符数据; excharr修改的是a0/a1指向的内存区域里的字符(直接操作STR所在的内存空间),栈里的指针数值只是记录了「字符串在内存的哪个位置」,恢复a0只是让寄存器重新指向这个地址,并不会改变已经被修改的内存内容。
举个具体流程例子:
- 第一次进入
trecrev时,a0是STR的地址,a1是7; - 把
a0(STR地址)、a1(7)存到栈,然后把a1改成STR+7,调用excharr交换STR[0]和STR[7]; - 从栈加载回
a0(STR地址)和a1(7)——此时寄存器恢复,但STR内存里的第一个和最后一个字符已经完成交换; - 接着调整
a0和a1为下一层递归的参数,继续执行直到完成整个字符串的反转。
所以字符串不会恢复,因为修改的是内存中的字符数据,栈里仅保存了指针的地址,恢复指针不影响已经修改的内容。
附完整代码
STR: DC "Hadoukan\0" main: addi a0, x0, STR jal ra, strlen addi a1, a0, -1 addi a0, x0, STR jal ra, trecrev addi x5, x0, STR ecall x0, x5, 4 ebreak x0,x0, 0 trecrev: bge x0, a1, trecrevbase addi sp, sp, -24 sd ra, 0(sp) sd a0, 8(sp) sd a1, 16(sp) add a1, a1, a0 jal ra, excharr ld ra, 0(sp) ld a0, 8(sp) ld a1, 16(sp) addi sp, sp, 24 addi a0, a0, 1 addi a1, a1, -2 jal x0, trecrev trecrevbase: jalr x0, 0(ra) ;;; EXCHARR ;;; expects two arguments (pointers to characters) and ;;; exchanges the two characters excharr: lb t0, 0(a0) lb t1, 0(a1) sb t1, 0(a0) sb t0, 0(a1) jalr x0, 0(ra) ;;; STRLEN ;;; expacts one argument pointer to char and returns the length of the ;;; string. ;;; t0 -> counter ;;; t1 -> temp to read the char in. strlen: addi t0, x0, 0 strlenloop: lb t1, 0(a0) beq t1, x0, strlenend addi t0, t0, 1 addi a0, a0, 1 jal x0, strlenloop strlenend: addi a0, t0, 0 jalr x0, 0(ra)
内容的提问来源于stack exchange,提问作者Bob Armstrong
相关产品推荐
相关产品推荐

