简体   繁体   中英

Is it possible to use a if statement inside #define?

I'm trying to make a macro with the following formula: (a^2/(a+b))*b , and I want to make sure that the there will be no dividing by zero.

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

and then I call the macro inside main:

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

I've tried using brackets around the if function but I keep getting syntax errors before the if statement. I've also tried using return, but I read somewhere that you're not supposed to use that in define.

You can not use if statement, because #define is interpret by the preprocessor, and the output would be

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

which is wrong syntax.

But an alternative is to use ternary operator. Change your define to

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

Remember to always put your define between parentheses, to avoid syntax error when replacing.

As far as I know, what you're trying to do (use if statement and then return a value from a macro) isn't possible in ISO C... but it is somewhat possible with statement expressions (GNU extension).

Since #define s are essentially just fancy text find-and-replace, you have to be really careful about how they're expanded. I've found that this works on gcc and clang by default:

#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

Brief explanation of the things in this macro:

  • The \ at the end of each line is to signal line continuation (ie to tell the compiler "this macro continues on the next line")
  • The ({ is a statement expression (GNU extension; not part of standard C).
  • Though not strictly necessary, it's safer to wrap up each use of the parameter/s in parentheses to avoid operator-precedence gotchas. For example, if x was 2+1 , then (x)*(x) would expand to (2+1)*(2+1) , which is 9 (what we wanted), but x*x would expand to 2+1*2+1 , which is 5 (not what we wanted)
  • In statement expressions, the last line functions like the return value (hence the answer; at the end)

This should give you the result you're looking for, and there's no reason it can't be extended to include multiple else if s as well (though as other answers have pointed out, it's probably better to use the ternary operator if you can).

if introduces a statement, not an expression. Use the "ternary" (conditional) operator:

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

Alternatively, make this an inline function:

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

This avoids the problem of multiple evaluation of x and/or y and is much more readable, but it does fix the types of x and y . You can also drop the inline and let the compiler decide whether inlining this function is worthwhile ( inline is not a guarantee that it will perform inlining).

There are multiple problems with your macro:

  • it expands to a statement, so you cannot use it as an expression

  • the arguments are not properly parenthesized in the expansion: invoking this macro with anything but variable names or constants will produce problems.

  • the arguments are evaluated multiple times: if you invoke the macro with arguments that have side effects, such as SUM_A(a(), b()) or SUM_A(*p++, 2) , the side effect will occur multiple times.

To avoid all these problems, use a function, possibly defined as static inline to help the compiler optimize more (this is optional and modern compilers do this automatically):

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

Notes:

  • this function uses floating point arithmetic, which the macro would not necessarily, depending on the actual types of its arguments.
  • the test does not prevent division by zero: SUM_A(-1, 1) still performs one.
  • division by zero is not necessarily a problem: with floating point arguments, it produces an Infinity or a NaN, not a runtime error.

The problem is that an if statement is not an expression, and doesn't return a value. Besides, there is no good reason to use a macro in this case. In fact, it could cause very serious performance problems (depending on what you pass as macro arguments). You should use a function instead.

YES you can have an if statement in a macro. You need to format it correctly. Here is an example:

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

I use macros with conditions quite a bit and they do have a legit use.

I have a few structures that are essentially blobs and everything is just a uint8_t stream.

To make internal structures more readable I have conditional macros.

Example...

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

Now to use the macro inside an array...

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

Three bytes are emitted in the array, 1st macro emits 1 and the second 2 bytes. This is evaluated at compile time and just makes the code more readable.

I also have... for example...

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

For the original problem... maybe the person wants to use the results with constants, but once again really comes down to where and how it's to be used.

#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) };

At runtime can have...

float x;
float y;

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

I have a program that creates fonts that are included as code inside C programs and most values are wrapped around macros that split 32 bit values into 4 bytes, float into 4 bytes, etc.

You can convert the conditional statement into a simple expression. Conditions evaluate to 0 or 1

// pseudo-code
// if (<something>) { 0; } else { 42; }
// if (!<something>) { 42; } else { 0; }
// !<something> * 42;

In your specific case

// if ((x == 0) || (y == 0)) { 0; } else { (x)(y)expression; }
// if ((x != 0) && (y != 0)) { (x)(y)expression; }
// ((x != 0) && (y != 0)) * ( (x)(y)expression );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM