首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用IMUL指令将数组中的值相乘会产生不正确的值

使用IMUL指令将数组中的值相乘会产生不正确的值
EN

Stack Overflow用户
提问于 2018-03-11 17:04:34
回答 1查看 843关注 0票数 4

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

要求: 将整数数组int_array的负元素乘以指定的整数inum

下面是我上面的代码:

C代码:

代码语言:javascript
运行
复制
#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代码:

代码语言:javascript
运行
复制
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)查找数组中哪些数组元素为负值,对于找到的每个正元素,什么都不做,继续循环到下一个元素。

代码语言:javascript
运行
复制
 cmp [int_ar +ecx*4-4], dword 0 
 jg .ifpositive 

 .ifpositive:
    loop multiply_loop
    leave
    ret

2)在发现负元素后,将其值移动到寄存器EBX中,在IMUL SRC函数中充当SRC。然后将寄存器EAX扩展到extend,其中结果存储在:

代码语言:javascript
运行
复制
mov ebx, [int_ar +ecx*4-4]
    imul ebx
    cdq

3)使用MOV将结果移到数组的负元素中:

代码语言:javascript
运行
复制
mov [int_ar +ecx*4-4], eax

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-03-11 19:05:33

不正确值的原因

如果我们忽略了低效和不需要的代码,并处理真正的问题,那么它可以归结为这样的指令:

代码语言:javascript
运行
复制
mov eax, inum

inum是什么?您在C中创建并初始化了一个名为inum的全局变量:

代码语言:javascript
运行
复制
int iaver, inum;
[snip]
inum = 2;

inum作为一个变量,本质上是一个包含int (32位值)的内存位置的标签。在程序集代码中,需要将inum视为指向值的指针,而不是值本身。在程序集代码中,需要更改:

代码语言:javascript
运行
复制
mov eax, inum

至:

代码语言:javascript
运行
复制
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 cdecl (代表C声明)是一种调用约定,起源于C编程语言,被许多C编译器用于x86体系结构。在cdecl中,子例程参数被传递到堆栈中。整数值和内存地址在EAX寄存器中返回,浮点值在ST0 x87寄存器中返回。寄存器EAX、ECX和EDX是调用者保存的,其余的是调用保存的。x87浮点寄存器ST0到ST7在调用新函数时必须为空(弹出或释放),而ST1到ST7在退出函数时必须为空。当不用于返回值时,ST0也必须是空的。

您的代码破坏程序EAX、EBX、ECX和EDX。您可以自由地销毁EAX、ECX和EDX的内容,但您必须保留EBX。如果不这样做,则会导致调用该函数的C代码出现问题。在执行enter 0,0指令之后,您可以使用push ebx,而就在每条leave指令之前,您可以执行pop ebx

如果要使用-O1-O2-O3 GCC编译器选项来启用优化,则程序可能无法按预期工作或完全崩溃。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49222679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档