JUMP命令とCALL命令はどのように異なりますか? GOTOやプロシージャコールなどのより高いレベルの概念とどのように関連していますか? (私は比較で正しいですか?)
これは私が思うことです:
JUMPまたはGOTOは、コントロールを別の場所に転送することであり、コントロールは、呼び出された場所から自動的に戻ることはありません。
一方、CALLまたはプロシージャ/関数呼び出しは、呼び出された場所に戻ります。この性質の違いにより、言語は通常スタックを利用し、スタックフレームは、呼び出されたプロシージャごとに戻る場所を「記憶」するためにプッシュされます。この動作は再帰的プロシージャにも適用されます。ただし、末尾再帰の場合、each呼び出しのためにスタックフレームを「プッシュ」する必要はありません。
あなたの答えとコメントは大歓迎です。
X86アセンブリなどのCALL/JMPについて話しているのであれば、ほとんど正しいです。主な違いは次のとおりです。
通常、CALLはJMPを使用して実装された便利な機能です。あなたは次のようなことをすることができます
movl $afterJmp, -(%esp)
jmp location
afterJmp:
cALLの代わりに。
あなたはジャンプとコールの違いについて正確に正しいです。
末尾再帰を使用する単一関数のサンプルケースでは、コンパイラーは既存のスタックフレームを再利用できる場合があります。ただし、相互再帰関数を使用すると、さらに複雑になる可能性があります。
void ping() { printf("ping\n"); pong(); }
void pong() { printf("pong\n"); ping(); }
Ping()とpong()が、異なる数のパラメーターを受け取るより複雑な関数である場合を考えてみます。 Mark Probstの論文 GCCの末尾再帰の実装について詳しく説明しています。
私はあなたが一般的な考えを持っていると思います。
アーキテクチャによって異なりますが、一般的にはハードウェアレベルで次のようになります。
ジャンプ命令は プログラムカウンタ を変更して、プログラムの別の部分で実行を継続します。
呼び出し命令は、現在のプログラムの場所(または現在の場所+ 1)を 呼び出しスタック にプッシュし、プログラムの別の部分にジャンプします。次に、return命令により、その場所がコールスタックからポップされ、元の場所(または元の場所+1)にジャンプして戻ります。
したがって、ジャンプ命令はGOTO
に近く、呼び出し命令は手続き型/関数呼び出しに近いです。
また、関数呼び出しを行うときにコールスタックが使用されるため、再帰によってコールスタックにプッシュするリターンアドレスが多すぎると、 スタックオーバーフロー が発生します。
アセンブリを学習するとき、命令が少なく操作が簡単になる傾向があるため、x86プロセッサよりも [〜#〜] risc [〜#〜] プロセッサを扱う方が簡単だと思います。
あなたの考えに対する1つの修正:スタックフレームを必要としないので、単純にそこでJMPを実行できるのは、末尾再帰だけでなく、一般的に末尾呼び出しでもあります(引数が正しく設定されている場合)。