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

前端 未结 8 1708
名媛妹妹
名媛妹妹 2021-02-02 13:34

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          


        
相关标签:
8条回答
  • 2021-02-02 13:40

    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.
    0 讨论(0)
  • 2021-02-02 13:47

    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"); } 
    
    0 讨论(0)
  • 2021-02-02 13:51

    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 #defines 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:

    #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 (i.e. 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 ifs as well (though as other answers have pointed out, it's probably better to use the ternary operator if you can).

    0 讨论(0)
  • 2021-02-02 13:55

    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.

    0 讨论(0)
  • 2021-02-02 13:55

    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.

    0 讨论(0)
  • 2021-02-02 13:56

    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 );
    
    0 讨论(0)
提交回复
热议问题