web-dev-qa-db-ja.com

JUMPとCALLの違い

JUMP命令とCALL命令はどのように異なりますか? GOTOやプロシージャコールなどのより高いレベルの概念とどのように関連していますか? (私は比較で正しいですか?)

これは私が思うことです:

JUMPまたはGOTOは、コントロールを別の場所に転送することであり、コントロールは、呼び出された場所から自動的に戻ることはありません。

一方、CALLまたはプロシージャ/関数呼び出しは、呼び出された場所に戻ります。この性質の違いにより、言語は通常スタックを利用し、スタックフレームは、呼び出されたプロシージャごとに戻る場所を「記憶」するためにプッシュされます。この動作は再帰的プロシージャにも適用されます。ただし、末尾再帰の場合、each呼び出しのためにスタックフレームを「プッシュ」する必要はありません。

あなたの答えとコメントは大歓迎です。

24
user59634

X86アセンブリなどのCALL/JMPについて話しているのであれば、ほとんど正しいです。主な違いは次のとおりです。

  • JMPは、他に何もせずに、ある場所にジャンプします
  • CALLは、現在の命令ポインタをスタックにプッシュし(むしろ、現在の命令の1つ後)、その場所にJMPをプッシュします。 RETを使用すると、元の場所に戻ることができます。

通常、CALLはJMPを使用して実装された便利な機能です。あなたは次のようなことをすることができます

          movl $afterJmp, -(%esp)
          jmp location
afterJmp:

cALLの代わりに。

26
Anteru

あなたはジャンプとコールの違いについて正確に正しいです。

末尾再帰を使用する単一関数のサンプルケースでは、コンパイラーは既存のスタックフレームを再利用できる場合があります。ただし、相互再帰関数を使用すると、さらに複雑になる可能性があります。

void ping() { printf("ping\n"); pong(); }
void pong() { printf("pong\n"); ping(); }

Ping()とpong()が、異なる数のパラメーターを受け取るより複雑な関数である場合を考えてみます。 Mark Probstの論文 GCCの末尾再帰の実装について詳しく説明しています。

1
Greg Hewgill

私はあなたが一般的な考えを持っていると思います。

アーキテクチャによって異なりますが、一般的にはハードウェアレベルで次のようになります。

  • ジャンプ命令は プログラムカウンタ を変更して、プログラムの別の部分で実行を継続します。

  • 呼び出し命令は、現在のプログラムの場所(または現在の場所+ 1)を 呼び出しスタック にプッシュし、プログラムの別の部分にジャンプします。次に、return命令により、その場所がコールスタックからポップされ、元の場所(または元の場所+1)にジャンプして戻ります。

したがって、ジャンプ命令はGOTOに近く、呼び出し命令は手続き型/関数呼び出しに近いです。

また、関数呼び出しを行うときにコールスタックが使用されるため、再帰によってコールスタックにプッシュするリターンアドレスが多すぎると、 スタックオーバーフロー が発生します。

アセンブリを学習するとき、命令が少なく操作が簡単になる傾向があるため、x86プロセッサよりも [〜#〜] risc [〜#〜] プロセッサを扱う方が簡単だと思います。

0
coobird

あなたの考えに対する1つの修正:スタックフレームを必要としないので、単純にそこでJMPを実行できるのは、末尾再帰だけでなく、一般的に末尾呼び出しでもあります(引数が正しく設定されている場合)。

0
Ingo