문제

Often and often I felt some of the parentheses around arguments in macro definitions were redundant. It’s too inconvenient to parenthesize everything. If I can guarantee the argument needs not be parenthesized, can I omit the parentheses? Or is parenthesizing them all highly recommended?

I came up with this question when I wrote:

#define SWAP_REAL(a, b, temp) do{double temp = a; a = b; b= temp;}while(0)

I think that if an argument appears as an l-value in the macro, the parentheses can be omitted because that means the argument appears with no other operation.

My reasoning is:

  1. The associative priority of assignment symbols is merely higher than comma’s.
  2. You cannot confuse the compiler into thinking that your argument is an expression with a comma in it. For example, SWAP(a, b, b) will not be interpreted successfully as

    do{double temp = a, b; a, b = b; b= temp;}while(0)
    

    which can pass compilation.

Am I right? Can anyone give me a counter-example?

In the following example,

#define ADD(a, b) (a += (b))

I think the argument a needn’t be parenthesized. And in this specific case, neither needs the argument b, right?

@JaredPar:

   #include <stdio.h>
   #define ADD(a, b) (a += (b))
   int main(){
      int a = 0, b = 1;
      ADD(a; b, 2);
      return 0;
   }

This cannot be compiled successfully on my VS2010. Error C2143: syntax error : missing ')' before ';'


In a nutshell, you don’t need to parenthesize the arguments having appeared as an l-value in the macro, but you are highly recommended to parenthesize them all.

Rules of Macros:

  1. DO NOT make macros with side effects!
  2. As for macros with no side effect, just parenthesize all the arguments without second thought!
  3. As for macros with side effect, stop being obsessive! Parenthesize them all! TnT
도움이 되었습니까?

해결책 2

It's unsafe to omit if you use any operator that isn't lowest precedence in your expression. I believe that would be comma.

a op b where op is some operator like +, * etc. will always bind wrong if a contains an expression of low precedence like 1 || 2.

Note I'm not claiming it's safe in those cases. There are more creative ways to break macros. By the way, don't use macros with side effects in the first place. More generally, don't use macros as functions. (See, e.g., Effective C++).

다른 팁

Here is one case where it makes a demonstrable difference

ADD(x;y, 42)

With parens this leads to a compilation error but without it leads to code that compiles.

(x;y) += 42; // With parens errors
x;y += 42;   // Without parens compiles

This may look like a silly example but it's possible combining macros together can easily lead to strange code expressions like the above.

Why take the chance here? It's just 2 characters

There are macros where you're concatenating elements to make a string (perhaps using the # operator), or building identifiers out of macro arguments using the ## operator. In such cases, you don't parenthesize the arguments.

Also, I think that when the macro arguments are themselves passed as function arguments, then you don't absolutely have to parenthesize them:

#define CALLOC(s, n) calloc(s, n)

You can play fiendish games calling such a macro (CALLOC({3, 4})), but you get what you deserve (a compilation error) — I'm not aware of a way of calling that macro that would work if instead of the macro you wrote the same arguments as a direct call to calloc().

However, if you are using the arguments in most arithmetic expressions, then you do need to wrap them in parentheses:

#define MIN(x, y) (((x) < (y)) ? (x) : (y))

Obviously, if you invoke it with arguments with side effects, then you get what you get. But the arguments won't be misinterpreted as they could be if you wrote:

#define MIN(x, y) x < y ? x : y

and then invoked it as:

MIN(x = 3 * y + 1, y = 2 * x - 2);

The comment by Moon suggests a SWAP_INT macro.

The following code using that macro compiles cleanly when compiled using default options, but fails to compile with -DWITH_PARENTHESES set.

#include <stdio.h>

#ifdef WITH_PARENTHESES
#define SWAP_INT(a, b) do { int temp = (a); (a) = (b); (b) = temp; } while (0)
#else
#define SWAP_INT(a, b) do { int temp = a; a = b; b = temp; } while (0)
#endif

int main(void)
{
    int p = 319;
    int q = 9923;
    int x = 23;
    int y = 300;

    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y);
    SWAP_INT(p, q);             // OK both ways
    SWAP_INT(x, y);             // OK both ways
    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y);
    SWAP_INT(x /= y, p *= q);   // Compiles without parentheses; fails with them
    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y);
    return 0;
}

The output:

p =      319, q =     9923, x =       23, y =      300
p =     9923, q =      319, x =      300, y =       23
p = 41150681, q =       13, x =        0, y =  3165437

That isn't a safe or effective way of swapping integers — the macro without the parentheses is not a good idea.

JFTR: when compiled without -DWITH_PARENTHESES, the line for the macro with the expressions is inscrutable:

do { int temp = x /= y; x /= y = p *= q; p *= q = temp; } while (0);

Normally, a swap macro expects two variable names — or array elements, or structure or union members, or any mix of these. It does not expect to be given arbitrary expressions. The parentheses ensure that the assignment to x /= y (which is not an lvalue) fails; without the parentheses, the expression is interpreted, but it isn't anything like what was intended (if, indeed, anything could be intended when 'swapping' two expressions like that).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top