我正在学习 ASM 语言并在 Ubuntu Eclipse C++ 上尝试 IMUL 函数,但由于某种原因,我似乎无法从我的代码中获得所需的输出。



这是我上面的代码:

代码:

#include <stdio.h>
extern void multiply_function();

// Variables
int iaver, inum;
int int_ar[10] = {1,2,3,4,-9,6,7,8,9,10};

int main()
{
    inum = 2;
    multiply_function();
    for(int i=0; i<10; i++){
        printf("%d ",int_ar[i]);
    }
}

ASM 代码:
extern int_ar
extern inum
global multiply_function

multiply_function:

    enter 0,0
    mov ecx, 10
    mov eax, inum

multiply_loop:
    cmp [int_ar +ecx*4-4], dword 0
    jg .ifpositive
    mov ebx, [int_ar +ecx*4-4]
    imul ebx
    cdq
    mov [int_ar +ecx*4-4], eax
    loop multiply_loop
    leave
    ret

.ifpositive:
    loop multiply_loop
    leave
    ret

问题

对于以下数组: {1,2,3,4,-9,6,7,8,9,10}inum ,我得到输出 {1,2,3,4,-1210688460,6,7,8,9,10} ,它暗示发生了某种溢出。

关于 x86 汇编语言中的 IMUL 函数如何工作,我是否遗漏或理解错误?

预期产出

我期望的输出是 {1,2,3,4,-18,6,7,8,9,10}
我的思考过程

我对上述任务的思考过程:

1) 找出数组中哪些数组元素为负数,对于找到的每个正数元素,什么都不做,继续循环到下一个元素
 cmp [int_ar +ecx*4-4], dword 0
 jg .ifpositive

 .ifpositive:
    loop multiply_loop
    leave
    ret

2) 找到负元素后,将其值移入寄存器 EBX,该寄存器将用作 IMUL SRC 函数中的 SRC。然后将寄存器 EAX 扩展到 EAX-EDX,结果存储在:
mov ebx, [int_ar +ecx*4-4]
    imul ebx
    cdq

3) 使用 MOV 将结果移动到数组的负元素中:
mov [int_ar +ecx*4-4], eax

4) 循环到下一个数组元素,重复上面的1)-3)

最佳答案

值不正确的原因

如果我们看看低效率和不需要的代码并处理真正的问题,它归结为以下指令:

mov eax, inum

什么是 inum ?您在 C 中创建并初始化了一个名为 inum 的全局变量:
int iaver, inum;
[snip]
inum = 2;
inum 作为变量本质上是包含 int(32 位值)的内存位置的标签。在您的汇编代码中,您需要将 inum 视为指向值的指针,而不是值本身。在您的汇编代码中,您需要更改:
mov eax, inum

至:
mov eax, [inum]

您的版本所做的是将 inum 的地址移动到 EAX 中。您的代码最终将变量的地址乘以数组中的负数。这会导致您看到的错误值。 inum 周围的方括号告诉汇编器您想将 inum 视为内存操作数,并且您想将 inum 处的 32 位值移动到 EAX 中。

调用约定

您似乎正在创建一个 32 位程序并在 32 位 Ubuntu 上运行它。我可以通过返回的错误值 -1210688460 来推断 32 位 Linux 的可能性。 -1210688460 = 0xB7D65C34 除以 -9 得到 804A06C。 32 位 Linux 上的程序通常从 0x8048000 开始加载

无论是在 32 位 Linux 还是 64 位 Linux 上运行,与 32 位 C/C++ 程序链接的汇编代码都需要遵守 CDECL calling convention :



您的代码破坏了 EAX、EBX、ECX 和 EDX。您可以随意销毁 EAX、ECX 和 EDX 的内容,但必须保留 EBX。如果不这样做,可能会导致调用该函数的 C 代码出现问题。在执行 enter 0,0 指令之后,您可以执行 push ebx,并且在每个 leave 指令之前,您可以执行 pop ebx
如果您要使用 -O1-O2-O3 GCC 编译器选项来启用优化,您的程序可能无法按预期运行或完全崩溃。

关于c - 使用 IMUL 指令将数组中的值相乘会产生不正确的值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49222679/

10-11 15:36