计组p2碎碎念
// 上机后补充:本次上机的三道编程题分别是【删除一个数字中的某些位,输出最小的可能值(提供了C代码)】、【分解自然数n为x1 + x2 + …的形式并按字典序输出(提供了C代码)】、【双关键字的排序(提供了冒泡排序代码)】。前两题直接翻译题示C代码可以一次通过,第三题只需要按第一个关键字排序后再按第二个排即可,第一次排的时候记得把a、b数组都交换一下,第二次排的时候只有a数组值相等的情况下才交换,逻辑比较简单,直接写出C代码并照翻即可。总的来说本次上机题比较简单,注意做题心态,细心翻译就行。
p.s. 上机前还在跟舍友猜测会不会考二维数组在图论算法中的应用之类的,还翻了一遍Floyd和Dijkstra,大一程设的东西差不多都忘了,不可谓不焦虑。)
1. 优雅的i-j二重循环写法(n重类似)
if-else / for / while / switch-case语句的mips表达直接翻译都很容易,不特别提出了
li $t0, 0
I_Begin:
slt $t2, $t0, $s0
beq $t2, $0, I_end
*#-----------------*
li $t1, 0
J_Begin:
slt $t2, $t1, $s0
beq $t2, $0, J_end
*#-----------------*
*#operation*
*#-----------------*
addi $t1, $t1, 1
j J_Begin
J_end:
*#-----------------*
*#Operation*
*#-----------------*
*#-----------------*
addi $t0, $t0, 1
j I_Begin
I_end:
jr $31
或许已经有人发现了,做法就是先写一个I_loop,在中间划出一块来填充operation(一重循环),然后将I_loop的所有内容复制一遍,将t0寄存器改成t1,I改成J,然后粘贴到I_loop的operation块中(记得删除J_End中的jr $31),在J_loop的operation块中填写想要的操作即可。如果需要n重循环,就机械地重复复制、修改、粘贴的操作就可以了。
// 注意:这种写法是标准的for循环写法,如果将判断放在计数变量的add操作后,会变成do-while循环,这样非常容易导致代码翻译时的逻辑错误。
// 上机后补充:通过观察水群同学的发言,发现确实有人因为这一点翻译错了题示代码,从而WA了。
2. 将需要进行的操作都分成一个个块,开头读完需要的操作数并进行初始化后,按顺序jal [BLOCKNAME],然后按部就班结束程序,接着再详细书写每一个BLOCK,但切记BLOCK结尾要jr $31
也就是 [模块化开发] 的思想
3. 二位数组的内存处理问题,以16*16的int型数组举例,写法比教程更优雅
好好利用 .macro 对代码进行复用,可以极大地增强代码可读性;但要注意:不允许出现嵌套宏指令!
.data
matrix: .word 0 :256 *#读写int型数据时按字读写,使用.word / lw / sw;相应地,按字节读写的char型数据应使用.space / lb / sb*
.macro CalTheAddress(%dst, %row, %column, %rank)
multu %row, %rank
mflo %dstaddu %dst, %dst, $column
sll %dst, %dst, 2
.end_macro
.text
jal Initial
jal PutValue
Initial:
li $s0, 16 *#s0 = num of rows*
li $s1, 16 *#s1 = num of columns*
li $t0, 0 *#t0 = row counter, i*
li $t1, 0 *#t1 = column counter, j*
li $t2, 0 *#t2 = the value to be stored*
jr $31
PutValue:
li $t0, 0
I_Begin:
slt $t3, $t0, $s0
beq $t3, $0, I_End
*#-----------------*
li $t1, 0
J_Begin:
slt $t3, $t1, $s1
beq $t3, $0, J_End
*#------------------*
CalTheAddress($t3, $t0, $t1, $s0)
lw $t2, matrix($t3) *#matrix[i \* m + j] = t2*
*#------------------*
addi $t1, $t1, 1
j J_Begin
I_End:
*#-----------------*
addi $t0, $t0, 1
j I_Begin
I_End:
jr $31
4. 在数据段中可以使用.eqv声明静态数据符号,用第二个操作数代替第一个操作数,类似C中的define
.eqv MAXN 100
li $a0, MAXN
li $v0, 1
syscall
5. 递归时的参数存储与递归回溯操作
存取的对象是:函数参数,$ra寄存器,(如果在循环中)循环计数变量i等:因为我们同时需要维护 pc值 以及每一层递归时使用的变量,包括函数内的临时变量
其实不仅仅是递归,任何需要出入栈维护寄存器的操作,都可以这样实现(倒不如说就是这样实现)
.macro store(%reg)
sw %reg, 0($sp)
addi $sp, $sp, -4
.end_mcro
.macro load(%reg)
addi $sp, $sp, 4
lw %reg, 0($sp)
.end_macro
*#......*
store($a0)
store($a1)
store($t0)
store($ra)
addi $a0, $a0, 1 *#请注意:进行递归操作时尽量把函数参数都存在$a_寄存器中*
jal dfs *#dfs(x + 1)*
load($ra)
load($t0)
load($a1)
load($a0)
*#......*
jr $31
6. 让代码更好看
这不是也没好看到哪里去吗。)
*#Ⅰ. 读数#*
.macro ReadInteger(%n)
li $v0, 5
syscall
move %n, $v0
.end_macro
*#Ⅱ. 打印#*
.macro PrintInteger(%n)
move $a0, %n
li $v0, 1
syscall
.end_macro
*#Ⅲ. 建议的架构#*
.data
*#Your arrangement#*
.macro [FuncName](%f1, %f2, ...)
*#Your Function#*
.end_macro
.text
Read([WhatYouNeed])
jal Block_1
jal Block_2
......
jal Block_n
j end
Block_1:
*#YourBlock#*
jr $31
...
Block_n:
*#YourBlock#*
jr $31
end:
li $v0, 10
syscall
最后,感觉关注点就是二维数组 / 多重循环嵌套 / 递归这类在高级语言中很好实现,但在mips汇编语言中需要费一番功夫的东西,比如高精度运算,比如汉诺塔、哈密顿回路等经典递归题目,比如图中的Floyd算法等对二维数组的较高级应用,也比如矩阵的各种花式运算(以考察二维数组使用)等。
不过只要能把C代码正确写出,翻译工作问题还是不大的(擦泪)复习吧,请!