我读了一本书,给出了下一个例子: 有一个列表,每个成员在其中有四个第一个字节的下一个成员的地址。最后一个成员的值为0.它说下一个实现是错误的,我不明白为什么:
freeList
mov eax, [ebp+8]
cmp eax, 0
jne cont
ret
cont:
mov ebx, [eax]
mov [ebp+8], ebx
push eax
call free
pop eax
call freeList
(我不需要正确的实现,我有一个。我只需要了解这个有什么问题)
感谢。
答案 0 :(得分:2)
错误实现的最后一行是call freeList
。但是当(递归)调用完成时,它将尝试返回该行之后的不存在的代码。附加ret
指令会使代码工作,但通常你会尝试避免这种情况,除非在调试代码时,有时候有助于查看所有中间调用。相反,您可以将call freeList; ret
简化为简单的jmp freeList
。
调试高级代码的示例:
function freeList(list) {
if (list) {
var next = list->next;
free(list);
freeList(next);
}
}
如果使用optimisations进行编译,编译器可能希望通过将freeList(next)
复制到next
并执行list
来编写递归调用jmp
,但这会破坏list
的值。这意味着当您尝试调试该函数时,您无法分辨列表中的哪些元素已被释放。因此,当您尝试调试函数中的问题时,您可能希望禁用此优化。
答案 1 :(得分:0)
假设这是一个函数,不可能直接调用它,因为您需要在调用之前设置堆栈帧,然后在调用它之后展开帧。它也不保留EBX,这与大多数系统ABI相反(只有EAX,ECX和EDX是临时寄存器)
如果这是内联代码,则需要解开当前帧(不是这样)。