2016-12-09 14 views
0

繰り返し回数は何回繰り返されますか?なぜこのコードは16回ループするのですか?

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

答えは16ですが、なぜですか?

と、shl instedがshrの場合、答えは無限ループです。どうして?

+4

コードを頭で動かしてください。ここで唯一奇妙なことは(もし偶数であれば) 'loop'は' cx'を減らし、結果に基づいて分岐するということです。 – harold

答えて

4

このような問題を理解することの二つの基本的な方法があります。

  1. はあなたの頭の中でコードを「実行」を通して考えると。それぞれの命令が何を意味し、どのようにコードの流れに影響を与えるかを考えてください。
  2. コードをエディタに入力し、アセンブルしてデバッガで実行します。デバッガを使用してコードをシングルステップ実行し、レジスタの値とその全体的な動作を確認します。

これらは基本的に同じことです。 1つは、マシンがすべての仕事をしている、あなたは魔法が起こるのを見ます。アセンブリ言語に比較的慣れていれば、このような単純なケースで#1のアプローチが必要です。あなたが学んでいて、あなたの骨にそれほど気分がないのであれば、それを "騙して"デバッガを手伝ってもらうことは非常に便利です。

スタックオーバーフローで利用可能なデバッガが(私はまだ自分のためにこれを試してみることをお勧めしますが)ありませんので、私たちの脳を使用して手動による方法に頼らてみましょう:明らか

mov bx,0 
mov cx,0 

、これは始まりbxcxレジスタを0に初期化する。

実際のアセンブリプログラマは、このようなコードを書くことはなかったことに注意してください。彼らは常に、これは同じ効果(任意の数のXOR自体は0である)、それは高速であり、コードの少ないバイトを使用してい

xor bx, bx 
xor cx, cx 

を記述します。

ループの内部では、このコードが実行さ:

shr cx,1 
inc bx 

SHRシフト右ソースオペランドにより(この場合、cx)デスティネーション・オペランド(この場合は、一定1) 。右シフトは2で除算したものと同等ですが、速いことを思い出してください。これはcx = cx >> 1、またはcx = cx/2と同じです。

INCは、オペランドを1増分します。bx = bx + 1と同じです。

loop again 

それが比較的遅いのでLOOP命令が広くx86アセンブリプログラミングではもはや使用されていません。あなたが使用する唯一の時間については、小さなコードが高速なコードよりも重要である場合でした。そういうわけで、私はそれが何をしているのか調べなければなりません。この情報は、ニーモニック名と「x86」を検索するか、インテルのマニュアルの1つであるか、タグwikiの資料で見つけることができます。また、教科書/教材の中に見つけることもできますアセンブリ言語プログラミングを学ぶために使用しています。私はそれを見てhere、これは、Googleを使用して発見されたインテルのマニュアルのオンライン転写を含むサイトです。 命令は、カウントレジスタ(cx)をデクリメントし、cx != 0の場合にループの別の繰り返しを行う。そうでない場合は、cx == 0の場合、ループを停止してから終了します。

その知識を身につけて、頭にコードを「実行」できるはずです。 bxに何が起きているかは全く関係ありません。興味深い操作は、cxに影響を与える操作だけです。興味深いのは、cxが0から始まり、0 >> 1が0であるので、LOOP命令は、を1減らして、ループの最初の反復後に-1にすることです。

符号付きの値に対してビット単位の右シフトを使用するのは珍しいことですが、CまたはC++とは異なり、x86アセンブリでは明確に定義された操作です。シフトされたビット(この場合、最後のビット)が消え、空のビットスロットがゼロで埋められるということが基本的に起こります。したがって、1だけ右シフトすると、最上位ビットに0が置かれます。 (そして、技術的に、それはキャリーフラグでそのシフトオフビットを置くだろうが、何もキャリーフラグをテストしていないので、それは、このコードでは重要ではありません。)例えば

6 >> 1 == 3      (0000 0110 >> 1 == 0000 0011) 。


ここで、2番目のフォローアップの質問に答えるには十分な動作を理解する必要があります。しかし、ヒントが必要な場合は、look up the behavior of the SHL instruction

cxは決して0にならないため、コードは無限にループします。これはLOOP命令がチェックしている条件です。なぜこれはそうですか? LOOP最初にが減少し、cxとなり、となるため、はゼロであるかどうかをテストします。だから、Peter Cordesが指摘したように、おそらく考えてみると、cxは1になりますか?SHL命令は、SHLの後のcxの結果が0の場合(0の場合) LOOPは-1に減らしてループを続ける)、または> = 2(この場合、LOOPは> 1に減らしてループを続ける)。

+1

* 'cx'は** LOOPが実行された後で決して0 * ... **になりません。それは初心者の混乱につながる重要な違いです。 'rep'プリフィックスとは異なり、' loop'はデクリメント後に[E | R] CXだけをチェックするので、 'do {} while(-cx)'の一番下を実装します。探しているのは、コードがLOOP命令に達したときにCXが常に1になることができるかどうかです。 (そして、 'shl'の結果は0か> = 2であることが分かります) –

+1

ありがとう、@Peter。私は少しの急いで最後の説明を書きました(私は最初に質問の一部を忘れてしまいました)。そして、私にはそれが不明であるかも知れません。私はさらに明確化を加えました。私は初心者の混乱につながる多くのものが 'loop'命令そのものであると思います。CISCの命令は非常に難しいので、コードサイズを最小限に抑えて最適化しているまれな状況でしか使用されていないため、なぜ教えているのか分かりません。 'dec' +' jnz'は明示的に書かれているので、理解しやすくなります! –

+0

LOOPは初心者のための複雑さに過ぎません。もし人々が、どのように動作するかを正確に説明する前に、ループのイディオムで始めるようにしたいのであれば、 'dec/jnz'も同様です。 LOOP、XLAT、または奇妙なものについて言及するだけでも、私にはもっと意味があります。 –