web-dev-qa-db-ja.com

#define内でifステートメントを使用することは可能ですか?

次の式でマクロを作成しようとしています:(a^2/(a+b))*b、そして私はゼロによる除算がないことを確認したいと思います。

#define SUM_A( x, y ) if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

次に、main内でマクロを呼び出します。

float a = 40, b = 10, result; 
result = SUM_A(a, b); 
printf("%f", result);

If関数の前後に角かっこを使用してみましたが、ifステートメントの前に構文エラーが発生し続けます。また、returnを使用してみましたが、defineでそれを使用することは想定されていません。

16
Frey1337

#defineはプリプロセッサによって解釈され、出力は次のようになるため、ifステートメントは使用できません。

 result=if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

これは間違った構文です。

ただし、代わりに三項演算子を使用することもできます。定義を次のように変更します

#define SUM_A( x, y )  ((x) == 0 || (y) == 0 ? 0 : ( ( ( (x) * (x) ) / ( ( x ) + ( y ) ) ) * ( y ) ))

置換するときの構文エラーを回避するために、常に括弧内に定義を置くことを忘れないでください。

23
tomahh

ifは、式ではなくステートメントを導入します。 「3項」(条件付き)演算子を使用します。

#define SUM_A(x, y) (((x) == 0 || (y) == 0)? 0: ((((x) * (x)) / ((x) + (y))) * (y)))

または、これをinline関数にします。

inline float sum_a(float x, float y)
{
    if (x == 0 || y == 0)
        return 0;
    else
        return ((x * x) / (x + y)) * y;
}

これにより、xyの複数の評価の問題が回避され、読みやすくなりますが、xyのタイプは修正されます。 inlineを削除して、この関数をインライン化する価値があるかどうかをコンパイラーに判断させることもできます(inlineは、インライン化を実行することを保証するものではありません)。

6
Fred Foo

#defineifステートメントを使用することは技術的に可能です(ただし、期待した方法ではありません)。 #definesは、基本的には空想的なテキストの検索と置換なので、それらがどのように展開されるかについては十分に注意する必要があります。これが機能することがわかりました...

#define SUM_A(x, y)                                     \
({                                                      \
    float answer;                                       \
    if ((x) == 0 || (y) == 0) {                         \
        answer = 0;                                     \
    } else {                                            \
        answer = ((float)((x)*(x)) / ((x)+(y))) * (y);  \
    }                                                   \
    (answer);                                           \
})
// Typecasting to float necessary, since int/int == int in C

このマクロの内容の簡単な説明:

  • 各行の最後の\は、行の継続を通知するためのものです(つまり、「このマクロは次の行に続く」とコンパイラに伝えます)
  • 技術的には必要ありませんが、演算子の優先順位の問題を避けるために、マクロ本体とパラメーターの使用を括弧で囲む方が安全です。たとえば、x2+1の場合、(x)*(x)(2+1)*(2+1)に展開され、9(希望どおり)になりますが、x*x2+1*2+1に展開され、5(希望どおりではありません)になります。
  • マクロでは、最後の式はreturn値のように機能します(したがって、最後に(answer);があります)

これはあなたが探している結果を与えるはずであり、複数のelse ifを含めるように拡張できない理由はありません(他の回答が指摘しているように、可能であれば三項演算子を使用する方が簡単です)。

6
Saeed Baig

マクロには複数の問題があります:

  • ステートメントに展開されるため、式として使用できません

  • 引数が展開で適切に括弧で囲まれていません。このマクロを変数名または定数以外で呼び出すと問題が発生します。

  • 引数は複数回評価されます。SUM_A(a(), b())SUM_A(*p++, 2)などの副作用のある引数を使用してマクロを呼び出すと、副作用が複数回発生します。

これらすべての問題を回避するには、static inlineとして定義されている可能性がある関数を使用して、コンパイラーがさらに最適化できるようにします(これはオプションであり、最新のコンパイラーはこれを自動的に行います)。

static inline int SUM_A(float x, float y) {
    if (x == 0 || y == 0)
        return 0; 
    else
        return x * x / (x + y) * y;
}

ノート:

  • この関数は、引数の実際のタイプに応じて、マクロが必ずしもそうではない浮動小数点演算を使用します。
  • テストはゼロによる除算を妨げません。SUM_A(-1、1)はまだ1を実行します。
  • ゼロによる除算は必ずしも問題ではありません。浮動小数点引数では、ランタイムエラーではなく、無限大またはNaNが生成されます。

問題は、ifステートメントが式ではなく、値を返さないことです。さらに、この場合にマクロを使用する十分な理由はありません。実際、これは非常に深刻なパフォーマンスの問題を引き起こす可能性があります(マクロ引数として渡すものによって異なります)。代わりに関数を使用する必要があります。

1
Alex D

私は条件付きのマクロをかなり使用していますが、それらには合法的な使用法があります。

基本的にブロブであるいくつかの構造があり、すべてが単なるuint8_tストリームです。

内部構造をより読みやすくするために、条件付きマクロがあります。

例...

#define MAX_NODES 10
#define _CVAL16(x)(((x) <= 127) ? (x) : ((((x) & 127) | 0x80) ), ((x) >> 7))  // 1 or 2 bytes emitted <= 127 = 1 otherwise 2

次に、配列内でマクロを使用します...

uint8_t arr_cvals[] = { _CVAL16(MAX_NODES), _CVAL16(345) };

配列では3バイトが放出され、最初のマクロは1バイトを放出し、2番目のマクロは2バイトを放出します。これはコンパイル時に評価され、コードを読みやすくします。

私も...例えば...

#define _VAL16(x) ((x) & 255), (((x) >> 8) & 255)

元の問題については、おそらく定数を使用して結果を使用したいと思うかもしれませんが、もう一度、実際にそれがどこでどのように使用されるのかということに行き着きます。

#define SUM_A(x, y) (!(x) || !(y)) ? 0 : ((x) * (x) / ((x) + (y)) * (y))
float arr_f[] = { SUM_A(0.5f, 0.55f), SUM_A(0.0f, -1.0f), SUM_A(1.0f, 0.0f) };

実行時に持つことができます...

float x;
float y;

float res = SUM_A(x,y); // note ; on the end

Cプログラム内のコードとして含まれているフォントを作成するプログラムがあります。ほとんどの値は、32ビット値を4バイトに、4バイトに浮動するなどのマクロにラップされています。

0
TheWhitde

はい、マクロ内にifステートメントを含めることができます。正しくフォーマットする必要があります。次に例を示します。

#define MY_FUNCTION( x )  if( x ) { PRINT("TRUE"); } else { PRINT("FALSE"); } 
0
Sarena Meas