何时可以省略宏中参数的括号?

时间:2014-01-07 05:11:24

标签: c macros c-preprocessor parentheses

通常我常常觉得宏定义中围绕参数的一些括号是多余的。将所有内容括起来太不方便了。如果我可以保证参数不需要括号,我可以省略括号吗?或者强烈建议将它们括起来?

我在写作时提出了这个问题:

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

我认为如果参数在宏中显示为l值,则可以省略括号,因为这意味着参数出现时没有其他操作。

我的理由是:

  1. 分配符号的关联优先级仅高于逗号。
  2. 您不能混淆编译器,认为您的参数是一个带逗号的表达式。例如,SWAP(a, b, b)将无法成功解释为

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

    可以通过编译。

  3. 我是对的吗?任何人都可以给我一个反例吗?

    在以下示例中,

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

    我认为论证a不需要用括号括起来。在这种特殊情况下,既不需要参数b,也不需要?

    @JaredPar:

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

    这在我的VS2010上无法成功编译。 Error C2143: syntax error : missing ')' before ';'


    简而言之,您不需要将在宏中显示为l值的参数括起来,但强烈建议您将它们全部括起来。

    宏规则:

    1. 不要制作带副作用的宏!
    2. 对于没有副作用的宏,只需在不加思索的情况下将所有参数括起来!
    3. 对于有副作用的宏,停止迷恋!将它们全部括起来!肌钙蛋白T

3 个答案:

答案 0 :(得分:3)

这是一个可以产生明显差异的案例

ADD(x;y, 42)

使用parens会导致编译错误,但如果没有它会导致编译代码。

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

这可能看起来像一个愚蠢的例子,但可能将宏组合在一起很容易导致奇怪的代码表达式,如上所述。

为什么要冒这个机会?这只是2个字符

答案 1 :(得分:2)

如果在表达式中使用任何不是最低优先级的运算符,则省略它是不安全的。我相信这将是逗号。

a op b其中op是某些运算符,如+*等,如果a包含低优先级的表达式,例如{{1 || 2将始终绑定错误1}}。

注意我声称在这些情况下它是安全的。有更多创造性的方法来打破宏。顺便说一句,首先不要使用带副作用的宏。更一般地说,不要使用宏作为函数。 (参见,例如,Effective C ++)。

答案 2 :(得分:0)

有些宏用于连接元素以生成字符串(可能使用#运算符),或使用##运算符从宏参数构建标识符。在这种情况下,您不会将参数括起来。

另外,我认为当宏参数本身作为函数参数传递时,你不必绝对将它们括起来:

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

你可以玩恶魔游戏调用这样一个宏(CALLOC({3, 4})),但你得到你应得的(编译错误) - 我不知道一种调用那个宏的方法,如果不是您编写与calloc()直接调用相同的参数的宏。

但是,如果您在大多数算术表达式中使用参数,那么您需要将它们包装在括号中:

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

显然,如果你用带副作用的参数调用它,那么你得到的就是你得到的。但这些论点不会被误解,因为如果你写的话可能会这样:

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

然后将其调用为:

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

comment Moon建议使用SWAP_INT宏。

以下使用该宏的代码在使用默认选项进行编译时干净地编译,但无法使用-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;
}

输出:

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

这不是交换整数的安全或有效方式 - 没有括号的宏不是一个好主意。