我正在研究一个包含两个过程的程序。一个将N个无符号双字的数组压入堆栈,另一个将N个无符号双字的堆栈弹出并存储在数组中。我能够将所有元素成功推送到堆栈,但是由于堆栈指针(esp
寄存器)已更改,因此该过程无法返回到主程序。
通过操纵esp
寄存器,我可以返回到 main ,以便保存返回地址,并在返回之前将该地址重新加载到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
答案 0 :(得分:2)
检查您的房屋。在第一段中,您将讨论将N个无符号双字数组推入堆栈的过程,但是您的代码处理了N个无符号 bytes 数组。 >
此外,我观察到您在控制台上的输出将以相反的顺序(到错误),并且您的代码在读取输入数组时将其归零。我将所有这些内容保留在下面的解决方案中。
前2个摘要将保留ECX
和EDX
。他们会破坏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
如果不需要保留ECX
和EDX
寄存器,但仍然允许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
答案 1 :(得分:1)
当过程P需要存储其寿命超过P寿命的数据时,该数据不能存储在P的堆栈帧内的堆栈上,因为,正如您所发现的,当P消失时,该数据会消失返回。
还有其他两个可行的选择。
让调用过程(主程序)在其堆栈帧中为数据分配空间,并将指向该空间的指针传递给P。如果调用者知道或可以确定多少数据P的最大大小,则此方法有效将产生。调用方应始终将大小与指针一起传递,以使P不会超出分配的空间。
在P中使用malloc(或某些等效项)并将指向数据的指针返回给调用方。