【问题标题】:Bubble Sort in MASM x86 Not Sorting After Few InterationsMASM x86中的冒泡排序在几次迭代后不排序
【发布时间】:2018-12-16 16:14:27
【问题描述】:

所以,我正在尝试使用此代码作为模板来实现 BubbleSort:

    int n = arr.length;  
    int temp = 0;  
     for(int i=0; i < n; i++){  
             for(int j=1; j < (n-i); j++){  
                      if(arr[j-1] > arr[j]){  
                             //swap elements  
                             temp = arr[j-1];  
                             arr[j-1] = arr[j];  
                             arr[j] = temp;  

但是,我的汇编代码只对前 1 到 2 次进行排序,并产生错误的结果。我尝试运行调试器,多次单步执行,但我天真的眼睛无法发现翻译中的任何错误。

.data
arr DWORD 3,2,1,4,5
temp DWORD 0
arr_j DWORD 0
; Bubble Sort
.code
main proc
mov esi, OFFSET arr
mov eax, 0 ; for outer loop
mov ebx, 1 ; for inner loop

OuterLoop:

     InnerLoop:

        ; swap elements
        ; referencing j in array
        call MULTIPLY
        add edx, esi ; edx = esi + 4*ebx that is *arr[j]
        mov edi, [edx]
        mov [arr_j], edi ; store arr[j]
        sub edx, 4
        mov edi, [edx] ; store arr[j - 1]

        cmp edi, [arr_j] ; if(arr[j-1] > arr[j]) -> swap elements
        jle FAIL_SWAP

        ; swap elements here
        mov [temp], edi
        mov edi, [arr_j]
        mov ebp, [temp]
        mov [edx], edi ; arr[j - 1] < arr[j]
        add edx, 4
        mov [edx], ebp

        FAIL_SWAP:

     inc ebx
     mov ecx, LENGTHOF arr
     sub ecx, eax
     cmp ebx, ecx
     jl InnerLoop

inc eax
cmp eax, LENGTHOF arr
jl OuterLoop     


invoke ExitProcess,0
main ENDP

MULTIPLY PROC ; multiply 4 with ebx, store at edx
    push esi

    xor esi, esi
    mov esi, 1

    mov edx, ebx

    LOOPER:
    add edx, ebx

    inc esi
    cmp esi, 4
    jl LOOPER

    pop esi
    ret
MULTIPLY ENDP
END main

非常感谢任何帮助。谢谢。

【问题讨论】:

  • 不要为temp 使用内存位置,这简直是疯狂的低效,而且更难看到发生了什么。只需加载两个寄存器,然后将它们存储回相反的位置。查看 C 版本代码的 优化 编译器输出。 (如gcc -O1)。这是一个干净且相当有效的 MASM 冒泡排序:Bubble sort in x86 (masm32), the sort I wrote doesn't work,另一个是:Assembly bubble sort swap。 (另见这个讨厌的code-golf version)。
  • @PeterCordes 感谢您的提醒。我使用 temp 尝试将 translate 伪代码转换为 ASM。问题出在我的代码中的任何想法?我真的想弄清楚我的错误是什么:(。谢谢
  • 顺便说一句,你在写一个乘以 4 的函数到底在做什么? lea edx, [esi + ebx*4] 可以替换调用+添加。或者,如果您坚持单独进行,至少使用移位来增加。
  • 给定指向 EDX 中一个元素的指针,前一个元素是 [edx-4]。或者完全避免 LEA 并使用 mov edi, [esi + ebx*4 ] / cmp edi, [esi + ebx*4 - 4],或者加载到 edx 并比较 regs(因为您不再使用 edx 来代替 &amp;arr[j])。或者只是总是增加指针,就像在我链接到的实现中一样。习惯于像 int *p = arr; do { p++ /* add esi,4*/ } while ( p &lt; endp ); ) 这样的循环来思考
  • 强烈推荐观看 Matt Godbolt 的 CppCon2017 演讲 “What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”。这不是一些随机的 youtube 视频;这是一位专家,在 C++ 会议上为包括 asm 新手(但不包括一般编程新手)的听众发表演讲。作为一个专家,我强烈推荐它。

标签: assembly x86 masm bubble-sort


【解决方案1】:
int n = arr.length;  
  int temp = 0;  
    for(int i=0; i < n; i++){  
      for(int j=1; j < (n-i); j++){
        ...

此模板代码已经有错误。外部循环执行 1 次迭代太多了!
考虑一个 n 为 2 的 2 元素数组。完整的冒泡排序将包含一个比较,但外部循环将执行 2 次迭代(i=0 和 i=1)。明显错了。

mov eax, 0 ; for outer loop
mov ebx, 1 ; for inner loop
OuterLoop:
    InnerLoop:

您的 InnerLoop 第一次运行一切正常,但第二次以BX=5 开头,因为这是循环结束时BX 的值。每次 InnerLoop 开始运行时,您都需要将 BX 寄存器重置为 1。

mov eax, 0 ; for outer loop
OuterLoop:
    mov ebx, 1 ; for inner loop
    InnerLoop:

更简单的解决方案使用EAX 作为递减计数器:

mov eax, LENGTHOF arr
dec eax
OuterLoop:
    mov ebx, 1 ; for inner loop
    InnerLoop:
        ...
        inc ebx
        cmp ebx, eax
        jbe InnerLoop
    dec eax
    jnz OuterLoop

这些将是 5 元素数组的 AXBX 的值:

AX   BX
4    1 to 4
3    1 to 3
2    1 to 2
1    1 to 1

其余代码虽然正确,但过于复杂且效率低下。参见 Peter Cordes 的 cmets。

【讨论】:

  • C 中额外的外循环迭代实际上并没有错误,因为在这种情况下内循环将运行零次。但是如果翻译成 asm 中的 do{}while() 循环,假设它们至少运行一个迭代,那就错了!编写它以使其无法转换为高效的 asm 是一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多