我正在学习ASM语言,并在Ubuntu C++上试用IMUL函数,但出于某种原因,我似乎无法从代码中获得所需的输出。
要求: 将整数数组int_array的负元素乘以指定的整数inum
下面是我上面的代码:
C代码:
#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扩展到extend,其中结果存储在:
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
3)使用MOV将结果移到数组的负元素中:
mov [int_ar +ecx*4-4], eax
4)循环到下一个数组元素,并重复上面的1)-3)
发布于 2018-03-11 19:05:33
不正确值的原因
如果我们忽略了低效和不需要的代码,并处理真正的问题,那么它可以归结为这样的指令:
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 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编译器选项来启用优化,则程序可能无法按预期工作或完全崩溃。
https://stackoverflow.com/questions/49222679
复制相似问题