マクロの概念がよくわかりません。マクロとは何ですか?機能とどう違うのか分かりませんか?関数とマクロの両方にコードのブロックが含まれています。それでは、マクロと機能はどのように違いますか?
マクロの概念がよくわかりません。マクロとは何ですか?機能とどう違うのか分かりませんか?関数とマクロの両方にコードのブロックが含まれています。それでは、マクロと機能はどのように違いますか?
回答:
この回答の偏光投票パターンを観察した後、次の説明を追加したいと思います。
答えは、技術的な正確さと広範な一般化を念頭に置いて書かれたものではありません。完全または正確にしようとせずに、マクロと関数の違いをプログラミング初心者に簡単な言語で説明しようとする謙虚な試みでした(実際は正確ではありません)。Cプログラミング言語は、回答を作成するときに頭にありましたが、明確に言及せず、(潜在的な)混乱を引き起こすことをおmyびします。
私はJörgW Mittagが共有する答えを本当に尊敬しています。それはそれを読んで洞察力に富んでいて(私がどれほど知っていないか)、投稿されてすぐにそれを支持しました。ソフトウェアエンジニアリングスタック交換を始めたばかりで、これまでの経験と議論は非常に洞察力に富んでいます。
他のソフトウェア開発初心者にとっては役に立つかもしれないので、ここでこの回答を残します。技術的な精度にとらわれずに概念を理解しようとするためです。
マクロと関数の両方は、自己完結型のコード単位を表します。どちらもプログラムのモジュール設計を支援するツールです。ソースコードを書いているプログラマーの観点からは、彼らは非常によく似ています。ただし、プログラム実行ライフサイクル中の処理方法は異なります。
マクロは一度定義され、プログラム内の多くの場所で使用されます。マクロは、前処理段階でインライン展開されます。したがって、技術的には、ソースコードがコンパイルされると、別個のエンティティのままではありません。マクロ定義のステートメントは、他のステートメントと同様にプログラム命令の一部になります。
マクロを記述する背後にある動機は、プログラマーにとってソースコードの記述と管理を容易にすることです。マクロは一般に、本格的な関数の作成がパフォーマンスのオーバーヘッド/実行時間のペナルティになるような単純なタスクに適しています。マクロが関数よりも望ましい状況の例は次のとおりです。
定数値(数学値や科学値など)、またはプログラム固有のパラメーターを使用します。
ログメッセージの印刷またはアサーションの処理。
単純な計算または条件チェックの実行。
マクロを使用する場合、プログラムでマクロが使用されているすべての場所ですぐに利用できる1つの場所で変更/修正を行うのは簡単です。変更を有効にするには、プログラムの簡単な再コンパイルが必要です。
一方、関数コードはプログラム内の個別のユニットとしてコンパイルされ、プログラムの実行中に必要な場合にのみメモリにロードされます。機能コードは、プログラムの残りの部分から独立したIDを保持します。関数が複数回呼び出された場合、ロードされたコードは再利用されます。実行中のプログラムで関数呼び出しが検出されると、ランタイムサブシステムによって制御が渡され、実行中のプログラムのコンテキスト(戻り命令アドレス)が保持されます。
ただし、関数を呼び出すときに、コンテキストの切り替え、メインプログラムの命令の戻りアドレスの保持、パラメーターの受け渡し、戻り値の処理などに遭遇する必要がある、わずかなパフォーマンスの低下があります。したがって、関数の使用は、コードの複雑なブロックに対してのみ必要です(単純なケースを処理するマクロに対して)。
プログラマーは経験を積んで、コード全体がプログラムアーキテクチャ全体のマクロまたは機能として適切かどうかを慎重に判断します。
残念ながら、プログラミングでは「マクロ」という用語が複数使用されています。
Lispファミリーの言語、およびそれらに触発された言語、ScalaやHaskellのような多くの現代の関数型または関数型言語、およびBooのようないくつかの命令型言語では、マクロはコンパイル時に実行されるコードの一部です(または少なくともコンパイラなしの実装の実行時前)およびコンパイル中に抽象構文ツリー(または特定の言語の同等物、たとえばLispの場合はS-Expressions)を他の何かに変換できます。たとえば、多くのScheme実装でfor
は、ボディへの複数の呼び出しに展開されるマクロです。静的に型付けされた言語では、マクロは多くの場合型保証されています。つまり、型付けされていないコードを生成することはできません。
Cファミリーの言語では、マクロはテキスト置換に似ています。また、適切に型付けされていない、または構文的にも合法ではないコードを生成できることも意味します。
マクロアセンブラでは、「マクロ」は「仮想命令」、つまりCPUがネイティブにサポートしていないが有用な命令を指します。したがって、アセンブラはこれらの命令を使用して、CPUが理解する複数の命令に展開します。 。
アプリケーションスクリプトでは、「マクロ」とは、ユーザーが「記録」および「再生」できる一連のアクションを指します。
これらはすべて、ある意味では実行可能コードの一種です。つまり、ある意味では関数と見なすことができます。しかし、例えば、Lispマクロの場合、その入力と出力はプログラムの断片です。Cの場合、入力と出力はトークンです。最初の3つには、コンパイル時に実行されるという非常に重要な違いもあります。実際、Cプリプロセッサマクロは、その名前が示すように、コードがコンパイラに到達する前に実際に実行されます。
C言語ファミリでは、マクロ定義(プリプロセッサコマンド)は、定義でコンパイルされずにマクロ呼び出しで置換されるコードのパラメーター化されたテンプレートを指定します。これは、すべての自由変数をマクロ呼び出しのコンテキストでバインドする必要があることを意味します。i++
複数のパラメータを使用することにより、副作用のあるパラメータ引数を繰り返すことができます。1 + 2
一部のパラメーターの引数のテキスト置換はx
、コンパイルの前に発生し、予期しない動作x * 3
(7
io 9
)を引き起こす可能性があります。マクロ定義のマクロ本体のエラーは、マクロ呼び出しでのコンパイル時にのみ表示されます。
関数定義関数本体のコンテキストにバインドされた自由変数で指定するコード。関数呼び出しではありません。
ただし、このマクロは一見負のように見えるため、呼び出し、行番号、ソースファイル、引数をstringとして使用できます。
少し抽象的な用語では、マクロは構文であり、関数はデータです。(抽象)関数は、データの一部の変換をカプセル化します。それは評価されたデータとして引数を取り、それらに対していくつかの操作を実行し、データでもある結果を返します。
対照的に、マクロは評価されていない構文を取り、その上で動作します。Cライクな言語の場合、構文はトークンレベルで提供されます。LISPのようなマクロを持つ言語の場合、ASTとして表される構文を取得します。マクロは構文の新しいチャンクを返す必要があります。
マクロは、一般的にされて何かを指し場所に展開し、コンパイルやターゲット言語における個々の命令で事前処理中にマクロ「呼び出し」を置き換えます、。実行時には、通常、マクロの開始位置と終了位置は示されません。
これはサブルーチンとは異なります。サブルーチンは、メモリ内に個別に配置され、実行時に制御が渡される再利用可能なコードです。ほとんどのプログラミング言語の「関数」、「手順」、および「メソッド」はこのカテゴリに分類されます。
イェルクWミッタークの回答で説明、正確な詳細は、言語間で変化:一部に、例えばC、ソースコード内のマクロを行うテキスト置換として; Lispなどの一部では、抽象構文ツリーのような中間フォームの操作を実行します。いくつかの灰色の領域もあります。一部の言語には、関数のように定義されているが、マクロのようにコンパイルされたプログラムに展開される「インライン関数」の表記があります。
ほとんどの言語は、プログラマーが各サブルーチンを個別に推論し、入力と出力の型コントラクトを定義し、呼び出し元コードに関する他の情報を隠すことを奨励しています。多くの場合、マクロが発生しないサブルーチンの呼び出しにはパフォーマンスへの影響があり、マクロはサブルーチンでは不可能な方法でプログラムを操作できる場合があります。したがって、マクロは抽象的ではないため、サブルーチンよりも「低レベル」と見なされる場合があります。
マクロはコンパイル中に実行され、関数は実行時に実行されます。
例:
#include <stdio.h>
#define macro_sum(x,y) (x+y)
int func_sum(x,y) {
return x+y;
}
int main(void) {
printf("%d\n", macro_sum(2,3));
printf("%d\n", func_sum(2,3));
return 0;
}
したがって、コンパイル中にコードは実際に次のように変更されます。
#include <stdio.h>
int func_sum(x,y) {
return x+y;
}
int main(void) {
printf("%d\n", (2+3));
printf("%d\n", func_sum(2,3));
return 0;
}