C宏和圆括号中的参数使用

24

例子

#define Echo(a)  a
#define Echo(a) (a)

我知道这里可能没有太大的区别,但是为什么你会想要在宏体内将a放在括号中呢?这样做有什么改变吗?


可能是 C 中宏定义中括号的必要性 的重复。 - Palec
问题在C语言宏中需要括号的原因是一个重复的问题,而不是相反的情况,并且现在已经被关闭。 - Jonathan Leffler
2个回答

28

假设你有以下代码:

#define mul(x, y)  x * y

如果我这样说会发生什么:

mul(a + 5, 6); /* a + 5 * 6 */

现在,如果我稍微修改一下宏:

#define mul(x, y)  ((x) * (y))
mul(a + 5, 6); /* ((a + 5) * (6)) */

请记住,这些参数未被评估或执行任何操作,只进行文本替换。

编辑

关于在括号中使用整个宏的解释,请参见链接,由Nate C-K发表。


2
假设您有一个结构体数组,并且由于某种原因,您执行了 foo(bar+5)。如果 foo 是一个函数,那么这将是完全可以的,但如果它是一个没有括号的宏,它最终会变成 bar+5->something。如果 bar 是指向指针的指针,并且您执行 foo(*bar),它最终会变成 *bar->something,这显然是错误的,您需要 (*bar)->something - nos
没关系,我想我已经弄明白了。 - rubixibuc
另一个值得一提的注意事项是,如果您在宏体中有多个语句,最好将它们放在'do{ ... macro statements... }while(0)'中。否则,在使用宏时可能会出现问题,例如在if主体中,后跟else(没有括号包围)。 - Kaos
@rubixibuc:规则是,除非你知道不必要(而不是“认为”不必要),否则必须完全加括号来处理宏和宏参数。 有时括号会使宏出现问题(对于某些不应成为表达式的宏或宏参数),但总的来说,每个都要用括号! - Michael Burr
Nate C-K提供的链接已经失效。幸运的是,archive.org的wayback机器还有它:https://web.archive.org/web/20151004214455/https://docs.freebsd.org/info/cpp/cpp.info.Macro_Parentheses.html - josch
显示剩余4条评论

0

仅供参考,我从这里 如何在使用宏时修复数学错误 着陆,并尝试在此扩展此答案以适应其他内容。

您正在询问以下差异:

#define Echo( a )  a
#define Echo( a ) ( a )

只要你不理解宏本身(我也不是专家:)),这很好。

首先,您可能已经知道运算符的优先级,因此这两个程序之间有很大的区别:

1):

#include <stdio.h>
#define ADD( a , b ) a + b

int main( void )
{
    auto const int a = 5;
    auto const int b = 10;

    auto const int c = ADD (  2 + a ,  2 + b );
    printf( "%d", c );
    return 0;
}

输出:

19

并且:

#include <stdio.h>
#define ADD( a , b ) ( a ) + ( b )

int main( void )
{
    auto const int a = 5;
    auto const int b = 10;

    auto const int c = ADD ( a , b );
    printf( "%d", c );
    return 0;
}

输出:

15

现在让我们用*替换+

#define ADD( a, b ) a * b

编译器将a * b视为例如a == 5b == 10,这会得到5 * 10的结果。但是,当您这样说时:ADD ( 2 + a * 5 + b ),就像这里一样:
#include <stdio.h>
#define ADD( a , b ) ( a ) * ( b )

int main( void )
{
    auto const int a = 5;
    auto const int b = 10;

    auto const int c = ADD ( 2 + a , 5 + b );
    printf( "%d", c );
    return 0;
}

由于运算符优先级的影响,您得到了 105,因为它将 2 + b * 5 + a 视为 ( 2 + 5 ) * ( 5 + 10 ),即 ( 7 ) * ( 15 ),所以结果为 105。但是当您执行以下操作时:
#include <stdio.h>
#define ADD( a, b ) a * b

int main( void )
{
    auto const int a = 5;
    auto const int b = 10;

    auto const int c = ADD ( 2 + a , 5 + b );
    printf( "%d", c );
    return 0;
}

你得到了 37 是因为:
 2 + 5 * 5 + 10

这意味着:
2 + ( 5 * 5 ) + 10

这意味着:
2 + 25 + 10

简答,这两者之间有很大的区别:

#define ADD( a , b ) a * b

"and"。
#define ADD( a , b ) ( a ) * ( a )

1
为什么在变量前面写“auto”?这完全是多余的。 - Erik W
@ErikW 请解释一下,在这些例子中使用 auto 有什么问题? - Michi
1
@Michi 这些基本上是风格问题。你的代码在某种意义上是 正确 的,因为你在所有变量上都加了 auto,编译器不会做出你不想要的操作,但是在 C 语言中,这种写法是 _不好的风格_,常规的风格是 从不auto。同样,在 main 函数结尾省略 return 0; 是正确的,但是也是不好的风格;常规的风格是 不要 省略它。违背常规的风格使得其他人阅读你的代码更加困难,就这一点就足以避免这样做了。 - zwol
1
@Michi 这些特定的风格规则确实有客观的理由:在C++中,auto意味着完全不同(并且实际上很有用),因此在C代码中编写它可能会让人们误以为它是要编译为C++;在main函数中省略return 0;在C99之前是UB,并且也是一个奇怪的特例,与语言的其他部分不一致。但并非所有的风格规则都有客观的理由,_你仍然应该遵守它们_。因为可读性。 - zwol
1
请注意,在C23中,auto关键字将拥有自己的生命,就像它在C++中已经做过的那样。我坚决同意这样的观点:在过去的40年里编写的C代码不应该使用关键字auto。即使在为C23编写的代码中,我也会谨慎使用auto,因为可能会产生误解,这让我感到不安(而且我在工作中还在努力让我们的代码库使用C11;至少我们现在已经转向了C99)。 - undefined
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接