【问题标题】:Why does this code loop 16 times?为什么这段代码循环了 16 次?
【发布时间】:2016-12-09 23:14:44
【问题描述】:

again 循环会重复多少次?

mov bx,0 
mov cx,0 
again: 
shr cx,1 
inc bx 
loop again

答案是 16,但为什么呢?

当我们使用shlshr 时,答案是:无限循环。为什么?

【问题讨论】:

  • 只需运行您脑海中的代码。这里唯一奇怪的事情(如果是偶数的话)是 loop 递减 cx 并根据结果进行分支。

标签: assembly x86


【解决方案1】:

有两种基本方法可以理解这样的问题:

  1. 仔细考虑并“执行”您脑海中的代码。考虑每条指令的含义,以及它将如何影响代码流。
  2. 在编辑器中键入代码,组装它,然后在调试器下运行它。使用调试器单步执行代码,观察寄存器的值及其整体行为。

请注意,这些基本上是相同的;只需让机器完成所有工作,您只需观看魔术发生即可。如果您对汇编语言比较熟悉,那么在这种简单的情况下,方法#1 将是您所需要的。如果您只是在学习并且对它还没有完全的感觉,那么“作弊”并让调试器提供帮助会非常有用。

由于 Stack Overflow 上没有可用的调试器(尽管我仍然鼓励您自己尝试一下),让我们用我们的大脑求助于手动方法:

mov bx,0 
mov cx,0 

显然,这开始于将 bxcx 寄存器初始化为 0。

请注意,没有真正的汇编程序员会编写这样的代码。他们总是会写

xor bx, bx
xor cx, cx

这具有相同的效果(任何数字 XOR 本身都是 0),但它更快并且使用的代码字节更少。

在循环内部,这段代码运行:

shr cx,1 
inc bx 

SHR 将目标操作数(在本例中为 cx)向右移动源操作数(在本例中为常量 1)。回想一下,右移相当于除以 2,但速度更快。所以这与cx = cx >> 1cx = cx / 2 相同。

INC 将其操作数加 1。与bx = bx + 1 相同。

loop again

LOOP 指令在 x86 汇编编程中不再广泛使用,因为它相对较慢。大约唯一一次使用它是小代码比快速代码更重要。因此,我必须查看它的作用。您可以通过谷歌搜索助记符名称和“x86”来找到此信息,或者您可以在英特尔的手册中找到它,或者您可以在 标签 wiki 的材料中找到它,或者您可以在任何教科书/用于学习汇编语言编程的课程材料。我查了here,这是一个包含英特尔手册在线转录的网站,是使用 Google 找到的。

结果是,LOOP 指令递减计数寄存器 (cx),如果出现cx != 0,则执行另一次循环迭代。否则,如果cx == 0,它会停止循环并通过。

有了这些知识,你应该能够在你的脑海中“执行”代码。 bx 发生的事情完全无关紧要。唯一有趣的操作是那些影响cx 的操作。有趣的是,由于 cx 从 0 开始,而 0 >> 1 为 0,LOOP 指令将其 递减 1,在第一次迭代后变为 -1循环。

对有符号值使用按位右移有点不寻常(通常是一个错误),但它是 x86 汇编中定义明确的操作(与 C 或 C++ 不同)。基本上,发生的情况是移位的位(在这种情况下,最后一位)消失了,空位槽被零填充。因此,右移 1 只会将 0 放在最高有效位中。 (而且,从技术上讲,它会将移位的位放在进位标志中,但这在这段代码中并不重要,因为没有任何东西测试进位标志。)

例如,6 >> 1 == 3    (0000 0110 >> 1 == 0000 0011)。


现在,您应该充分了解该行为,以便能够回答第二个后续问题。但如果您需要提示,look up the behavior of the SHL instruction。它是按位左移,相当于乘以 2。

在这种情况下,代码将无限循环,因为cx 永远不会为0,这是LOOP 指令正在检查的条件!为什么会这样?因为LOOP first 递减cx,然后 then 测试它是否为零。因此,正如 Peter Cordes 指出的那样,也许更好的思考方式是 cx 是否可以为 1。SHL 指令确保永远不会出现这种情况,因为 cx 在 @987654357 之后的结果@ 要么是 0(在这种情况下,LOOP 会将其递减到 -1 并因此继续循环),要么 >= 2(在这种情况下LOOP 会将其递减到 >= 1 并因此继续循环)。

【讨论】:

  • cx 永远不会为 0... LOOP 执行之后。这是一个重要的区别,可能会导致新手困惑。与rep前缀不同,loop只在减量后检查[E|R]CX,所以它实现了do{}while(--cx)的底部。所以要寻找的是当代码到达 LOOP 指令时 CX 是否可以是1。 (很容易看出shl 的结果不是 0 就是 >= 2。)
  • 谢谢,@彼得。我有点匆忙写了最后一个解释(我最初忘记了问题的第 2 部分),我什至没有想到它可能不清楚。我已经添加了进一步的说明。我认为导致新手困惑的很多原因只是 loop 指令本身。 CISC 指令更难推理,并且由于它仅用于某些极少数情况下您正在优化最小代码大小,我不知道您为什么要教它。 dec+jnz 更容易理解,因为它都是明确写的!
  • 完全同意 LOOP 对初学者来说只是额外的复杂性。如果人们想让学生先了解循环习语,然后再解释它是如何工作的,dec/jnz 也一样好。甚至不提 LOOP、XLAT 或任何奇怪的东西对我来说更有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2020-02-02
  • 2017-02-21
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多