汇编:遍历一系列字符并交换它们

Posted

技术标签:

【中文标题】汇编:遍历一系列字符并交换它们【英文标题】:Assembly: loop through a sequence of characters and swap them 【发布时间】:2016-11-06 18:27:58 【问题描述】:

我的任务是在汇编中实现一个函数,该函数将执行以下操作: 循环遍历一系列字符并交换它们,以使最终结果是反向的原始字符串(100 分) 提示:从用户那里收集字符串作为 C 字符串,然后将其与用户输入的字符数一起传递给汇编函数。要找出字符数,请使用 strlen() 函数。

我已经编写了 c++ 和汇编程序,并且在范围内工作正常:例如,如果我输入 12345,则输出正确显示为 54321,但如果输入超过 5 个字符:输出开始不正确:对于例如,如果我输入 123456,则输出为:653241。我将非常感谢任何能指出我的错误所在的人:

.code

_reverse PROC 
  push ebp     
  mov ebp,esp  ;stack pointer to ebp

  mov ebx,[ebp+8]       ; address of first array element
  mov ecx,[ebp+12]  ; the number of elemets in array
  mov eax,ebx   
  mov ebp,0         ;move 0 to base pointer 
  mov edx,0     ; set data register to 0
  mov edi,0

Setup:

  mov esi , ecx
  shr ecx,1
  add ecx,edx
  dec esi

reverse:

  cmp ebp , ecx
  je allDone

  mov edx, eax
  add eax , edi
  add edx , esi

Swap:
  mov bl, [edx]
  mov bh, [eax]

  mov [edx],bh
  mov [eax],bl

  inc edi
  dec esi

  cmp edi, esi
  je allDone

  inc ebp
  jmp reverse

allDone:
  pop ebp               ; pop ebp out of stack
  ret                   ; retunr the value of eax
 _reverse ENDP

END

这是我的 C++ 代码:

#include<iostream>
#include <string>

using namespace std;
extern"C"
char reverse(char*, int);

int main()

  char str[64] = NULL;
  int lenght;

  cout << " Please Enter the text you want to reverse:";
  cin >> str;
  lenght = strlen(str);

  reverse(str, lenght);

  cout << " the reversed of the input is: " << str << endl;

  

【问题讨论】:

还没有注意到任何实际错误,只是非常奇怪的东西。如果您打算将 EBP 用作通用寄存器,则应省略 mov ebp,esp 的内容。 (并且只需使用 ESP 的偏移量访问您的参数)。 set data register to 0 之类的评论毫无用处。有用的是描述您的函数使用 EDX 的用途。函数顶部的mov eax,ebx 似乎没用,为什么不首先加载到 EAX 中呢?您在函数的其余部分中唯一使用 EBX 是将字节放入 BL 和 BH。 一个错误是您应该像执行 EBP 一样保存/恢复 EBX,因为它在所有常见的 32 位调用约定中都保留了调用。您可能在某处有另一个错误,因为踩到调用者的 EBX 值可能不会导致您看到的行为。您应该在调试器中单步执行您的代码,看看它做了什么。 非常感谢 Cordes 先生的出色和建设性的 cmets 我很抱歉我上传了如此糟糕的代码,根本没有评论,我对我的代码感到非常沮丧,我忘了在我愚蠢的 cmets。 代码的第一部分是我们的教练给我们的,所以我什至没有看里面发生了什么,我用 EAX 替换了 EBX,正如你告诉我的那样,它运行良好。修复了 cmets。保存并恢复 EBX。自从我在大学的第二个学期以来,我从来没有被告知过任何关于单步调试的事情。我做到了,并弄清楚我的错误在哪里。 【参考方案1】:

你没有评论你的代码,所以 IDK 你到底想做什么,但看起来你是手动使用 MOV / ADD 进行数组索引,而不是使用像 [eax + edi] 这样的寻址模式。

但是,您似乎正在修改原始值,然后以未经修改的方式使用它。

  mov edx, eax         ; EAX holds a pointer to the start of array, read every iter
  add eax , edi        ; modify the start of the array!!!
  add edx , esi

Swap:
  inc edi
  dec esi

EAX 每一步都通过 EDI 增长,而 EDI 线性增长。所以EAX呈几何级数增加(integral(x * dx) = x^2)。

在调试器中单步执行这个应该很容易找到。


顺便说一句,执行此操作的正常方法是向上移动一个指针,向下移动一个指针,然后在它们交叉时退出循环。那你就不需要单独的计数器了,只需要cmp / ja。 (不要检查 JNE 或 JE,因为它们可以相互交叉而不相等。)

【讨论】:

我已经使用了你告诉我的寻址模式,并以几何方式固定了 EAX 的增量。还使用指针而不是索引进行比较,因为我不知道它们甚至可以在不相等的情况下相互交叉,所以我按照你的建议使用了 JA 推荐,它工作得很好 @ArashDe:很高兴它有帮助。如果您觉得它回答了问题,则应单击投票箭头下方的复选框以将此答案标记为已接受。【参考方案2】:

总的来说,从字符串的两端开始并交换元素直到到达中间是正确的想法。但实施起来很糟糕。

mov ebp,0         ;move 0 to base pointer

这似乎是循环计数器(注释无用甚至更糟);我想想法是交换 length/2 元素,这很好。提示我只是比较指针/索引并在它们发生冲突时退出。

mov edx,0     ; set data register to 0
...
add ecx,edx
mov edx, eax

无用且具有误导性。

mov edi,0
mov esi , ecx
dec esi

看起来像字符串开始/结束的索引。好的。提示我会使用指向字符串开始/结束的指针;但索引也有效

cmp ebp , ecx
je allDone

如果进行了 length/2 次迭代,则退出。好的。

mov edx, eax
add eax , edi
add edx , esi

eaxedx 指向要交换的当前符号。几乎没问题,但是这破坏了 eax!秒后的每个循环迭代都将使用错误的指针!这就是首先导致您的问题的原因。如果您使用指针而不是索引,或者使用偏移寻址[eax+edi]/[eax+esi]

,则不会发生这种情况
...

交换部分没问题

cmp edi, esi
je allDone

第二个退出条件,这次比较索引冲突!通常一个退出条件就足够了;几个退出条件通常要么是多余的,要么暗示了算法中的某些缺陷。等式比较也是不够的 - 在单次迭代期间,索引可以从 edi&lt;esi 变为 edi&gt;esi

【讨论】:

以上是关于汇编:遍历一系列字符并交换它们的主要内容,如果未能解决你的问题,请参考以下文章

交换参数列表 C++ 中的两个字符串

Ruby - 比较数组和交换索引

网络协议系列二 - 集线器/网桥/交换机/路由器

如何用分隔符分割字符串并在bash中交换子字符串的位置?

670. 最大交换-贪心

在 Java 中交换两个字符串,通过将它们传递给实用程序函数,但不返回对象或使用包装类