Why use apparently meaningless do-while and if-else statements in macros?

前端 未结 9 1917
轻奢々
轻奢々 2020-11-21 04:00

In many C/C++ macros I\'m seeing the code of the macro wrapped in what seems like a meaningless do while loop. Here are examples.

#define FOO(X         


        
相关标签:
9条回答
  • 2020-11-21 04:40

    @jfm3 - You have a nice answer to the question. You might also want to add that the macro idiom also prevents the possibly more dangerous (because there's no error) unintended behavior with simple 'if' statements:

    #define FOO(x)  f(x); g(x)
    
    if (test) FOO( baz);
    

    expands to:

    if (test) f(baz); g(baz);
    

    which is syntactically correct so there's no compiler error, but has the probably unintended consequence that g() will always be called.

    0 讨论(0)
  • 2020-11-21 04:45

    Jens Gustedt's P99 preprocessor library (yes, the fact that such a thing exists blew my mind too!) improves on the if(1) { ... } else construct in a small but significant way by defining the following:

    #define P99_NOP ((void)0)
    #define P99_PREFER(...) if (1) { __VA_ARGS__ } else
    #define P99_BLOCK(...) P99_PREFER(__VA_ARGS__) P99_NOP
    

    The rationale for this is that, unlike the do { ... } while(0) construct, break and continue still work inside the given block, but the ((void)0) creates a syntax error if the semicolon is omitted after the macro call, which would otherwise skip the next block. (There isn't actually a "dangling else" problem here, since the else binds to the nearest if, which is the one in the macro.)

    If you are interested in the sorts of things that can be done more-or-less safely with the C preprocessor, check out that library.

    0 讨论(0)
  • 2020-11-21 04:45

    I don't think it was mentioned so consider this

    while(i<100)
      FOO(i++);
    

    would be translated into

    while(i<100)
      do { f(i++); g(i++); } while (0)
    

    notice how i++ is evaluated twice by the macro. This can lead to some interesting errors.

    0 讨论(0)
  • 2020-11-21 04:52

    The above answers explain the meaning of these constructs, but there is a significant difference between the two that was not mentioned. In fact, there is a reason to prefer the do ... while to the if ... else construct.

    The problem of the if ... else construct is that it does not force you to put the semicolon. Like in this code:

    FOO(1)
    printf("abc");
    

    Although we left out the semicolon (by mistake), the code will expand to

    if (1) { f(X); g(X); } else
    printf("abc");
    

    and will silently compile (although some compilers may issue a warning for unreachable code). But the printf statement will never be executed.

    do ... while construct does not have such problem, since the only valid token after the while(0) is a semicolon.

    0 讨论(0)
  • 2020-11-21 04:53

    Explanation

    do {} while (0) and if (1) {} else are to make sure that the macro is expanded to only 1 instruction. Otherwise:

    if (something)
      FOO(X); 
    

    would expand to:

    if (something)
      f(X); g(X); 
    

    And g(X) would be executed outside the if control statement. This is avoided when using do {} while (0) and if (1) {} else.


    Better alternative

    With a GNU statement expression (not a part of standard C), you have a better way than do {} while (0) and if (1) {} else to solve this, by simply using ({}):

    #define FOO(X) ({f(X); g(X);})
    

    And this syntax is compatible with return values (note that do {} while (0) isn't), as in:

    return FOO("X");
    
    0 讨论(0)
  • 2020-11-21 05:01

    While it is expected that compilers optimize away the do { ... } while(false); loops, there is another solution which would not require that construct. The solution is to use the comma operator:

    #define FOO(X) (f(X),g(X))
    

    or even more exotically:

    #define FOO(X) g((f(X),(X)))
    

    While this will work well with separate instructions, it will not work with cases where variables are constructed and used as part of the #define :

    #define FOO(X) (int s=5,f((X)+s),g((X)+s))
    

    With this one would be forced to use the do/while construct.

    0 讨论(0)
提交回复
热议问题