37

SQR次のコードでマクロの定義を試してみました。

#define SQR(x) (x*x)
int main()
{
    int a, b=3;
    a = SQR(b+5);      // Ideally should be replaced with (3+5*5+3), though not sure.
    printf("%d\n",a);
    return 0;
}

印刷し23ます。マクロ定義をに変更するとSQR(x) ((x)*(x))、出力は期待どおりになり64ます。Cでマクロを呼び出すと、その呼び出しがマクロの定義に置き換えられることは知っていますが、それがどのように計算されたかはまだわかりません23

4

8 に答える 8

39

プリプロセッサ マクロは、コードがコンパイルされる前にテキスト置換を実行するため SQR(b+5)、(b+5*b+5) = (6b+5) = 6*3+5 = 23 に変換されます。

通常の関数呼び出しでは、関数に渡す前にパラメーターの値 (b+3) を計算しますが、マクロはコンパイル済みの置換であるため、演算の代数的順序が非常に重要になります。

于 2012-05-30T16:28:05.930 に答える
17

このマクロを使用したマクロ置換を検討してください。

#define SQR(x) (x*x)

引数として使用b+5します。交換はご自身で行ってください。コードでは、 は、またはSQR(b+5)になります。ここで、演算子の優先順位規則を覚えておいてください: before . したがって、これは、またはとして評価されます。(b+5*b+5)(3+5*3+5)*+(3+15+5)23

マクロの 2 番目のバージョン:

#define SQR(x) ((x) * (x))

演算子の優先順位の影響からマクロ引数を保護するために括弧を使用しているため、正しいです。

C のオペレーター設定を説明するこのページには、優れたチャートがあります。C11 リファレンス ドキュメントの関連セクションを次に示します

ここで覚えておくべきことは、括弧を使用してマクロ内の引数を常に保護する習慣を身に付ける必要があるということです。

于 2012-05-30T16:31:01.820 に答える
11

なぜなら(3+5*3+5 == 23)

一方((3+5)*(3+5)) == 64

これを行う最善の方法は、マクロを使用しないことです。

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

または単に書くx*x

于 2012-05-30T16:28:00.050 に答える
8

マクロはに展開されます

 a = b+5*b+5;

すなわち

 a = b + (5*b) + 5;

だから23。

于 2012-05-30T16:27:30.063 に答える
4

前処理後、SQR(b+5)に展開され(b+5*b+5)ます。これは明らかに正しくありません。

の定義には 2 つの一般的な誤りがありますSQR

  1. マクロ本体でマクロの引数を括弧で囲まないでください。したがって、これらの引数が式である場合、それらの式で異なる優先順位を持つ演算子が問題を引き起こす可能性があります。これは、この問題を修正したバージョンです

    #define SQR(x) ((x)*(x))
    
  2. マクロの引数を複数回評価するため、それらの引数が副作用を持つ式である場合、それらの副作用は複数回発生する可能性があります。たとえば、 の結果を考えてみましょうSQR(++x)

    GCC typeof extension を使用することで、この問題は次のように修正できます。

    #define SQR(x) ({ typeof (x) _x = (x); _x * _x; })
    

これらの問題は両方とも、そのマクロをインライン関数に置き換えることで修正できます

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

これには GCC インライン拡張または C99 が必要です。「 6.40 インライン関数はマクロと同じくらい速い」を参照してください。

于 2014-05-27T12:47:16.697 に答える
2

マクロは単なるテキスト置換です。前処理後、コードは次のようになります。

int main()
{
    int a, b=3;
    a = b+5*b+5;
    printf("%d\n",a);
    return 0;
}

乗算は加算よりも演算子の優先順位が高いため、 の値を計算するときに 2 つの加算の前に実行されaます。マクロ定義に括弧を追加すると、次のようにして問題を解決できます。

int main()
{
    int a, b=3;
    a = (b+5)*(b+5);
    printf("%d\n",a);
    return 0;
}

括弧で囲まれた演算は乗算の前に評価されるため、加算が最初に行われ、a = 64期待どおりの結果が得られます。

于 2012-05-30T16:34:44.703 に答える
1

マクロは単なる文字列の置換であり、完了プロセスの前に発生するためです。コンパイラは、マクロ変数とその値を確認する機会がありません。例: マクロが次のように定義されている場合

#define BAD_SQUARE(x)  x * x 

そしてこのように呼ばれました

BAD_SQUARE(2+1) 

コンパイラはこれを確認します

2 + 1 * 2 + 1

その結果、おそらく、予期しない結果が発生します

5

この動作を修正するには、次のように常にマクロ変数を括弧で囲む必要があります。

#define GOOD_SQUARE(x)  (x) * (x) 

このマクロが呼び出されると、たとえば、このように

GOOD_SQUARE(2+1)

コンパイラはこれを確認します

(2 + 1) * (2 + 1)

その結果、

9

さらに、ポイントをさらに説明するための完全な例を次に示します

#include <stdio.h>

#define BAD_SQUARE(x)  x * x 
// In macros alsways srround the variables with parenthesis
#define GOOD_SQUARE(x)  (x) * (x) 

int main(int argc, char const *argv[])
{
    printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) ); 
    printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) ); 
    printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) ); 
    printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) ); 

    return 0;
}
于 2017-01-09T20:35:36.297 に答える
-3

マクロ展開のすべての引数を括弧で囲むだけです。

#define SQR(x) ((x)*(x))

これは、渡した引数または値に対して機能します。

于 2016-03-13T11:13:14.377 に答える