インライン関数とプリプロセッサマクロ


回答:


127

プリプロセッサマクロは、コードに適用される単なる置換パターンです。これらは、コンパイルが始まる前に展開で置き換えられるため、コードのほぼどこでも使用できます。

インライン関数は、本体が呼び出しサイトに直接挿入される実際の関数です。これらは、関数呼び出しが適切な場合にのみ使用できます。

ここで、関数のようなコンテキストでマクロとインライン関数を使用する限り、次のことに注意してください。

  • マクロはタイプセーフではなく、構文的に正しいかどうかに関係なく展開できます。コンパイルフェーズでは、マクロ展開の問題に起因するエラーが報告されます。
  • マクロは予期しない状況で使用でき、問題が発生します
  • マクロは、他のマクロを展開できるという点でより柔軟性がありますが、インライン関数はこれを行う必要はありません。
  • 入力式はパターン内のどこにでもコピーされるため、マクロは展開されるために副作用が発生する可能性があります。
  • インライン関数は常にインライン化されることが保証されているわけではありません-一部のコンパイラはこれをリリースビルドでのみ、または特にそうするように構成されている場合にのみ行います。また、インライン化ができない場合もあります。
  • インライン関数は変数(特に静的変数)のスコープを提供でき、プリプロセッサマクロはコードブロック{...}でのみこれを実行できます。静的変数はまったく同じように動作しません。

39
インライン関数は必ずしもインライン化されるとは限りません。コンパイラーがインライン化しないと、遅いコードなどが生成されるためです。コンパイラーは、エンジニアができない多くの分析を行い、正しいことを行います。
マーティンヨーク

14
再帰関数は、ほとんどのコンパイラーがインライン化を無視するもう1つの例だと思います。
LBushkin 2009

この場合、C ++と比較してCに重要な違いはありますか?
rzetterberg 2011

7
言及されていない1つの点は、インライン化はコンパイルフラグの影響を受ける可能性があるということです。たとえば、GCC -O2 / -O3のように最大速度でビルドする場合、コンパイラーは多くの関数をインライン化することを選択しますが、最小サイズ(-Os)でビルドする場合、通常1回だけ呼び出される関数(または非常に小さな関数)をインライン化します)。マクロではそのような選択肢はありません。
dbrank0

インライン関数は可能ですが、マクロはアクセス指定子(privateやprotectedなど)でカバーできません。
Hitの

78

まず、プリプロセッサマクロは、コンパイル前のコードに "コピーペースト"するだけです。したがって、型チェックは行われず、いくつかの副作用が発生する可能があります

たとえば、2つの値を比較する場合:

#define max(a,b) ((a<b)?b:a)

max(a++,b++)たとえばを使用すると、副作用が発生します(aまたはb2倍になります)。代わりに、(たとえば)を使用します

inline int max( int a, int b) { return ((a<b)?b:a); }

