3

我正在开发一个包含两个程序的程序。一种将包含 N 个无符号双字的数组推入堆栈,另一种将 N 个无符号双字从堆栈中弹出并将它们存储在数组中。esp我能够成功地将所有元素推入堆栈,但是由于堆栈指针(寄存器)已更改,因此该过程无法返回主程序。

我已经能够通过操作寄存器返回到mainesp以便保存返回地址,并esp在返回之前重新加载该地址。但是,在调用下一个过程时,我推入堆栈的条目已被覆盖。

在过程中工作时是否有正确的方法将数据保存在堆栈中?

这是我的一些代码:

主要程序:

main PROC
main_loop:
; Main menu
    mov edx, offset choicePrompt
    call WriteString

    read_input:
        call ReadInt
        jno good_input
        jmp main_loop

    good_input:
        cmp eax, 0
        je create_vector
        cmp eax, 1
        je array_stack
        cmp eax, 2
        je stack_array
        cmp eax, -1
        je end_program

        call crlf
        jmp main_loop

    create_vector:
        call CreateVector
        jmp end_options

    array_stack:
        call ArrayToStack
        jmp end_options

    stack_array:
        call StackToArray
        jmp end_options

    end_options:
        call crlf
        jmp main_loop

end_program:
    mov edx, offset exitPrompt
    call WriteString
    call crlf
exit
main ENDP

ArrayToStack过程中将数组推入堆栈:

mov esi, offset Vector
mov ebx, N
dec ebx
mov eax, 0
mov ecx, -1

push_array_loop:
    inc ecx
    mov al, [esi + ecx]
    push eax
    mov [esi + ecx], byte ptr(0)
    cmp ecx, ebx
    jl push_array_loop

在StackToArray过程中将堆栈写入控制台:

mov eax, N
mov ebx, 4
mul ebx
mov ebx, eax
mov ecx, 0
write_stack_loop:
    mov eax, [esp + ecx]
    add ecx, 4
    call WriteDec
    mov edx, offset spacePrompt
    call WriteString
    cmp ecx, ebx
    jl write_stack_loop
4

2 回答 2

2

检查您的场所。在您的第一段中,您讨论了一个将 N 无符号双字数组推入堆栈的过程,但您的代码处理 N 无符号字节数组。

此外,我观察到您在控制台上的输出将是相反的顺序(与数组),并且您的代码在读取输入数组时将其归零。我把所有这些东西都保存在下面的解决方案中。

前 2 个片段将保留ECXEDX。他们做破坏EAX

对您的编码问题的真正解释当然是查看堆栈是如何随着每一步而被修改的。仔细观看!

ArrayToStack:

                                                  [ ret ]
                                                  ^ esp

    mov     eax, N                 ; The number of array elements is a runtime value
    dec     eax
    shl     eax, 2
    sub     esp, eax

                             <-- eax = (N-1)*4 -->
                             [     ][ ... ][     ][ ret ]
                             ^ esp

    push    dword ptr [esp + eax]

                      [ ret ][     ][ ... ][     ][     ]
                      ^ esp

    push    ecx
    push    edx

        [ edx ][ ecx ][ ret ][     ][ ... ][     ][     ]
        ^ esp

    xor     ecx, ecx
  ToStack:
    xor     edx, edx
    xchg    dl, [Vector + ecx]     ; Reading byte-sized element while zeroing the source
    mov     [esp + 12 + eax], edx
    inc     ecx
    sub     eax, 4
    jnb     ToStack

        [ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
        ^ esp                ^ esp+12

    pop     edx
    pop     ecx

                      [ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
                      ^ esp

    ret                            ; EAX ends at -4

                             [ a_N ][ ... ][ a_2 ][ a_1 ]
                             ^ esp


StackToConsoleProcedure:

                      [ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
                      ^ esp

    push    ecx
    push    edx

        [ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
        ^ esp                ^ esp+12

    xor     ecx, ecx
  FromStack:
    mov     eax, [esp + 12 + ecx*4]
    call    WriteDec
    mov     edx, offset spacePrompt
    call    WriteString
    inc     ecx
    cmp     ecx, N
    jb      FromStack
    shl     ecx, 2                 ; ECX = N*4
    mov     eax, [esp + 8]         ; Fetch return address
    mov     [esp + 8 + ecx], eax

                      <-------- ecx = N*4 ------->
        [ edx ][ ecx ][     ][ a_N ][ ... ][ a_2 ][ ret ]
        ^ esp         ^ esp+8

    mov     eax, ecx
    pop     edx
    pop     ecx

                      <-------- eax = N*4 ------->
                      [     ][ a_N ][ ... ][ a_2 ][ ret ]
                      ^ esp

    add     esp, eax

                                                  [ ret ]
                                                  ^ esp


    ret                            ; EAX ends at N*4

如果不需要保留ECXandEDX寄存器,但仍然允许EAX被破坏:

ArrayToStack:
    mov     eax, N
    dec     eax
    shl     eax, 2
    sub     esp, eax
    push    dword ptr [esp + eax]
    xor     ecx, ecx
  ToStack:
    xor     edx, edx
    xchg    dl, [Vector + ecx]
    mov     [esp + 4 + eax], edx
    inc     ecx
    sub     eax, 4
    jnb     ToStack
    ret

StackToConsoleProcedure:
    xor     ecx, ecx
  Fromtack:
    mov     eax, [esp + 4 + ecx*4]
    call    WriteDec
    mov     edx, offset spacePrompt
    call    WriteString
    inc     ecx
    cmp     ecx, N
    jb      FromStack
    shl     ecx, 2
    pop     eax
    add     esp, ecx
    push    eax
    ret
于 2019-03-17T20:31:13.753 回答
1

当过程 P 需要存储生命周期超过 P 生命周期的数据时,数据不能存储在 P 的堆栈框架内的堆栈中,因为正如您所发现的,当 P 返回时它“消失”了。

以下是其他几个可行的选项。

  1. 让调用过程(主)在其堆栈帧中为数据分配空间,并将指向该空间的指针传递给 P。如果调用者知道或可以确定 P 将生成多少数据的最大大小,则此方法有效。调用者应始终将大小与指针一起传递,因此 P 不会超过分配的空间。

  2. 在 P 中使用 malloc (或某些等效项)并将指向数据的指针返回给调用者。

于 2019-03-15T01:56:51.547 回答