You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含数组的C语言循环转MIPS汇编(栈实现)的疑问咨询

关于C转MIPS汇编的疑问解析

先把原C代码贴出来方便对照:

void subr1(char *a, int v[]) { 
    int i; 
    char c[12]; 
    for (i=11; i >= 0; i--) { 
        v[i] = subr2(c[i], *a); 
    } 
}

再看看教授给出的带你的疑惑标注的汇编代码:

addiu $sp,$sp,-28    # Activation block(疑惑:为什么是-28?栈帧大小怎么算的?)
sw $ra, 24($sp)      # Space for $ra (4 bytes)
sw $s0, 20($sp)      # Space for *a(疑惑:$s0用来保存*a?为什么不用临时寄存器?)
sw $s1, 16($sp)      # Space for v[](疑惑:同理,$s1存v的地址?)
sw $s2, 12($sp)      # Space for i(疑惑:循环变量i存在$s2?)
# 这里是不是还有c[12]的空间?12字节的char数组,那栈帧里的分配怎么对应?
move $s0, $a0        # 把参数a($a0)存入$s0
move $s1, $a1        # 把参数v($a1)存入$s1
li $s2, 11           # i = 11,初始化循环变量
loop:
sll $t0, $s2, 2      # t0 = i * 4(因为v是int数组,每个元素4字节)
add $t1, $s1, $t0    # t1 = v + i*4,即&v[i]
sll $t0, $s2, 0      # t0 = i(因为c是char数组,每个元素1字节,左移0位就是i本身)
add $t2, $sp, $t0    # t2 = sp + i?不对啊,c[12]是在栈里的,那c的起始地址是哪里?
lb $a0, 0($t2)       # a0 = c[i](疑惑:这里的地址计算是不是有问题?)
lb $a1, 0($s0)       # a1 = *a,取a指向的char值
jal subr2            # 调用subr2,返回值在$v0
sw $v0, 0($t1)       # v[i] = 返回值
addi $s2, $s2, -1    # i--
bge $s2, $zero, loop # i >=0 继续循环
lw $s2, 12($sp)      # 恢复$s2
lw $s1, 16($sp)      # 恢复$s1
lw $s0, 20($sp)      # 恢复$s0
lw $ra, 24($sp)      # 恢复$ra
addiu $sp,$sp,28     # 释放栈帧
jr $ra               # 返回

接下来逐个解答你的疑惑:

  • 栈帧大小为什么是28字节?
    咱们掰着手指头算:MIPS栈是向下增长的,这个函数的栈帧需要容纳这些内容:

    • 4个需要保存的寄存器:$ra(4字节)、$s0(4字节)、$s1(4字节)、$s2(4字节)→ 加起来16字节
    • 局部char数组c[12] → 12字节
      16+12=28,刚好对应addiu $sp,$sp,-28。另外MIPS通常要求栈对齐到8字节,28是4的倍数,但这里刚好凑齐,完全符合要求。
  • 为什么用$s系列寄存器保存参数和循环变量?
    MIPS的调用约定里,$s0-$s7是保存寄存器(saved registers)——意思是如果当前函数要使用这些寄存器,必须先把它们原来的值存到栈里,函数返回前再恢复。而$t0-$t9是临时寄存器,不需要保存,调用其他函数时很可能被修改。
    这里循环变量i在整个循环过程中要持续使用,而且调用subr2的时候临时寄存器可能会被覆盖,所以用$s2保存i更稳妥;同理,*a和v[]是函数的输入参数,整个函数执行过程中都要访问,存在$s0、$s1里既不用每次重新从参数寄存器取,也能避免被临时寄存器的操作冲掉。

  • c[i]的地址计算是不是正确?
    完全没问题!你看,add $t2, $sp, $t0里的$t0就是i(sll $t0, $s2, 0就是把i的值直接赋给t0,左移0位等于没操作)。而c[12]的起始地址就是当前的$sp——因为我们刚把栈指针减了28,栈帧最底部($sp指向的位置)就是c[0],$sp+11就是c[11],所以当i=11时,$sp+11正好对应c[11],完美匹配循环的起始值。而且char是1字节,所以直接用i作为偏移量就行,不用像int数组那样乘4。

  • 关于栈帧里的空间分配对应关系
    给你理一下栈帧的布局(从高地址到低地址,因为栈向下增长):

    • 24($sp):保存的$ra(4字节)
    • 20($sp):保存的$s0(4字节)
    • 16($sp):保存的$s1(4字节)
    • 12($sp):保存的$s2(4字节)
    • 0($sp)到11($sp):char数组c[12](12字节)
      这样整个栈帧从$sp到$sp+27,总共28字节,完全对应上。

内容的提问来源于stack exchange,提问作者Arnau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:28:33