3
副作用に加えて、マクロが追加の作業負荷を導入する可能性があるという例に追加したいだけです。max(fibonacci(100), factorial(10000))大きい方が2回計算されることを考慮してください:(
watashiSHUN

誰もが型チェックについて話しますが、実際の例を提供しただけなので、この答えを賛成します。
Ivanzinho

16

インライン関数はコンパイラーによって展開されますが、マクロはプリプロセッサーによって展開されるため、単なるテキスト置換です。したがって、

  • マクロの呼び出し中に型チェックは行われませんが、関数の呼び出し中に型チェックが行われます。

  • 引数の再評価と演算の順序が原因で、マクロ展開中に望ましくない結果と非効率が発生する可能性があります。例えば

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    結果として

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • マクロ引数はマクロ展開の前に評価されません

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • 関数の場合のように、returnキーワードをマクロで使用して値を返すことはできません。

  • インライン関数はオーバーロード可能

  • マクロに渡されたトークンは、トークン貼り付け演算子と呼ばれる演算子##を使用して連結できます。

  • マクロは通常、コードの再利用に使用されます。インライン関数は、関数呼び出し中の時間オーバーヘッド(余分な時間)を排除するために使用されます(サブルーチンへのジャンプを回避します)。


13

主な違いは型チェックです。コンパイラーは、入力値として渡されるものが、関数に渡されるタイプであるかどうかをチェックします。これはプリプロセッサマクロには当てはまりません。マクロは型チェックの前に展開されるため、重大でバグの検出が困難になる可能性があります。

ここではいくつかの他のより明確なポイントが概説されています。


11

すでに与えられたものに別の違いを追加するには:#defineデバッガーでa をステップ実行することはできませんが、インライン関数をステップ実行できます。



3

インライン関数はマクロに似ています(関数コードはコンパイル時に呼び出し時に展開されるため)、インライン関数はコンパイラーによって解析されますが、マクロはプリプロセッサーによって展開されます。その結果、いくつかの重要な違いがあります。

  • インライン関数は、通常の関数に適用されるタイプセーフのすべてのプロトコルに従います。
  • インライン関数は、関数宣言にインラインキーワードが含まれていることを除いて、他の関数と同じ構文を使用して指定されます。
  • インライン関数に引数として渡された式は一度評価されます。
  • 場合によっては、マクロに引数として渡された式を複数回評価できます。 http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • マクロはプリコンパイル時に展開され、デバッグには使用できませんが、インライン関数は使用できます。

- 良い記事:http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;


2

インライン関数は値のセマンティクスを維持しますが、プリプロセッサマクロは構文をコピーするだけです。引数を複数回使用すると、プリプロセッサマクロで非常に微妙なバグが発生する可能性があります。たとえば、引数に「i ++」のようなミューテーションが2回実行されている場合は、驚きです。インライン関数にはこの問題はありません。


1

インライン関数は構文的に通常の関数と同じように動作し、型の安全性と関数のローカル変数のスコープを提供し、メソッドの場合はクラスメンバーへのアクセスを提供します。また、インラインメソッドを呼び出すときは、プライベート/保護された制限に従う必要があります。


1

マクロとインライン関数の違いを知るには、まずそれらが正確に何であり、いつ使用すべきかを知る必要があります。

機能

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • 関数呼び出しにはオーバーヘッドが関連付けられています。関数の実行が終了した後、関数はどこに戻る必要があるかを知る必要があり、スタックメモリに値を格納する必要があるためです。

  • 小さなアプリケーションの場合は問題ありませんが、毎秒数千のトランザクションが発生する金融アプリケーションの例を考えてみましょう。関数呼び出しを使用することはできません。

マクロ:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • マクロは前処理段階で機能します。つまり、この段階で#キーワードで記述されたステートメントはコンテンツで置き換えられます。つまり、

int結果= Square(x * x)

しかし、マクロにはそれに関連するバグがあります。

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

ここでは、出力は36ではなく11です。

インライン関数

inline int Square(int x) {
    return x * x;
}

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

出力36

インラインキーワードは、関数呼び出しを関数の本文で置き換えるようコンパイラーに要求します。ここでは、最初に式が評価されてから渡されるため、出力は正しいです。戻りアドレスとスタックを格納する必要がないため、関数呼び出しのオーバーヘッドが削減されます。関数の引数にメモリは必要ありません。

マクロとインライン関数の比較:

  1. マクロは置換によって機能しますが、インライン関数では、関数呼び出しは本体で置き換えられます。
  2. マクロは置換が原因でエラーが発生しやすくなりますが、インライン関数は安全に使用できます。
  3. マクロにはアドレスがありませんが、インライン関数にはアドレスがあります。
  4. マクロは複数行のコードで使用することは困難ですが、インライン関数はそうではありません。
  5. C ++では、マクロはメンバー関数では使用できませんが、インライン関数では使用できます。

結論:

インライン関数は、パフォーマンスを向上させ、安全に使用でき、関数呼び出しのオーバーヘッドも削減されるため、マクロよりも役立つ場合があります。これは単にコンパイラーへのリクエストであり、特定の関数は次のようにインライン化されません。

  • 大きな機能
  • 条件付き引数が多すぎる関数
  • 再帰的なコードとループなどのコード

これは良いことです。なぜなら、コンパイラが別の方法で物事を行うのが最善であると考えるときはいつでもです。


備考と同じように:マクロを修正して、角かっこで同じ数に評価することができます。ただし、実装中の絶対的な代替とすべてのケースについて考える必要があるため、それでもエラーが発生しやすくなります。
マイク

0

GCC(他の人についてはよくわかりません)では、関数をインラインで宣言することはコンパイラーへのヒントにすぎません。それが呼び出されるときはいつでも、それが関数の本体を含むかどうかを決定するのは、結局のところ、コンパイラー次第です。

インライン関数とプリプロセッサマクロの違いは比較的大きいです。プリプロセッサマクロは、結局のところ、単なるテキスト置換です。コンパイラが引数と戻り値の型のチェックを実行する機能の多くをあきらめます。引数の評価は大きく異なります(関数に渡す式に副作用がある場合は、デバッグがとても楽しいでしょう)。関数とマクロを使用できる場所については微妙な違いがあります。たとえば、私が持っていた場合:

#define MACRO_FUNC(X) ...

MACRO_FUNCは明らかに関数の本体を定義します。関数を使用できるすべてのケースで正しく実行されるように、特別な注意が必要です。たとえば、MACRO_FUNCが正しく記述されていないと、エラーが発生します。

if(MACRO_FUNC(y)) {
 ...body
}

通常の機能は問題なく使用できます。


0

コーディングの観点からは、インライン関数は関数のようなものです。したがって、インライン関数とマクロの違いは、関数とマクロの違いと同じです。

コンパイルの観点からは、インライン関数はマクロに似ています。呼び出されるのではなく、コードに直接挿入されます。

一般に、インライン関数は、いくつかの小さな最適化が組み込まれた通常の関数であると考える必要があります。ほとんどの最適化と同様に、実際に適用するかどうかはコンパイラーが決定します。多くの場合、コンパイラーは、さまざまな理由により、プログラマーが関数をインライン化しようとする試みを喜んで無視します。


0

インライン関数は、その中に反復または再帰ステートメントが存在する場合、関数呼び出しとして動作し、命令の繰り返し実行を防止します。プログラム全体のメモリを節約することは非常に役立ちます。


-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

マクロは実際の関数呼び出しのオーバーヘッドを含まないため、通常、関数よりも高速です。

マクロの短所:型のチェックがありません。単純な置換が行われるためデバッグが困難です。マクロには名前空間がないため、コードの1つのセクションのマクロが他のセクションに影響を与える可能性があります。上記のCUBE()の例に示すように、マクロは副作用を引き起こす可能性があります。

マクロは通常、1つのライナーです。ただし、複数行で構成することもできます。関数にはそのような制約はありません。


あなたはどのくらいのより多くの楽しみを得るのですから#define TWO_N(n) 2 << n当時とcout << CUBE(TWO_N(3 + 1)) << endl;?(出力の行は、出力endlを開始するよりも終了する方が適切です。)
Jonathan Leffler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.