在MIPS中迭代链表链表、迭代、MIPS

2023-09-03 08:50:08 作者:初見

这是我第一次使用汇编,我正在尝试实现一个链表。 每个节点有两个字--第一个是节点的值,第二个是列表中下一个节点的地址。对于最后一个节点,NEXT为零。 列表的底部是包含第一个节点地址的单词,如果列表为空,则为0。

我正在尝试实现函数&Add Item&Quot;,其中第一个参数($a0)是列表基址的地址,$a1是存储我想要添加到列表中的值的地址。 为此,我尝试遍历列表并查找最后一项,因此将其";Next";值设置为我创建的新节点。

我觉得这很难看(我同时使用了&循环和&增量),并且我错过了一种更简单的遍历列表的方法,但我有点困惑如何使用一个循环正确地完成这项工作,因为我想在列表结束前停止一步 实现这一目标的更好方法是什么?

LeetCode 2 两数相加 链表 迭代

谢谢!!

AddItem:
    # first I make the new node:
    addi $sp,$sp,-8         # we make room in stack for the new item
    lw $t0,0($a1)           # load new item's value from its address
    sw $t0,0($sp)           # set new node's value
    sw $zero,4($sp)         # set new node's next to 0 because it's now the last item
    # now I want to find where to put it:
    lw $t2,0($a0)           # $t2 contains the address of the first node in the list
    beq $t2,$zero,AddFirstItem  # in case the list is empty and we add the first item
    # if the list is not empty we need to find the last item:
    add $t0,$zero,$t2       # initialize $t0 to point to first node
    loop:
    lw $t1,4($t0)           # $t1 is the address of next item
    bne $t1,$zero,increase
    j addItem           # when we reach here, $t0 is the last item of the list
    increase:           # we iterate on the items in the list using both "loop" and "increase"
    add $t0,$t1,$zero       # $t0 which is the current item is now updates to current item's next
    j loop
    
    addItem:
    sw $sp,4($t0)           # set current item ($t0) next to be the node we created 
    j EndAddItem
    
    AddFirstItem:
    sw $sp,0($a0)       # setting the base of the list to the node we created
    
    EndAddItem:
    jr $ra

推荐答案

您用于遍历列表的代码是正确的,看起来有点像这样:

...
while ( node != null ) {
    prev = node;
    node = node -> next;
}
// node is null and prev is the last non-null pointer

就改进而言,如下所示:

    bne $t1,$zero,increase
    j addItem           # when we reach here, $t0 is the last item of the list
increase:

此条件分支围绕无条件分支进行分支。*这是不必要的,因为我们可以反转条件并交换分支目标:

    beq $t1,$zero,addItem
    # j addItem           # when we reach here, $t0 is the last item of the list
#increase:

您将能够删除j addItem,并且也不再需要标签increase

为了进一步改进,您的主要列表对象可以保留指向列表末尾的指针。虽然在向列表添加节点和从列表中删除节点时需要更多维护,但它将消除查找列表末尾的搜索循环。

就内存分配而言,您是在为持久化函数实例化的数据结构节点分配堆栈上的节点。这在短期内可能是可行的,但从长期来看,在运行时堆栈上分配持久数据结构非常容易出错。*函数应该返回到其调用方,堆栈的顶部(即堆栈指针的值)位于它被调用时的相同位置。如果此函数的任何调用方以正常方式使用了堆栈,他们将无法找到它们基于堆栈的变量/存储。

mars&;QTSPIM没有正确的mallocfree函数,但这些函数应该用于永久内存分配。这些模拟器有sbrk可以替代malloc,但没有相应的free操作。(我们可以说把合适的mallocfree留给读者练习;)

您可以更改堆栈头,但必须将其恢复到返回时的位置。通过这种方式,堆栈用于其生存期与函数调用的持续时间匹配的任何存储需求。

例如,假设main调用AA调用BB调用C。因此,当main使用jal A时,它传递A中一个(从C中隐藏)参数,该参数告诉A如何返回。但是,在A结束之前,它调用B,使用jal B。因为jal将重新使用$ra寄存器,现在作为B返回A的参数。如果没有缓解,A$ra参数(返回到main)将被清除。因此,在A使用jal调用B之前,它会保存原始的$ra值。因为它需要在结尾有这个值,这样才能返回到main。因此,在A调用的持续时间内,A将其$ra值存储在堆栈上。这在逻辑上释放了$ra寄存器,以便重新利用它来调用BB中也发生了类似的情况。如果C是我们所称的叶函数(不进行任何函数调用),则C将简单地离开$ra并在结束时使用它。C返回到B,而B从它分配的堆栈内存中获取它的返回地址,释放它分配的堆栈内存,并返回到AA还获取它存储在堆栈分配的内存中的返回地址,它可以找到它,因为堆栈指针与它存储返回地址时具有相同的值:A操作没有改变堆栈顶部,它在调用B后位于相同的位置-尽管B也分配了(并释放)一些内存。

总而言之,在许多情况下,函数需要一些本地存储,其生存期与函数调用的持续时间完全匹配。堆栈是一种廉价的内存分配机制,以满足这些需求。