3

次のように、ヘッダーの 1 つにいくつかの短い定義があります。

#define ROUND_DOWN(a,b)   (a)-(a)%(b)

例えば

ROUND_DOWN(178,32) = 160

しかし、これを渡すと:

ROUND_DOWN(160*2, 32);

次に、このようにコンパイルされますか?

(160*2)-(160*2)%(32), 

これは、160*2 を 2 回行うため、処理が増えるだけです。

インライン関数が同じように動作するかどうか疑問に思っていますか? 例えば

inline int RoundDown(int a, int b)
{
return (a)-(a)%(b)
}

160*2 は 320 として「int a」に格納され、計算は機能しますか、それとも定義と同じように動作しますか?

より良い例は、次の呼び出しです。

RoundDown((x+x2)*zoom, tile_width);
4

5 に答える 5

8

「#define」とインラインは同じように動作しますか?

いいえ、彼らはしません!

マクロ関数とインライン関数には多くの違いがあります。

-評価回数

インライン関数に引数として渡される式は、1回評価されます。

場合によっては、マクロに引数として渡される式は、複数回評価される可能性があります。マクロで引数を使用するたびに、その引数が評価されます。

コードサンプル:

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

int main()
{

  int a = 0;
  int b = 1;

  int c = max(a++, b++);

  cout << a << endl << b << endl;
  return 0;

}

意図はおそらく1と2を印刷することでしたが、マクロは次のように拡張されます。

int c = a++ > b++ ? a++ : b++;

bは2回インクリメントされ、プログラムは1と3を出力します。

-誰がそれらを評価するか

インライン関数はコンパイラーによって評価され、マクロはプリコンパイラーによるプリコンパイル時に評価されます。

-タイプチェック

インライン関数は、通常の関数に適用されるタイプセーフティのすべてのプロトコルに従います。引数の型がチェックされ、必要な変換が正しく実行されます。コンパイラは、インライン関数をシンボルテーブルに配置する前に、リターン型チェック、関数シグネチャを実行します。
それらをオーバーロードして、適切な種類のデータに対して適切な種類の操作を実行できます。

マクロは、インライン関数と比較してエラーが発生しやすくなります。パラメータは型指定されていません(マクロは算術型のオブジェクトに対して機能します)。コンパイル中にエラーチェックは行われません。

コードサンプル:

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

int main( void)
{
   cout << "Maximum of 10 and 20 is " << MAX("20", "10") << endl;
   return 0;
}

整数演算を行うマクロに文字列を渡すことができ、マクロは文句を言いません!

-提案またはコマンド?

インラインはコンパイラへの単なる提案です。関数をインライン展開するかどうかはコンパイラーの決定です。

マクロは常に展開されます。

-デバッグはどうですか?

インライン関数の定義にブレークポイントを設定し、段階的にデバッグする方法にステップインできるため、インライン関数を簡単にデバッグできます。

マクロはコンパイル前に展開されるため、デバッグには使用できません。

于 2011-07-05T15:20:59.833 に答える
5

まず、すべての定数式はコンパイル時に評価され、プログラムの実行時に乗算が実行されることはないと想定する必要があります。

第二に、何らかの効果があることにまったく依存することはできませんinline。これはコンパイラへの単なるヒントであり、要件ではありません。

ただし、関数がインライン化されていない場合でも、引数を渡すには関数の本体が実行される前に式を評価する必要があるため、式が 2 回評価されることはありません。

于 2011-07-05T14:49:44.197 に答える
3

#defines は単純なテキスト置換であるため、(お気づきのように) 括弧などに注意する必要がある場合があります。 inlineパラメーターは正常に解析されます。

条件に関して関連する問題があります。

于 2011-07-05T14:55:48.133 に答える
2

通常、関数の引数160*2は 1 回だけ評価され、その結果が関数の本体で使用されますが、マクロは160*22 回評価されます。引数式に副作用がある場合、これを見ることができます[*]: ROUND_DOWN(printf("hi!\n"), 1);vsRoundDown(printf("hi!\n"), 1);

実際には、関数がインライン化されているか、マクロが展開されているかに関係なく、式内の整数演算であり、副作用はありません。最適化コンパイラは、マクロ/関数呼び出し全体の結果を計算し、発行されたコードに答えを貼り付けることができます。そのため、マクロとインライン関数がまったく同じコードを実行する結果になる場合があり、両方とも と同じである可能性がありint a = ROUND_DOWN(160*2, 32);ます。int a = RoundDown(160*2, 32);int a = 320;

副作用がない場合、最適化は中間結果を保存して再利用することもできます。そのint c = ROUND_DONW(a*2, b);ため、次のように記述したようなコードが出力される可能性があります。

int tmp = a*2;
int c = tmp - tmp % b;

関数を実際にインライン化するかどうかは、独自の最適化規則に基づいてコンパイラが決定することに注意してください。これらのルールでは、関数がマークされているかどうが考慮されるinline場合がありますが、コンパイラ オプションを使用してインライン化などを強制しない限り、考慮されない可能性が非常に高くなります。

したがって、まともなコンパイラを想定すると、これにマクロを使用する理由はありません-特にマクロについては、誰かに来て書いてもらいたいだけです:

int a = ROUND_DOWN(321, 32) * 2;

そして、なぜ結果が 319 なのかと考えて、数分を無駄にします。

i++[*] 夢中にならないでください - たとえばwhereiが整数であるなど、副作用のある式の場合、マクロはシーケンス ポイントがないために未定義の動作をします。

于 2011-07-05T14:51:58.163 に答える
1

定数で指定した例では、適切なコンパイラでは、両方のバージョンがコンパイル時に定数を計算します。

変数が渡されるケースについて実際に質問していると仮定すると、コンパイラのオプティマイザーは両方のケースで同じコードを生成すると予想されます (結果を保存する方が効率的であれば、乗算を 2 回実行することはありません。最後に、 inline 関数、パフォーマンスが向上する場合、実際の関数呼び出しを行うオプションをコンパイラに提供します。

最後に、このようなマイクロ最適化については心配しないことに注意してください。99% はプログラムのパフォーマンスに影響を与えないためです。I/O がボトルネックになります。

于 2011-07-05T14:59:25.780 に答える