2016-01-13 49 views
1

我想在Assembly中编写一个函数,它将返回数组中偶数元素的总和。这是我的代码大会偶发或奇怪的错误

 int sum(int *a, int n) 
     { 
      int S = 0; 
      _asm { 

      mov eax, [ebp + 8] 
      mov edx, 0 
      mov ebx, [ebp + 12] 
      mov ecx, 0 
      for1: cmp ecx, ebx 
      jge endfor 
      and [eax + ecx * 4], 1 
      jz even 
      inc ecx 
      jmp for1 

      even: add edx, [eax + ecx * 4]    
      inc ecx 
      jmp for1 
      endfor: mov S, edx 
      } 
     return S; 
     } 

但它不工作。有人知道这是什么错误,我该如何解决它?谢谢。

+2

定义“不工作”。它在做什么,这与你所期望的有什么不同? – davmac

+1

另请注意,'和[eax + ecx * 4],1'会破坏您的输入数组。这是打算吗? – davmac

回答

4

只是一些猜测(尤其是因为我不知道你使用的是什么编译器,而你并不清楚你所说的“不工作”的意思):

 mov eax, [ebp + 8] 
     mov edx, 0 
     mov ebx, [ebp + 12] 
     mov ecx, 0 

我认为这是应该将两个参数an加载到寄存器中。你怎么知道补偿是正确的?是否可以直接引用名称?

 and [eax + ecx * 4], 1 

这将销毁输入数组中的元素(如果奇数或0,则设置为1),这可能不需要。你应该使用test指令(这是非破坏性的)而不是and

 even: add edx, [eax + ecx * 4] 

这将增加0,因为你已经通过and指令我在上面提到设置[eax + ecx * 4]为0。基于此,我期望你的函数总是返回0.

+0

它将参数正确加载到寄存器中。不工作的部分是均匀测试部分。我是一名初学者,所以我现在还没有真正了解那么多大会。你能告诉我如何检查一个数是否是偶数,然后添加它们以返回数组中所有偶数元素的总和/?谢谢 – Andreea

+0

@Andreea一个奇数将最低有效位设置为“1”。因此,一个偶数的最低有效位被清除为“0”。您可以使用'1'来逐位检查数字的奇偶性。但似乎你已经知道,看着你的代码... – Levi

+0

@Andreea'和'指令确实检查数字是偶数还是奇数。问题是它也破坏了第一个操作数的原始值,在你的代码中,它是数组元素。在你应用'和'指令之前,你需要将元素值从数组中取出。 (在我的回答中已经提到过这个问题......如果你有什么不明白的地方,那么请明确地询问一下,而不是仅仅改写原来的问题)。 – davmac

0

为什么即使你自己想要从栈中取出参数,也可以使用内联asm?要么让编译器给你参数(所以它在内联之后仍然有效),或者在单独的文件中写入纯粹的asm。

下面是你写的功能很好的一个版本。请注意缩进和更高效的代码(分支结构,并加载到寄存器中,而不是在内存中进行比较,然后用作添加的内存操作数。如果预期条件不太可能,则带有内存的testcmp操作将使感,虽然)。

int sum(const int *a, int n) 
{ 
    int S; 

    _asm { 
     mov ecx, n 
     xor eax,eax    // Sum = 0 
     test ecx,ecx 
     jz .early_out    // n==0 case 
     mov esi, a 
     lea edi, [esi + 4*edi] // end pointer 

    .sum_loop: 
     // Assume even/odd is unpredictable, so do it branchlessly: 
     xor ecx,ecx 
     mov edx, [esi]   // lodsd could be used here (to load eax: keep the total somewhere else) 
     test edx, 1    // ZF cleared for odd numbers only 
     cmovz ecx, edx    // ecx=even:a[i] odd:0 
     add eax, ecx    // add zero or a[i] 

     add esi, 4 
     cmp esi, edi    // pointer < end pointer 
     jb .sum_loop 
    .early_out: 
     mov S, eax  // MSVC inline asm is super dump: we can't just tell it the result is in eax 
    } 

    return S; 
} 

用树枝,简单的方法就是

.sum_loop: 
    mov edx, [esi] 
    test edx, 1 
    jnz .odd     // conditionally skip the add 
    add eax, edx 
.odd: 
    add esi, 4 
    cmp esi, edi    // pointer < end pointer 
    jb .sum_loop 

你原来的分支结构(复制回路奇/偶分支的尾部)是对于某些情况有用的技术,但不是在这里它们在加入总量后再次相同。

在asm中,使用底部的条件分支编写循环。在进入循环之前检查暗示零迭代的条件。你不希望一个未采取的cmp/jcc 采取无条件分支。

lodsd与英特尔的mov eax, [esi]/add esi, 4一样有效,从Haswell开始。它在早期的英特尔和AMD上是3个微软,所以它以牺牲速度为代价来节省代码大小。

查看的指南和东西。