在程序集 x86 中交换两个 int 指针

Swapping two int pointers in assembly x86

我想交换两个 int 指针。

我可以像这样用 C 来做

void swap(int* a, int* b)
    int temp = *a;
    *a = *b;
    *b=temp;

现在我正尝试在汇编中执行此操作,但请耐心等待,我不明白为什么这不起作用

push %ebp
mov %esp,%ebp   

mov 8(%ebp), %ecx       #a 
mov 12(%ebp), %ebx      #b

mov (%ecx), %eax        #move o a para temp
mov (%ebx), %ecx        #move o b para o a
mov (%eax), %ebx        #move o temp para o b

mov %ebp, %esp
pop %ebp
ret

谁能解释一下我做错了什么?

正如风向标所说,您显示的 C 代码没有交换两个 int 指针。它正在交换两个 int 指针指向的两个 int。

但是在您的程序集中,您似乎在尝试交换由两个 int 指针指向的两个 int,因此您的代码并非完全失败。

研究这个以了解它的作用,然后尝试一下:

mov (%ecx), %eax
mov (%ebx), %edx
mov %edx, (%ecx)
mov %eax, (%ebx)

您的 C 代码试图交换指针指向的值,但您的汇编代码似乎将值视为指向 segmentation faults 的指针。处理这个问题的一种方法是使用额外的寄存器来保存指针和它们指向的值。

32-bit CDECL允许我们使用EAXECXEDX 而不用担心保存他们的价值。我们需要第 4 个寄存器,我们必须自己保存它。

我还假设您需要帧指针(没有它也可以编写例程):

push   %ebp
mov    %esp,%ebp
push   %ebx              # Preserve non-volatile EBX register
mov    8(%ebp),%eax      # EAX = Pointer to a (arg1)
mov    12(%ebp),%edx     # EDX = Pointer to b (arg2)
mov    (%eax),%ecx       # Temporarily save value at memory address a (*a) to ECX
mov    (%edx),%ebx       # Temporarily save value at memory address b (*b) to EBX
mov    %ebx,(%eax)       # Move value of b (EBX) to memory address of a (*a)
mov    %ecx,(%edx)       # Move value of a (ECX) to memory address of b (*b)
pop    %ebx              # Restore non-volatile EBX register
pop    %ebp
ret

理论上您可以完全删除堆栈帧(可能会使调试器中的堆栈跟踪更难跟踪)。该代码可以使用 ESP 来访问参数而不是 EBP:

push   %ebx              # Preserve non-volatile EBX register
mov    8(%esp),%eax      # EAX = Pointer to a
mov    12(%esp),%edx     # EDX = Pointer to b
mov    (%eax),%ecx       # Temporarily save value at memory address a (*a) to ECX
mov    (%edx),%ebx       # Temporarily save value at memory address b (*b) to EBX
mov    %ebx,(%eax)       # Move value of b (EBX) to memory address of a (*a)
mov    %ecx,(%edx)       # Move value of a (ECX) to memory address of b (*b)
pop    %ebx              # Restore non-volatile EBX register
ret

注意:无法保留(根据 32 位 CDECL 调用约定)非易失性寄存器,如 EBX EBPESIEDI 是一个错误。在简单的测试代码中,它可能看起来有效,但在更复杂的优化代码中,如果不严格遵守调用约定,您可能会遇到未定义的行为。