Итерация върху свързан списък в MIPS

За първи път използвам асемблиране и се опитвам да внедря свързан списък. Всеки възел е 2 думи - първата е стойността на възела, а втората е адреса на следващия възел в списъка. За последния възел next е нула. Основата на списъка е дума, съдържаща адреса на първия възел или 0, ако списъкът е празен.

Опитвам се да внедря функцията add item, където първият параметър ($a0) е адресът на основата на списъка, а $a1 е адрес, който съхранява стойността, която искам да добавя към моя списък. За да направя това, се опитвам да обходя списъка и да намеря последния елемент, така че задавам следващата му стойност да бъде новият възел, който създадох.

Чувствам, че това е много грозно (използвам и цикъл, и увеличение) и пропускам по-лесен начин за итерация в списъка, но съм малко объркан как да направя това правилно, използвайки един цикъл, тъй като искате да спрете 1 стъпка преди края на списъка Какъв е по-добрият начин да постигнете това?

Благодаря ти!!

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

person ronihm    schedule 29.04.2021    source източник


Отговори (1)


Вашият код за преминаване през списъка е ок и изглежда малко по този начин:

...
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 нямат подходящи malloc и free функции, но те трябва да се използват за постоянно разпределение на паметта. Тези симулатори имат sbrk, който може да замести malloc, но няма съответстваща операция free. (Можем да кажем, че правилните malloc и free са оставени като упражнение за читателя ;)

Можете да промените главата на стека, но трябва да го възстановите там, където е бил при връщане. По този начин стекът се използва за всякакви нужди от съхранение, чийто живот съответства на продължителността на извикването на функцията.

Например, да кажем, че main извиква A и че A извиква B и че B извиква C. И така, когато main използва jal A, той предава A параметър (скрит от C) в $ra, който казва на A как да се върне обратно към main. Въпреки това, преди A да приключи, той извиква B, използвайки jal B. Това jal ще преназначи регистъра $ra, сега като параметър за B, за да се върне към A. Без смекчаване параметърът $ra на A (за връщане към main) се изтрива. Следователно, преди A да използва jal за извикване на B, той запазва оригиналната стойност $ra. Нуждае се от тази стойност в края, за да може да се върне към main. И така, по време на извикването на A, A съхранява своята $ra стойност в стека. Това логично освобождава регистъра $ra, за да бъде преназначен за извикване на B. Подобно се случва в B. Ако C е това, което наричаме листова функция (не прави никакви извиквания на функция), тогава C просто ще остави $ra сама и ще я използва в края. C се връща към B, а B извлича адреса си за връщане от паметта на стека, която е разпределил, освобождава паметта на стека, която е разпределил, и се връща към A. A също извлича адреса за връщане, който е съхранил в разпределената памет на стека, който може да намери, тъй като указателят на стека има същата стойност, както когато е съхранил адреса за връщане: операцията jal B, що се отнася до A, не е променила върха на стека , той е на същото място след извикване на B — въпреки че B също е разпределил (и освободил) малко памет.

В обобщение, има редица сценарии, при които дадена функция се нуждае от локално хранилище, чийто живот съвпада точно с продължителността на извикването на функцията. Стекът е механизмът за евтино разпределение на паметта, за да отговори на тези нужди.

person Erik Eidt    schedule 29.04.2021
comment
Благодаря ти! Относно разпределението на паметта - това е добра точка, която не разбирам напълно. Къде е правилното място за съхраняване на данни в дългосрочен план? Освен това, ако не трябва да променям главата на стека, каква е ползата от това? само за временни операции ли се използва? - person ronihm; 29.04.2021
comment
Добавих допълнителен материал, за да отговоря на тези въпроси. - person Erik Eidt; 29.04.2021
comment
Благодаря ти много! - person ronihm; 29.04.2021