先来看下c语言版的冒泡排序
外循环遍历n - 1次,每次把最大的数"推"到最后,这就是冒泡名字的由来
也就是说每次外循环一次结束后,需要排序的数就减少一个,即内循环比外循环 - i
for (i = 0; i < length - 1; i++) {
int j = 0;
for (j = 0; j < length - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
然后来看汇编:
(代码思路来自于汇编语言实现冒泡排序这篇博客,思路3也许会有些抽象,本文单独抽出来在文末详细解释一下)
思路:
- 本题采用双字节存储,首先用cx记录外套循环次数,每次大循环后,次数-1,每次将cx与0比较,如果等于0,就是循环结束,直接跳出
- 用si记录内套循环中当前更换数字的下标,每次外套循环开始时就将si置为0
- 将bx加为cx 的两倍来判断内套循环结束的条件,每次都与si比较。
- 在内套循环中,将其中一个数据取到寄存器中,与内存数比较,比较完以后,将si+2,因为是采用双字节,所以每一次需要加两个地址单位。
assume cs:code,ds:data,ss:stack
data segment
arr dw 7,9,5,8,6,4,10,1,3,2
data ends
stack segment
db 10 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,9
s1:
mov si,0
cmp cx,0
je exit
dec cx
mov bx,cx
add bx,cx
s2:
mov ax,arr[si]
cmp ax,arr[si+2]
jle s3
xchg ax,arr[si+2]
xchg ax,[si]
s3:
cmp si,bx
je s1
add si,2
jmp s2
exit:
mov ax,4c00h
int 21h
code ends
end start
在上面的思路3中,大家或许会对bx加为cx 的两倍来判断内套循环结束的条件有些难以理解,我们一步一步来看:
- 程序从start标号处开始顺序执行,进入s1中先
mov si,0
cmp cx,0
je exit
,然后dec cx
,cx变成8,接下来两步:mov bx,cx
add bx,cx
意在把cx的两倍赋给bx,此时bx变为16,也就是整个数组对应c代码中的内层循环的 j,一开始j的大小也是整个数组长度 - 由于汇编代码是顺序存放的,所以接着顺序执行到s2,这两条
mov ax,arr[si]
cmp ax,arr[si+2]
比较相邻两个数,jle s3
表示如果这两个数是≤的关系,就跳转到s3,此时的两个数是7和9,满足条件,所以下面跳转到s3执行 - s3中每次先
cmp si,bx
je s1
,意在内层循环是否结束,为什么是这个意思呢,咱们接着看add si,2
jmp s2
,即si+=2,继续比较后面两个数。那对应到c代码中的哪一句呢?
两段代码对应:
- 其实整个s3标号里的内容就是内层的
for (j = 0; j < length - 1 - i; j++)
中的后面两句,而前面的j = 0
就对应着s1中的mov si,0
- s1标号里的
cmp cx,0
je exit
对应外层循环的for (i = 0; i < length - 1; i++)
中间的判断,dec cx
对应i++因为这段汇编中没有数组长度的length,所以cx一开始的9就是数组长度,之后cx就充当了外层循环的计数器