C中宏中需要括号[重复]
Posted
技术标签:
【中文标题】C中宏中需要括号[重复]【英文标题】:The need for parentheses in macros in C [duplicate] 【发布时间】:2012-06-04 22:15:42 【问题描述】:我尝试在以下代码中使用宏 SQR
的定义:
#define SQR(x) (x*x)
int main()
int a, b=3;
a = SQR(b+5); // Ideally should be replaced with (3+5*5+3), though not sure.
printf("%d\n",a);
return 0;
它打印23
。如果我将宏定义更改为SQR(x) ((x)*(x))
,则输出与预期一致,64
。我知道在 C 中调用宏会用宏的定义替换调用,但我仍然无法理解,它是如何计算的 23
。
【问题讨论】:
在你未来的职业生涯中。尽量不要依赖宏。或者,如果您必须使用它们,请将它们用于非常小的代码。不是覆盖半页的巨大宏怪物。 @CJohnson:是的,我几乎没有意识到在我的 C/C++ 项目中使用宏的必要性,这是 C 语言中令人困惑的领域之一,即使在做了多年的开发人员/学生之后也是如此. 相关:Can we remove parentheses around arguments in C macros definitions? 相关:When can the parentheses around arguments in macros be omitted? Nice answer to a duplicate 【参考方案1】:考虑使用这个宏替换宏:
#define SQR(x) (x*x)
使用b+5
作为参数。自己更换。在您的代码中,SQR(b+5)
将变为:(b+5*b+5)
或 (3+5*3+5)
。现在记住您的运算符优先级规则:*
在+
之前。所以这被评估为:(3+15+5)
,或23
。
宏的第二个版本:
#define SQR(x) ((x) * (x))
是正确的,因为您使用括号来保护宏参数免受运算符优先级的影响。
This page 解释操作员对 C 的偏好有一个很好的图表。 Here'sC11参考文档的相关部分。
这里要记住的是,您应该养成始终使用括号屏蔽宏中的任何参数的习惯。
【讨论】:
【参考方案2】:只需将宏扩展中的每个参数括在括号中即可。
#define SQR(x) ((x)*(x))
这适用于您传递的任何参数或值。
【讨论】:
没有解释,没有回答问题(如何宏扩展工作),格式错误,没有附加值。因此没有理由存在这个答案。【参考方案3】:因为宏只是字符串替换,它发生在完成过程之前。编译器将没有机会看到宏变量及其值。例如:如果一个宏被定义为
#define BAD_SQUARE(x) x * x
这样称呼
BAD_SQUARE(2+1)
编译器会看到这个
2 + 1 * 2 + 1
这可能会导致
的意外结果5
要纠正这种行为,您应该始终用括号括住宏变量,例如
#define GOOD_SQUARE(x) (x) * (x)
当这个宏被调用时,例如,像这样
GOOD_SQUARE(2+1)
编译器会看到这个
(2 + 1) * (2 + 1)
这将导致
9
另外,这里有一个完整的例子来进一步说明这一点
#include <stdio.h>
#define BAD_SQUARE(x) x * x
// In macros alsways srround the variables with parenthesis
#define GOOD_SQUARE(x) (x) * (x)
int main(int argc, char const *argv[])
printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) );
printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) );
printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) );
printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) );
return 0;
【讨论】:
【参考方案4】:经过预处理,SQR(b+5)
将扩展为(b+5*b+5)
。这显然是不正确的。
SQR
的定义有两个常见错误:
不要将宏的参数括在宏体中的括号中,因此如果这些参数是表达式,则这些表达式中具有不同优先级的运算符可能会导致问题。这是解决此问题的版本
#define SQR(x) ((x)*(x))
多次评估宏的参数,因此如果这些参数是具有副作用的表达式,则这些副作用可能会被多次使用。例如,考虑SQR(++x)
的结果。
通过使用 GCC typeof 扩展,这个问题可以这样解决
#define SQR(x) ( typeof (x) _x = (x); _x * _x; )
这两个问题都可以通过用内联函数替换宏来解决
inline int SQR(x) return x * x;
这需要 GCC 内联扩展或 C99,请参阅 6.40 An Inline Function is As Fast As a Macro。
【讨论】:
【参考方案5】:预处理器宏在编译代码之前执行文本替换,因此
SQR(b+5)
转换为
(b+5*b+5) = (6b+5) = 6*3+5 = 23
常规函数调用会在将参数 (b+3) 传递给函数之前计算其值,但由于宏是预编译替换,因此运算的代数顺序变得非常重要。
【讨论】:
【参考方案6】:宏只是一个直接的文本替换。预处理后,您的代码如下所示:
int main()
int a, b=3;
a = b+5*b+5;
printf("%d\n",a);
return 0;
乘法的运算符优先级高于加法,因此在计算a
的值时,它在两次加法之前完成。在宏定义中添加括号可以解决问题:
int main()
int a, b=3;
a = (b+5)*(b+5);
printf("%d\n",a);
return 0;
括号内的运算在乘法之前进行计算,因此现在首先进行加法,您会得到您所期望的a = 64
结果。
【讨论】:
【参考方案7】:因为(3+5*3+5 == 23)
。
而((3+5)*(3+5)) == 64
.
最好的方法是不使用宏:
inline int SQR(int x) return x*x;
或者干脆写x*x
。
【讨论】:
【参考方案8】:宏扩展为
a = b+5*b+5;
即
a = b + (5*b) + 5;
所以 23。
【讨论】:
以上是关于C中宏中需要括号[重复]的主要内容,如果未能解决你的问题,请参考以下文章